A Philosophy of Software Design

Cards (73)

  • The most fundamental problem in computer science is problem decomposition
  • Problem decomposition = how to take a problem and break it down into smaller problems that can be solved independently
  • 2 general approaches to fighting complexity:
    1. Make code simpler and more obvious
    2. Encapsulate the complexity
  • Red flags that suggest code is more complicated than it needs to be are your hint that you haven’t found the right design yet
  • Complexity = anything related to the structure of the software system that makes it hard to understand and modify the system
  • Complexity of code is more apparent to readers of the code than writers
  • If a reader of your code thinks it is complicated then it is complicated. They decide not you
  • Symptoms of complexity:
    1. Change amplification
    2. Cognitive load
    3. Unknown unknowns
  • Change amplification as a sign of complexity is when changing code in one place requires you to change code in many places
  • Cognitive load as a symptom of complexity is when developers need to know a lot and hold it all in their head in order to change some code
  • Unknown unknowns as a symptom of complexity is when it’s not obvious which pieces of code must be modified to complete the task
  • Unknown unknowns are the worst symptom of complexity because at least with the other two the programmer knows what they need to do to complete the task
  • A well designed system makes things obvious
  • Complexity is caused by:
    1. Dependencies
    2. Obscurity
  • A dependency exists when a given piece of code cannot be understood and modified in isolation
  • Obscurity exists in a system when important information is not obvious
  • Complexity is usually caused by lots of small decisions instead of one big decision
  • Tactical programming = when you get something working as fast as possible without consideration for its impact on the systems complexity
  • Tactical tornado = a prolific programmer who pumps out code faster than others but that works in a totally tactical fashion
  • Good programmers know that writing working code is not enough, it needs to also minimise system complexity
  • Strategic programming is when you don’t just care about the code working but you try to produce a great design that minimises system complexity
  • Strategic programming requires a more long-term investment mindset
  • The author suggests spending 10-20% of regular development time on investments that reduce system complexity
  • Strategic programming pays off over the long-term
  • Every module has an interface and an implementation
  • An interface described what a module does but not how it does it
  • An implementation is the code that carries out the promises of the interface
  • The best modules are ones whose interfaces are much simpler than their implementations
  • Clear interfaces can help reduce unknown unknowns
  • Abstraction = a simplified view of an entity which omits unimportant details
  • An abstraction can go wrong in two ways:
    1. by including details that are unimportant
    2. by omitting details that are important
  • Deep modules are better
  • Deep modules provide powerful functionality yet have simple interfaces
  • The benefit provided by a module is its functionality and its cost in terms of system complexity is its interface
  • Shallow modules are a red flag
  • A shallow module is one whose interface is complicated relative to the functionality it provides
  • Many people mistakenly believe classes should always be small, but instead the author thinks they should be deep
  • Interfaces should be designed to make the common path in particular as simple as possible
  • Interfaces should not reveal unimportant information to the user and instead should hide that information
  • Information hiding reduces complexity in 2 ways:
    1. Simplifies the interface
    2. Reduces dependencies on the information so makes it easier to evolve the system