A Philosophy of Software Design
Main Argument
Complexity is the single greatest constraint on software development over a system's lifetime. The job of design is to manage complexity, and the most powerful lever is modular design: split the system into modules whose interfaces are far simpler than their implementations, so each developer faces only a small slice of the whole at a time.
Key Takeaways
- Complexity is whatever makes a system hard to understand and modify. It shows up as change amplification, cognitive load, and unknown unknowns, and it accumulates incrementally.
- Modules should be deep: a simple interface hiding a powerful implementation. Shallow modules whose interfaces are nearly as complex as their implementations add cost without benefit.
- Information hiding is the core mechanism; information leakage (the same design decision spread across modules) is the core failure, often caused by temporal decomposition.
- When forced to choose, pull complexity downward: a complex implementation is better than a complex interface, because the cost is paid once by the implementer instead of repeatedly by every caller.
- Special cases and exceptions are a major source of complexity; design them out of existence where possible.
- Design is an investment. Strategic (design-first) beats tactical (feature-first) over the life of a system.
- Generate at least two designs before committing. Code should be obvious; comments capture the design knowledge the code cannot.