Software Engineering Fundamentals for AI coding: Orthogonality
How to improve AI-generated code using a timeless design principle called Orthogonality
Introduction
Orthogonality is a critical concept if you want to produce systems that are easy to design, build, test and extend. Nowadays, in AI-generated code era, we are facing a huge amount of bad codes being developed by AI Agentic systems. These codes are, usually, hard to change, extend, improve and to be reviewed by a human developer.
Today I’m going to help you how to build a orthogonal system, and how it can change the way you plan, design and ask for an AI to build your code.
What is Orthogonality?
In geometry, two lines are orthogonal if and only if they meet at right angles. In vector terms, the two directions are independent. However, the idea of orthogonality also appears in several other fields.
In linear algebra and geometry, orthogonality has its most familiar meaning: two vectors are orthogonal when their inner product is zero. If they also have unit length, they are orthonormal [1][2]. Geometrically, orthogonal vectors meet at a right angle, and orthonormal bases make it easier to represent and decompose vectors.
Outside inner-product spaces, the idea becomes less clear. One well-known generalization, proposed by James, says that (x) is orthogonal to (y) if adding any multiple of (y) to (x) always increases its norm [3]. This preserves some of the intuition behind orthogonality, but the relation no longer has all the properties found in Euclidean geometry. For example, it may not be symmetric.
In statistics, orthogonality is often used to describe effects, variables, or contrasts that are separated from one another. Independent variables are said to be orthogonal if they are uncorrelated [4]. In ANOVA, orthogonal contrasts allow comparisons between treatment means without overlapping information [5]. The focus is not geometric perpendicularity itself, but the separation of different sources of variation.
In computing, the term usually means independence or decoupling. Two things are considered orthogonal if changes in one do not affect the other [6]. In a well-designed system, changes in the user interface should not affect the recommendation engine. In that case, we can say that the relationship between the User Interface and Recommendation Engine is orthogonal.
This idea is related to the mathematical meaning, but only by analogy. In linear algebra, orthogonality is a precise structural relation, usually defined by an inner product equal to zero. In computing, it refers to minimizing dependencies between components. This is why software engineering literature often connects orthogonality with modularity and separation of concerns. One source defines modularity as “the degree to which a system or computer program is composed of discrete components such that a change to one component has minimal impact on other components,” while another states that separation of concerns means “one computational unit should be concerned with only one functionality” [7][8].
The practical consequence is similar in both cases. If two modules are orthogonal, one can be changed with little impact on the other. If they are not, hidden coupling appears, just as non-orthogonal vectors fail to isolate variation in geometry. This is why software engineering papers often discuss decoupling, implementation independence, and orthogonal facilities: all of these ideas aim to organize a system so that one part does not depend unnecessarily on the behavior of another [9][10].
So the relationship is the following: geometric orthogonality is the original concept, statistical orthogonality refers to non-overlapping explanatory information, and computing orthogonality refers to non-overlapping responsibilities or change effects.
The benefits of Orthogonality
I mapped three major benefits of orthogonality.
The strongest benefits highlighted in the sources are localized change and lower coupling. Thomas and Hunt (Pragmatic Programmer) describe orthogonality as a way to make systems “easy to design, build, test, and extend,” defining it as independence or decoupling, where “changes in one do not affect any of the others.” This is closely related to the software-engineering concept of modularity, in which components can be modified with minimal impact on the rest of the system [11][12].
Another important benefit is reuse. One source argues that “to make software reusable, divide it into orthogonal facilities” because this provides “maximal functionality with minimal dependencies.” Another connects orthogonality to lower complexity, greater reusability, and easier evolution through separation of concerns. In practice, orthogonal modules are easier to reuse and combine in different contexts without creating unexpected side effects [13][14].
A third benefit is that it makes system design easier to reason about. If each unit is “concerned with only one functionality,” responsibilities become clearer, tests are easier to isolate, and integration problems are less likely to emerge from hidden dependencies between components [15].
How to identify a non-orthogonal code (AI-generated or not)
A quick way to spot non-orthogonal code is to look for ripple effects: if changing one part forces edits in unrelated parts, the code is coupled rather than orthogonal.
Some common warning signs are:
A small feature change requires touching many files, layers, or teams. This suggests that responsibilities are not well separated and that changes cannot remain localized [8][14].
The same concept appears in multiple places, meaning knowledge is duplicated instead of isolated. This often indicates overlapping concerns rather than a design where each unit has a single responsibility [9].
Modules depend on one another through hidden channels such as global variables, shared mutable state, or side effects. These hidden dependencies are exactly what orthogonal design seeks to eliminate [16].
A module’s interface exposes implementation details, forcing callers to understand its internals. This weakens modularity and reduces reusability [17].
Tests are difficult to isolate because exercising one behavior requires many unrelated fixtures, dependencies, or mocks. This is often a sign that the system is not easy to design, test, and evolve [14].
For AI-generated code, the same signals apply. In addition, the code may appear correct at a local level while being inconsistent with the overall architecture, or it may repeat implementation patterns without preserving the actual dependency structure of the system.
How to design, code, test and document an Orthogonal system
Designing orthogonal systems means making each module responsible for a single concern, exposing a clear interface, and minimizing dependencies on other parts of the system. In software engineering, orthogonality is often described as independence or decoupling: changes in one component should have little or no effect on others [7].
In practice, this means separating concerns, avoiding shared state, and keeping design decisions localized. Orthogonal modules are easier to reuse, test, and maintain because their responsibilities are clearly defined [8][10][14][16].
A simple rule of thumb is that if a change to the user interface requires modifications to the database layer, or if changing logging affects business logic, the system is probably not orthogonal. The more independently a component can be changed, tested, and understood, the closer the design is to orthogonality.
I built the checklist around the main orthogonality signals repeatedly discussed in software literature: localized change, single responsibility, low coupling, absence of hidden dependencies, isolated testing, explicit boundaries, and reusability.
Code examples: what AI delivers vs. what you should commit
Python Example
Non-orthogonal code
def notify_user(user_id: int):
conn = sqlite3.connect("app.db")
cursor = conn.cursor()
cursor.execute("SELECT name, email FROM users WHERE id = ?", (user_id,))
row = cursor.fetchone()
conn.close()
name, email = row
subject = "Hello!"
body = f"Hi {name}, how are you?\nVisit your account at app.com."
server = smtplib.SMTP("smtp.gmail.com", 587)
server.starttls()
server.login("user@gmail.com", "password")
message = f"Subject: {subject}\n\n{body}"
server.sendmail("user@gmail.com", email, message)
server.quit()Works on the first run, but try changing the email template or swapping the database, or just testing the discount rule in isolation. Good luck!
Orthogonal code
def get_user(user_id: int) -> dict:
conn = sqlite3.connect("app.db")
cursor = conn.cursor()
cursor.execute("SELECT name, email FROM users WHERE id = ?", (user_id,))
row = cursor.fetchone()
conn.close()
return {"name": row[0], "email": row[1]}
def build_welcome_message(user_name: str) -> dict:
return {
"subject": "Hello!",
"body": f"Hi {user_name}, how are you?\nVisit your account at app.com.",
}
def send_email(to: str, subject: str, body: str):
server = smtplib.SMTP("smtp.gmail.com", 587)
server.starttls()
server.login("user@gmail.com", "password")
message = f"Subject: {subject}\n\n{body}"
server.sendmail("user@gmail.com", to, message)
server.quit()
def notify_user(user_id: int):
user = get_user(user_id)
message = build_welcome_message(user["name"])
send_email(user["email"], message["subject"], message["body"])Each function has one responsibility. Swapping the database doesn’t affect the email, changing the template doesn’t affect the lookup. Each piece is independently testable.
References
[1] Orthogonal Vectors and Matrices, Section 3.1: Orthogonal Vectors. Available at:
https://www.semanticscholar.org/paper/8c8ff4af5a20c29c3182b76b0166ad1d4a503889
[2] Orthogonal Vectors and Matrices, Section 5.1: Orthogonal Vectors. Available at:
https://www.semanticscholar.org/paper/e933972eac214108ca89cb0e7e2e1b7d1bc55b70
[3] James, R. C. (1947). Orthogonality and Linear Functionals in Normed Linear Spaces. Transactions of the American Mathematical Society, 61(2), 265–292. Available at:
https://pubs.ams.org/journals/tran/1947-061-02/S0002-9947-1947-0021241-4
[4] Papoulis, A., & Pillai, S. U. (2002). Probability, Random Variables, and Stochastic Processes. McGraw-Hill.
[5] Hinkelmann, K. (1985). Orthogonal Contrasts: Theory and Applications. The American Statistician. Available at:
https://www.tandfonline.com/doi/abs/10.1080/00031305.1985.10479404
[6] Hunt, A., & Thomas, D. (1999). The Pragmatic Programmer: From Journeyman to Master. Addison-Wesley.
[7] IEEE. Modularity in Software Engineering. Available at:
https://ieeexplore.ieee.org/document/4228632/
[8] Richardson, L., & Amundsen, M. Separation of Concerns. In Pro RESTful APIs. Springer. Available at:
https://link.springer.com/chapter/10.1007/978-1-4842-2394-9_16
[9] Parnas, D. L. (1972). On the Criteria To Be Used in Decomposing Systems into Modules. Communications of the ACM, 15(12), 1053–1058. Available at:
https://dl.acm.org/doi/10.1145/361598.361623
[10] Hxa. Reusability By Orthogonalysis. Available at:
https://www.semanticscholar.org/paper/Reusability-By-Orthogonalysis-Hxa/f6f196753c6bce0b28ac30bbd3b879ed501e9149
[11] Hunt, A., & Thomas, D. The Pragmatic Programmer: From Journeyman to Master. Addison-Wesley.
[12] IEEE. Modularity in Software Engineering. Available at:
https://ieeexplore.ieee.org/document/4228632/
[13] Hxa. Reusability By Orthogonalysis. Available at:
https://www.semanticscholar.org/paper/Reusability-By-Orthogonalysis-Hxa/f6f196753c6bce0b28ac30bbd3b879ed501e9149
[14] Tarr, P., Ossher, H., Harrison, W., & Sutton, S. (1999). N Degrees of Separation: Multi-Dimensional Separation of Concerns. Proceedings of the 21st International Conference on Software Engineering (ICSE). Available at:
https://www.semanticscholar.org/paper/Multi-Dimensional-Separation-of-Concerns-Tarr-Ossher/f149c97bd00b8af946d88d53f7dd51d2bf2239ce
[15] Parnas, D. L. (1972). On the Criteria To Be Used in Decomposing Systems into Modules. Communications of the ACM, 15(12), 1053–1058. Available at:
https://dl.acm.org/doi/10.1145/361598.361623
[16] Suri, P. K., & Garg, R. Software Reuse Metrics: Measuring Component Reusability and Application Stability. Available at:
https://www.semanticscholar.org/paper/Software-Reuse-Metrics%3A-Measuring-Component-and-its-Suri-Garg/9da64c195851b4f5097b778eeb7f1b716729d47d
[17] Software Architecture / Interface Design and Information Hiding. Springer. Available at:
https://link.springer.com/chapter/10.1007/978-3-642-45404-2_14



