Skip to content

Minimizing complexity

Principle · Chapter 6

High complexity in software:

  • Causes schedule delays and cost overruns.
  • Leads to unintended behavior or unanticipated application states.
  • Creates security loopholes or hides security issues.
  • Predicts lower quality attributes — worse maintainability, extensibility, reusability.

Essential vs. accidental difficulty (Fred Brooks)

Section titled “Essential vs. accidental difficulty (Fred Brooks)”
  • Essential difficulties are the core problems you are actually trying to solve — they cannot simply be removed. Teams spend most of their time here.
  • Accidental difficulties are incidental to producing software and can be reduced — better languages, frameworks, design patterns, IDEs, and methodologies chip away at them.
  • Architects work to minimize both. Key tools: KISS, DRY, information hiding, YAGNI, and SoC.
  • Systems generally work best kept simple; don’t overcomplicate solutions. (Coined by aeronautical engineer Kelly Johnson; “Stupid” was never about a person.)
  • Ways to apply KISS: eliminate duplication (DRY), remove unnecessary features (YAGNI), hide complexity and design decisions (information hiding), follow known standards and minimize surprises, refactor toward simpler methods/classes when you get the chance.
  • Caveat — don’t oversimplify: if simplicity starts hurting required functionality or quality attributes, you’ve gone too far. “As simple as possible, but not simpler.”
  • Reduce duplication in the codebase; duplication bloats the code, complicates maintenance, and risks inconsistent edits (the anti-pattern is a WET — “Write Everything Twice” — solution).
  • Copy-and-paste programming is a common source of duplication. (Small reusable snippets are fine; wholesale copying of application logic is not.)
  • Magic strings (literals duplicated across code — cache keys, messages, paths, URLs) are a classic violation; centralize them in a constant (or a resource file when translation/i18n matters).
  • Eliminate duplication via the abstraction principle: pull shared logic into one place and route callers through it. Duplicated processes can be removed via automation (e.g. automating tests/builds — see DevOps practices).
  • Don’t over-DRY: values or constants that are only coincidentally identical but represent distinct concepts should stay separate.
  • Modules should hide their implementation details from the rest of the system (D.L. Parnas, 1972). Callers interact only with a public interface and are shielded from internals.
  • Benefits:
    • Reduces complexity → improves maintainability (you don’t have to understand internals to use a module).
    • Hides design decisions (API choice, data representation, algorithm) so that if a decision changes, the modifications stay localized.
  • Forces you to deliberately decide what to expose vs. hide, rather than lazily exposing most of a class. The public interface is a contract; the implementation decides how it’s fulfilled. Supports the Law of Demeter (see orthogonal systems).

YAGNI — “You Aren’t Gonna Need It”

Section titled “YAGNI — “You Aren’t Gonna Need It””
  • From Extreme Programming. Implement functionality only when you actually need it, never because you foresee you might (Ron Jeffries).
  • Avoids over-engineering — speculative features often end up unneeded or their requirements change; unwritten code is time and money saved for what you do need.
  • Where YAGNI does not apply: it targets presumptive features, not work that keeps the system maintainable/modifiable later. A good, maintainable design is exactly what lets you add features later cheaply. Architecture decisions are made early and are costly to change, so experienced architects learn to spot the exceptions where a change should be made before it’s strictly needed.
  • A concern is an aspect of functionality the system provides. SoC partitions the system so each partition owns a separate concern, minimizing overlap.
  • Decompose a big problem into smaller, manageable concerns → less complexity, less change effort, higher quality. Following DRY tends to produce SoC naturally.
  • Applies at multiple levels:
    • Architecture: separate UI, business, and infrastructure logic — e.g. the Model-View-Controller pattern.
    • Class: distinct concerns (e.g. validating a credit card vs. updating inventory) shouldn’t live together — this ties into the Single Responsibility Principle.
    • Language: HTML (content), CSS (presentation), JavaScript (behavior).
  • Software Architect’s Handbook (Packt, 2018), Ch.6 “Minimizing complexity”, pp. 418-436.