Jonathon Lacher

My little blog

Book notes: A Philosophy of Software Design, Chapters 1-3

Posted at — Jan 19, 2023

This is the first in a series of posts of my notes on the book A Philosophy of Software Design by John Ousterhout.

Most of my notes are loosely copied quotes, paraphrases, or summaries of my understanding. I’ve included the page number (from my edition) in parentheses.

Intro

  1. The greatest limitation in writing software is our ability to understand the systems we are creating. (1)
  2. There are two approaches to fighting complexity.
    1. Eliminate complexity by making code simpler and more obvious. (2)
    2. Encapsulate it, so you can work on the system without being exposed to all the complexity at once (2)
  3. Book as two goals: (3)
    1. Describe the nature of software complexity, what does “complexity” mean, why does it matter, how can you recognize it?
    2. Present techniques you can use to help minimize complexity.
  4. Best way to use this book is in conjunction with code reviews. (4)
  5. Good way to improve design skills to to learn go recognize red flags (4)
  6. Moderation and discretion is important. Everything has limits, don’t take the ideas in this book to the extreme. (4)

Chapter 2

  1. Complexity is anything related to the structure of a software system that makes it hard to understand and modify. (5)
  2. Complexity is more apparent to readers than writers. (6)
  3. Symptoms of complexity:
    1. Change amplification: A seemingly simple change requires modifications in many places. (7)
    2. Cognitive load: An engineer needs to spend a lot more time learning and needs to hold much more info in their mind to make the change. Complexity isn’t measured in the number of lines; sometimes the more verbose version of code is simpler because it’s not trying to be clever. (7)
    3. Unknown unknowns: When it’s not obvious what pieces of code must be modified to complete a task or even what info is needed to complete the task. This is the worst symptom, since you only find out about it after something unknown rears its head. (9)
  4. Causes of complexity:
    1. Dependencies
      1. A dep exists when a piece of code can’t be understood and modified in isolation. (9)
      2. Deps can’t be eliminated, but we can be intentional.
      3. Two goals of software design: (10) 1. Reduce the number of deps 2. Make deps that remain simple and obvious
    2. Obscurity
      1. Occurs when important information is not obvious.
      2. Obscurity is often associated with deps, where it’s not obvious that a dep exists. (10)
      3. Inconsistency is also a major contributor to obscurity.
      4. The need for extensive docs is often a red flag that the design isn’t good.
  5. Complexity isn’t caused by a single terrible error, but accumulates in small chunks over time. (11)
  6. Once complexity has accumulated, it’s hard to eliminate since fixing any one issue won’t make a big difference. (11)
  7. In order to slow the growth of complexity, we must have a zero tolerance approach.

Chapter 3

  1. Tactical programming is the mindset of only getting something working, nothing else is considered. (13)
  2. The problem with tactical programming is it’s short sighted. The accumulation of just trying to introduce a new feature or fix a bug introduces complexity and kludges, which is how systems get complicated. (13)
  3. Once you start taking the tactical approach, it’s difficult to change. (14)
  4. Tactical tornado: A prolific programmer who pumps out code faster than others but in a totally tactical fashion. They may be admired by management, but they leave a wake of destruction and messes that other engineers must clean up. (14)
  5. Strategic programming: Working code isn’t enough. The most important thing is the long-term structure of the system. Your primary goal is to produce a great design which also happens to work. (14)
  6. Strategic programming requires an investment mindset. Rather than taking the shortest and fastest path to finishing something, you must invest time to improve the design of the system. (15)
  7. Continually make small improvements to system design: (15)
    1. Iterate on a design, don’t use the first one you think of.
    2. Imagine a few ways a system might need to be changed in the future and make sure the current design makes this easy.
    3. Write good documentation.
  8. Big upfront investments in design don’t work, this is waterfall. The best approach is lots of small investments on a continual basis. (15)
  9. Suggestion: spend 10%-20% of total development time on investments. While you make take 10-20% longer at first, these will pay off in the long-run and future changes will be easier. (15-16)
  10. Once a codebase is spaghetti, it’s nearly impossible to fix. (17)
  11. The most important factor of success in a company is the quality of its engineers. The best engineers care deeply about good design. (17)
  12. Once you start delaying design improvements, it’s easy for delays to become permanent and your culture will slip into the tactical approach. (18)