Amstelden

A Philosophy of Software Design - John Ousterhout

Note: These are my own notes and observations upon reading the book. They serve as a reminder, when I need to rehash some content. I own none of the content.

1. Introduction

How to reduce complexity:

  1. make code simpler & obvious
  2. encapsulate complexity (modular programming) -- programmer is not exposed to all complexity at once

2. The Nature of Complexity

"Complexity is anything related to the structure of a software system that makes it hard to understand and modify the system."

complexity = sum(câ„—*tâ„—) where câ„— = complexity of part p & tâ„— = time developers spend working on part p

2.2. Symptoms of complexity

  1. Change amplification (any change => multiple things needing a change)
  2. Cognitive load (doing any change requires a lot of context)
  3. Unknown unknowns (unclear which parts of code need to be modified to get a task done)

2.3. Causes of complexity

  1. dependencies
  2. obscurity

"Complexity isn't caused by a single catastrophic error; it accumulates in lots of small chunks. [...] The incremental nature of complexity makes it hard to control."

3. Working Code Isn't Enough

Tactical programming = getting things done, ASAP Strategic programming = focus on long-term structure of the system

It's rarely worthwhile to engage in tactical programming, as strategic programming pays dividends very early on (6 months).

"Good design doesn't come for free."

4. Modules Should Be Deep

"An abstraction is a simplified view of an entity, which omits unimportant details."

Focus on Deep modules instead of Shallow modules.

Red Flag: Shallow Module

Deep modules tend to be general

"Interfaces should be designed to make the most common usage as simple as possible."

5. Information Hiding (and Leakage)

"Information leakage occurs when a design decision is reflected in multiple modules."

Red Flag: Information Leakage

Red Flag: Temporal Decomposition (when code is design based on when something is called, rather than what dependencies it should have -- think of JSON reader & writer -- makes no sense to have 2 abstractions, as they both depend on the format; have a single module to deal with this)

"Information hiding can often be improved by making a class slightly larger."

Red Flag: Overexposure (when the API forces you to learn about rarely used features -- the API should focus on the most common path, allowing features as optional add-ons you can learn about)

6. General-Purpose Modules are Deeper

Guiding questions:

  1. "What is the simplest interface that will cover all my current needs?"
  2. "In how many situations will this method be used?" -- if there's only 1 use-case, most likely the method is not general, interface is shallow
  3. "Is this API easy to use for my current needs?"

Eliminate special cases with general code where possible.

Separate specialized code from general code.

7. Different Layer, Different Abstraction

Red Flag: Pass-Through Method

Suggestion on pass-through variables -- general context objects; rebuttal -- they become huge bags of data that are hard to maintain and rarely thread-safe.

"Each piece of design infrastructure added to a system [...] adds complexity. In order for an element to provide a net gain against complexity, it must eliminate some complexity [...]."

8. Pull Complexity Downwards

"It is more important for a module to have a simple interface, than a simple implementation."

"Avoid configuration parameters as much as possible."

"When developing a module, look for opportunities to take a little bit of extra suffering upon yourself in order to reduce the suffering of your users."

9. Better Together or Better Apart?

"Bringing pieces of code together is most beneficial if they are closely related. If the pieces are unrelated, they are probably better off apart."

Red Flag: Repetition

Red Flag: Special-General Mixture (general use case code contains code for specific use cases)

Red Flag: Conjoined Methods (if you can't understand one method in isolation -- without understanding another, then you have conjoined methods)

"It's more important for a module to have a simple interface than a simple implementation."

10. Define Errors Out of Existence

11. Design It Twice

12. Why Write Comments? The Four Excuses

"If users must read the code of a method in order to use it, then there is no abstraction."

"The overall idea behind comments is to capture information that was in the mind of the designer but couldn't be represented in code."

13. Comments Should Describe Things That Are Not Obvious From Code

Red Flag: Comment Repeats Code

Can someone write the comment just by reading the code next to the comment? If yes, bad comment.

Red Flag: Implementation Documentation Contaminates Interface

14. Choosing Names

Red Flag: Vague Name

Red Flag: Hard to Pick Name

"The greater the distance between a name's declaration and its uses, the longer the name should be."

15. Write the Comments First

Red Flag: Hard to Describe

16. Modifying Existing Code

"Ideally, when you have finished with each change, the system will have the structure it would have had if you designed it from the start with that change in mind."

17. Consistency

18. Code Should Be Obvious

Red Flag: Non-obvious Code

Software iterations should be focused on abstractions, not features.

The place where TDD applies most -- you found a bug, write a test before fixing the bug.

20. Designing for Performance

21. Decide What Matters

21.2 Minimize what matters

"[...]minimize the number of parameters that must be specified to construct an object, or provide default values that reflect most common usage. For things that do matter, try to minimize the number of places where they matter."

21.4 Mistakes

  1. Treat too many things as important => clutter. I.E. Don't force users of your interface to be aware of all options -- focus on the usual path & provide everything else as optionals.
  2. Fail to recognize something is important => developers continuously recreating the functionality; leads to unknown unknowns.

"[...]'good taste' describes the ability to distinguish what is important from what isn't[...]"

"Separate what matters from what doesn't matter and emphasize the things that matter."

22. Conclusion