John Ousterhout’s “A Philosophy of Software Design” is a short read, but rich in wisdom and best practices. It’s the kind of book that makes you cling to a highlighter. Junior developers are often recommended larger books on similar topics. Clean Code is over 2x as long and Code Complete is nearly 5x longer. This might be my new recommendation since people are more likely to actually read it!
Tactical vs. Strategic Programming
The book contrasts two programming approaches: tactical and strategic. Tactical programming is about the short-term: getting code to work promptly, tolerating complexity and the odd hack to meet rapid delivery schedules. In contrast, strategic programming emphasizes well-designed code as the primary goal, even if it takes longer to achieve.
The agility-focused world often rewards the tactical, but Ousterhout warns of the long-term costs—technical debt and increased complexity that can haunt the code’s future maintainers and ultimately cause you to deliver worse software over budget and behind schedule.
Designing with Foresight
Ousterhout encourages developers to resist the path of least resistance, often taken when extending or fixing existing code. The smallest possible change isn’t always the best. He advocates for a design that would make sense as if the codebase was originally constructed with the current changes in mind.
Principles to Code By
Out of the 16 design principles Ousterhout presents, a few resonate strongly with me:
- Complexity is Incremental: Pay attention to the small stuff; it adds up.
- Define Errors Out of Existence: Design APIs that make incorrect use difficult.
- Abstractions Over Features: Build abstractions that hide details and simplify usage, rather than adding features that may increase complexity.
Red Flags in Software Design
The book doesn’t stop at principles; it also outlines ‘red flags’—signs of potential problems in design. Here are some key ones:
-
Information Leakage: When multiple modules reflect a single design decision, it’s a sign that abstractions might be leaking details they shouldn’t.
-
Overexposure: If an API forces awareness of features that are seldom needed, it’s probably revealing too much.
-
General-Specific Mixture: The lack of a clear separation between code for general use and code for special cases can lead to a convoluted mess.
-
Modules Should Be Shallow: A shallow module does its job without the caller needing to understand its internal workings, embodying the essence of good abstraction.
-
Temporal Decomposition: A Misstep Organizing code around timing rather than design logic is another misstep, leading to a scattered and confusing architecture.
-
Commentary on Comments: Even comments get a spotlight; if they’re merely echoing the code, they’re likely unnecessary. Names should be clear and self-explanatory, avoiding the need for redundant comments.