Three Different Notions of Software Complexity - Article Recap
A recap examining three influential perspectives on software complexity from Rich Hickey, John Ousterhout, and Zach Tellman, and how they define what makes software complex versus simple.
- Three perspectives examined: The article analyzes complexity definitions from Rich Hickey's "Simple Made Easy" talk, John Ousterhout's "A Philosophy of Software Design" book, and Zach Tellman's "Explaining Software Design" newsletter.
- Rich Hickey's simplicity: Defined by 'oneness'—one fold or braid, one role, one task, one concept, one dimension—the opposite of interleaving.
- Complexity as complecting: Hickey defines complexity as interleaving things together, creating intertwined relationships and tight coupling.
- Simple vs. easy distinction: Simple is about objective lack of connections; easy is about proximity, familiarity, and subjective experience—something easy for one person can be hard for another.
- Objective simplicity: Simplicity is an objective property of systems (low interleaving), while ease is subjective and depends on the observer.
- Hickey's examples: Parentheses in Clojure are simple (low interleaving) even if they aren't easy for all users to learn initially.
- Simplicity enables understanding: Simple systems are more reliable, flexible, and easier to reason about—complexity (tight coupling) undermines these qualities.
- Simple vs. complex constructs: Hickey contrasts complex constructs (state, objects, inheritance, imperative loops) with simple alternatives (values, functions, data).
- Ousterhout's focus: How software structure makes systems hard to understand and modify, identifying three symptoms of problematic complexity.
- Change amplification: Simple changes require modifications in many parts of the code—a sign that complexity has spread throughout the system.
- Cognitive load: The mental burden required to understand and work with the code—high cognitive load indicates poor design.
- Unknown unknowns: Lack of clarity about what needs to change or what developers must know to implement features—dangerous and frustrating.
- Complexity creep: Complexity sneaks into systems over time, often unnoticed until it causes significant trouble and hampers development.
- Design trade-offs: Ousterhout emphasizes balancing under-design (leading to complexity) and over-engineering (creating unnecessary complexity).
- Tellman's perspective: Focuses on how incidental dependencies accumulate over time and how design choices impact complexity growth.
- Essential vs. accidental: Complexity can be essential (inherent to the problem domain) or accidental (introduced by our design decisions and tool choices).
- Undermines maintainability: Complexity damages understanding, making systems harder to debug, extend, and reason about over time.
- Measurement matters: Metrics like cyclomatic complexity, information flow metrics, and object-oriented metrics can help quantify complexity.
- Design principles: Strive for simple (not just easy) designs by minimizing dependencies and interleaving between components.
- Cost of complexity: Measure complexity costs in terms of understanding effort, change effort, and cognitive load on developers.
- Intentional design: Recognize complexity as fundamental in large systems but manageable through intentional, careful design choices.
- Minimize interconnections: Focus on reducing coupling, separating concerns clearly, and avoiding unnecessary dependencies.
The full article is available here.