A Philosophy of Software Design
John Ousterhout
Summary: reducing complexity of software, from the author of Tcl
Score: 85 / 100
Chapter 1: Introduction
Simpler designs allow us to build larger and more powerful systems before complexity becomes overwhelming.
To fight complexity we can:
- Make code simpler.
- Modularize it.
Modularization is useful for us humans as we have limited capacity of working memory.
Waterfall model:
----
----
----
----
Project is designed at the beginnins and does not allow changes. Like in normal engineering.
Agile development:
xxxx
x xx xxxx xxxxx
x xx x xx x xx xx
xxxxxxxxxx x x x x
xx x xx xx x
xxx xxxx x xx
xxxxx x xx
xxxxxxxxxx
x
xx
xxxxxxxx
Development in agile is incremental.
Developers should always think about design issues.
Chapter 2: Complexity
Complexity = dependency + obscurity
What about simple system but with many dependencies? It might be hard to do a change/fix if a dependency breaks… Also, a framework allows for small changes but it’s hard to know what the changes should be.
Chapter 3: Strategic vs. tactical programming
Working code isn’t enough. The best way to lower costs is to hire great engineers.
Chapter 4: Modules should be deep
Interface (what module does) and implementation (how it does it).
------interface----
| |
####### |
| | |
| | ####################
| | | |
| | | | |
| | | -----------|--------
| | | |
---|--- |
| |
-----implementation----
Thought: modern IDEs encourage shallow modules: users don’t need to remember much, there is the IDE’s autocomplete…
Chapter 5: Information hiding (vs. leakage)
One of the best skills: a high level of sensitivity to information leakage.
Pass-through methods.
↓ ↓ ↓ ↓ |
A B C D |
↓ ↓ ↓ ↓
B C D E
Solutions:
a) invoke directly
↓ | | | |
A | | | |
↓ ↓ ↓ ↓
B C D E
b) redistribute functionality
↓ ↓ ↓ ↓ ↓
A B C D E
c) combine
↓ ↓ ↓ ↓ ↓
A B C D E
Chapter 8: Complexity
Most modules have more users than developers so it is better for the developers to suffer.
Suffer instead of your user.
Chapter 9: Together or apart?
- Are modules co-occurring? → together
- Information is shared? → together
- Would simplify interface → together
- De-duplicating → together
In general, the trend is to break up methods too much!
CALLERS CALLERS CALLERS CALLERS
↓ ↓ ↓ ↓ ↓ | ↓
########### ####### ##### ### ##### | #####
| | | | | | | | | | | | |
| | | | | | | | | | | | ↓ -----
----------- ----|-- ----- --- --|-- #########
↓ ↓ | |
##### ##### ---------
| | | |
----- -----
rarely avoid!
works
Each method should do one thing and do it completely.
Split or join based on complexity!
Chapter 10: Define errors out of existence
Exceptions are hard to test.
Code that hasn’t been executed doesn’t work.
Exceptions make interface more complex.
Throwing is easy, handling is hard. Reduce number of places where exceptions have to be handled.
Chapter 11: Design it twice
This will help you come up with a better design.
Chapter 12: Comments (2023-06-10)
Good for:
- rationale for a design
- what is not represented in code
- high level description
Myth: good code is self-documentary.
To capture information in mind not representable in the code.
Chapter 13: Comments to describe the non-obvious
Distinguish (and separate) interface and implementation comments!
Chapter 14: Choosing names
Underrated aspect of software engineering. It’s a form of documentation.
(Does functional code with limited use of variables is less error-prone?)
Good names are consistent and precise.
If it’s hard to pick a name, the code might need refactoring?
Chapter 15: Write the comments first
It helps with design process. Delayed comments won’t happen at all.
Is it hard to describe/comment a code: It might be an indicator of a design problem?
Simple comments indicate good abstractions.
Chapter 16: Modifying existing code
Every change (adding a feature, refactoring, …) should improve the codebase. Is this attainable?
When you have finished with each change, the system will have the structure it would have had if you had designed it from the start with that change in mind.
If you are not making the design better you are probably making it worse.
Keep comments near the code.
The farther from code the more abstract should a comment be. It helps with obsoleting comments as well.
Refer to external documentation, do not repeat / duplicate info.
Chapter 17: Consistency 2023-06-23
Document and enforce rules. Follow local (file) conventions.
Chapter 18: Code should be obvious
Good names, consistency helps. Formatting comments (typography) helps.
General rule:
software should be designed to ease to be read not to be written.
Chapter 19: Software trends 2023-07-02
- object-oriented programming
- class inheritance, private methods, interface and implementation inheritance (use the latter with caution)
- agile
- unit tests: aim at increasing coverage; integration tests
- test-driven development: attention on features running more than on the best design
- good use: write test for a bug before fixing it
- design patterns: apply a well-tested pattern to a problem
- getters and setters
Chapter 20: Designing for performance
- use micro-benchmarking
- measure before modifying
- a method: design around the critical path
- don’t deal with special cases, optimize for a common case
Conclusion: this was well worth reading!
The author has a few useful concepts:
- deep / shallow modules,
- pass-through methods,
- tactical vs. strategic programming
last modified: 2023-07-03
https://vit.baisa.cz/books/a-philosophy-of-software-design/