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.