Created
May 6, 2026 14:49
-
-
Save 8ullyMaguire/764e7dc375d3f9d34cadb0d668bd518d to your computer and use it in GitHub Desktop.
A Philosophy of Software Design by John Ousterhout
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| **Summary of *A Philosophy of Software Design* by John Ousterhout** | |
| Source: danlebrero.com | |
| These are notes by Daniel Lebrero Berna on John Ousterhout’s *A Philosophy of Software Design.* | |
| Some advice in the book goes against the current software dogma. The current dogma is the result of previous pains, but has now been taken to the extreme, causing new pains. | |
| What the author solves with “Comment-First Development,” others solve with Test-Driven Development. The excuses for not writing comments mirror those for not writing tests. | |
| --- | |
| **Key Insights** | |
| - It’s easier to see design problems in someone else’s code than your own. | |
| - Total complexity equals the sum over each part of the complexity of that part multiplied by the time spent on that part. | |
| - The goal of good design is to make the system obvious. | |
| - Complexity accumulates incrementally, making it hard to remove. Adopt a zero tolerance philosophy. | |
| - Better modules have an interface that is much simpler than their implementation – these are deep modules. | |
| - Design modules around required knowledge, not task order. | |
| - Adjacent layers with similar abstractions are a red flag. | |
| - Prioritize simple interfaces over simple implementations. | |
| - Each method should do one thing and do it completely. | |
| - Long methods are fine if the signature is simple and the code easy to read. | |
| - Difficulty naming a method may indicate an unclear design. | |
| - Comments should add precision or intuition. | |
| - If you aren’t improving the design when changing code, you’re probably making it worse. | |
| - Comments belong in the code, not commit logs. | |
| - Poor designers spend most of their time chasing bugs in brittle code. | |
| --- | |
| **Preface** | |
| The most fundamental problem in computer science is problem decomposition. The book is an opinion piece. Its goal is to reduce complexity. | |
| **1. Introduction (It’s All About Complexity)** | |
| Fight complexity by simplifying and encapsulating it in modules. Software design is never finished. Design flaws are easier to see in others’ code. | |
| **2. The Nature of Complexity** | |
| Complexity is what makes code hard to understand or modify. Total complexity depends on time spent in each part. Complexity is more obvious to readers than writers. Symptoms include change amplification, cognitive load, and unknown unknowns. Causes include dependencies and obscurity. Complexity accumulates incrementally; remove it aggressively. | |
| **3. Working Code Isn’t Enough** | |
| Distinguish tactical programming (short-term) from strategic programming (long-term). The “tactical tornado” writes lots of code fast but increases complexity. | |
| **4. Modules Should Be Deep** | |
| A module consists of an interface plus an implementation. Deep modules have simple interfaces and complex implementations. The interface is what clients must know, both formal and informal. Avoid “classitis” – too many small classes increase system complexity. Interfaces should make the common case simple. | |
| **5. Information Hiding (and Leakage)** | |
| Information hiding is key to deep modules. Avoid temporal decomposition (ordering-based design). Larger classes can improve information hiding. | |
| **6. General-Purpose Modules Are Deeper** | |
| Make modules somewhat general-purpose. Implementation fits current needs; the interface supports future reuse. Questions to balance generality: What is the simplest interface covering current needs? How many times will it be used? Is the API simple for the current use? If not, it is too general. | |
| **7. Different Layer, Different Abstraction** | |
| Adjacent layers with similar abstractions are a red flag. Pass-through methods and variables add no value. Fix pass-throughs by grouping related data or using shared or context objects. | |
| **8. Pull Complexity Downwards** | |
| Prefer simple interfaces over simple implementations. Push complexity into lower layers. Avoid configuration parameters; compute reasonable defaults automatically. | |
| **9. Better Together or Better Apart?** | |
| Combine elements when they share information, are used together, overlap conceptually, or when combining simplifies interfaces or eliminates duplication. Developers often split methods too much. Methods can be long if they are cohesive and clear. A red flag is when one component requires understanding another’s implementation. | |
| **10. Define Errors Out of Existence** | |
| Exception handling increases complexity. Reduce exception points by designing APIs that eliminate exceptional cases, handling exceptions at low levels, aggregating exceptions into a common type, or crashing when appropriate. | |
| **11. Design It Twice** | |
| Explore at least two radically different designs before choosing. | |
| **12. Why Write Comments? The Four Excuses** | |
| Writing comments improves design and can be enjoyable. Excuses: “Good code is self-documenting” (false); “No time to write comments” (it is an investment); “Comments get outdated” (update them); “Comments are worthless” (learn to write better ones). | |
| **13. Comments Should Describe Things That Aren’t Obvious** | |
| Comments should add precision and intuition. Document both interface and implementation. | |
| **14. Choosing Names** | |
| Names should be precise and consistent. If naming is hard, the design likely isn’t clean. | |
| **15. Write the Comment First** | |
| Like test-driven development, comment-first helps design, pacing, and clarity. | |
| **16. Modifying Existing Code** | |
| Always improve design when changing code. Comments belong in code, not commit logs. | |
| **17. Consistency** | |
| Don’t “improve” existing conventions without strong reason. | |
| **19. Software Trends** | |
| Agile and test-driven development often promote tactical programming. | |
| **20. Designing for Performance** | |
| Simpler code tends to be faster. Design around the critical path. | |
| **21. Conclusion** | |
| Poor designers spend their time debugging brittle systems. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment