In the early stages of my software engineering career, the primary audience for whom I write my code is the machine, via the compiler. Over the years, A realization occurs. Famously articulated by Martin Fowler:
“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”
Software architecture is not merely technical; it is anthropological. When we organize code, we are designing a system that must interface with the limits and peculiarities of human comprehension. To truly understand this, we must look beyond computer science to literature on physical architecture, cognitive psychology, and linguistics.
1. Habitability over Perfection
The strongest parallel to software design lies in the physical world. Christopher Alexander, author of A Pattern Language, argued that great architecture is defined by “habitability.” This was expanded upon for software by Richard Gabriel, who suggested that we should not strive for “the perfect cathedral”- a rigid, beautiful structure that is impossible to change – but for a “habitable home.”
A habitable codebase is one that is inviting to the “inhabitants” (the developers). It is structured so that a programmer can walk into an unfamiliar module and feel an immediate sense of where things are and how they can be repaired. If a building is a labyrinth, the inhabitant feels anxiety; if a codebase is a tangle of “spaghetti code,” the developer feels that same cognitive friction. Architecture, therefore, is the art of making a system liveable.
2. The Locus of Attention and Cognitive Load
Why is habitability so difficult to achieve? Cognitive psychology suggests the hardware of the human mind has strict biological limitations. While George Miller’s classic “Magical Number Seven” suggested we can hold 7±2 items in working memory, modern research into complex tasks – like reading code – suggests the limit is closer to 4±1.
Jef Raskin, in The Humane Interface, introduced the concept of the “Locus of Attention.” A human can only focus on one complex task at a time. When an architect creates deep nesting or requires a developer to jump between five different files to understand a single function, they are shattering that locus. This creates what John Sweller calls Extraneous Cognitive Load: the mental energy wasted on navigating the way information is presented, rather than solving the problem itself. Good architecture is essentially an exercise in load management; it keeps all necessary information within the developer’s realistic mental reach.
3. Simple vs. Easy
To manage this load, we must understand what “simplicity” actually means. Rich Hickey, the creator of Clojure, argues that we often confuse “Easy” with “Simple.”
- Easy is “near to hand”- it’s what is familiar or convenient in the moment (like adding one more global variable).
- Simple comes from the Latin simplex, meaning “one-fold.” It is the opposite of Complex (complexus), which means “braided” or “intertwined.”
An architect’s job is to “simplify” the system – to untangle the logic so that each component does only one thing. A “simple” system may not be “easy” to learn initially, but because its parts are not tangled, the cognitive load remains constant as the system grows.
4. Signifiers and Structural Truth
Once we have untangled our logic, we must label it. Don Norman, in The Design of Everyday Things, distinguishes between affordances (what an object can do) and signifiers (the clues that tell you how to use it).
In software, Bertrand Meyer’s Command-Query Separation (CQS) provides a moral code for these signifiers: a function should either be a Query (returning data without side effects) or a Command (changing state without returning data). To name a function getUser() (a signifier for a Query) while it silently modifies a database (a hidden affordance) is an architectural lie.
Modern engineers like Alexis King expand this into the type system with the mantra “Parse, don’t validate.” By using the architecture of the language—such as static types—to signify that data has been cleaned, we “prove” truths to the human reader. When a developer sees a Username object instead of a raw String, the architecture itself acts as a signifier, telling them: “You don’t need to worry about validation; that work is already done.”
5. The Mirror of the Organization
Finally, we must acknowledge that code is a social artifact. Conway’s Law states that systems inevitably mirror the communication structures of the organizations that build them. If a team is fragmented, the code will be fragmented. The astute architect acknowledges that you cannot fix a system’s organization without first looking at the human interfaces—the meetings, the Slack channels, and the team boundaries.
Conclusion
The transition from thinking about algorithms to thinking about systems is the transition from speaking to machines to speaking to people. Whether we are building a “habitable” home for our teammates or “simplifying” logic to fit the human locus of attention, the goal is the same. We do not just build code that works; we build code that can be understood, inhabited, and evolved by the human mind.
Citations and Further Reading
- Alexander, Christopher. A Pattern Language: Towns, Buildings, Construction. Oxford University Press, 1977.
- Fowler, Martin. Refactoring: Improving the Design of Existing Code. Addison-Wesley Professional, 2018.
- Gabriel, Richard P. Patterns of Software: Habitability and the Piecemeal Growth. Oxford University Press, 1996.
- Hickey, Rich. “Simple Made Easy.” Strange Loop Conference, 2011.
- King, Alexis. “Parse, don’t validate.” Lexi-Lambda, 2019.
- Meyer, Bertrand. Object-Oriented Software Construction. Prentice Hall, 1988.
- Norman, Don. The Design of Everyday Things. Basic Books, 2013.
- Raskin, Jef. The Humane Interface: New Directions for Designing Interactive Systems. Addison-Wesley Professional, 2000.
- Sweller, John. “Cognitive Load Theory, Learning Difficulty, and Instructional Design.” Learning and Instruction, 1994.
- Zakirullin, Iskander. “Cognitive Load is What Matters.” Minds.md, 2023.