A Philosophy of Software Design
Rate it:
Open Preview
8%
Flag icon
Complexity is anything related to the structure of a software system that makes it hard to understand and modify the system.
9%
Flag icon
Complexity is determined by the activities that are most common. If a system has a few parts that are very complicated, but those parts almost never need to be touched, then they don’t have much impact on the overall complexity of the system.
9%
Flag icon
The first symptom of complexity is that a seemingly simple change requires code modifications in many different places.
9%
Flag icon
The second symptom of complexity is cognitive load, which refers to how much a developer needs to know in order to complete a task.
10%
Flag icon
Sometimes an approach that requires more lines of code is actually simpler, because it reduces cognitive load.
10%
Flag icon
The third symptom of complexity is that it is not obvious which pieces of code must be modified to complete a task, or what information a developer must have to carry out the task successfully.
11%
Flag icon
Complexity isn’t caused by a single catastrophic error; it accumulates in lots of small chunks.
11%
Flag icon
The bottom line is that complexity makes it difficult and risky to modify an existing code base.
12%
Flag icon
In the tactical approach, your main focus is to get something working,
12%
Flag icon
working code isn’t enough.
13%
Flag icon
The term technical debt is often used to describe the problems caused by tactical programming.
14%
Flag icon
The best way to lower development costs is to hire great engineers: they don’t cost much more than mediocre engineers but have tremendously higher productivity. However, the best engineers care deeply about good design. If your code base is a wreck, word will get out, and this will make it harder for you to recruit. As a result, you are likely to end up with mediocre engineers. This will increase your future costs and probably cause the system structure to degrade even more.
14%
Flag icon
These examples show that a company can succeed with either approach. However, it’s a lot more fun to work in a company that cares about software design and has a clean code base.
14%
Flag icon
Good design doesn’t come for free. It has to be something you invest in continually, so that small problems don’t accumulate into big ones. Fortunately, good design eventually pays for itself, and sooner than you might think.
14%
Flag icon
It’s crucial to be consistent in applying the strategic approach and to think of investment as something to do today, not tomorrow.
14%
Flag icon
The longer you wait to address design problems, the bigger they become;
14%
Flag icon
The most effective approach is one where every engineer makes continuous small investments in good design.
21%
Flag icon
When designing modules, focus on the knowledge that’s needed to perform each task, not the order in which tasks occur.
37%
Flag icon
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.
40%
Flag icon
This red flag occurs when a general-purpose mechanism also contains code specialized for a particular use of that mechanism. This makes the mechanism more complicated and creates information leakage between the mechanism and the particular use case: future modifications to the use case are likely to require changes to the underlying mechanism as well.
42%
Flag icon
It should be possible to understand each method independently. If you can’t understand the implementation of one method without also understanding the implementation of another, that’s a red flag. This red flag can occur in other contexts as well: if two pieces of code are physically separated, but each can only be understood by looking at the other, that is a red flag.
43%
Flag icon
Depth is more important than length: first make functions deep, then try to make them short enough to be easily read. Don’t sacrifice depth for length.
43%
Flag icon
The decision to split or join modules should be based on complexity. Pick the structure that results in the best information hiding, the fewest dependencies, and the deepest interfaces.
46%
Flag icon
File deletion provides another example of how errors can be defined away. The Windows operating system does not permit a file to be deleted if it is open in a process. This is a continual source of frustration for developers and users. In order to delete a file that is in use, the user must search through the system to find the process that has the file open, and then kill that process. Sometimes users give up and reboot their system, just so they can delete a file. The Unix operating system defines file deletion more elegantly. In Unix, if a file is open when it is deleted, Unix does not ...more
51%
Flag icon
Try to pick approaches that are radically different from each other;
52%
Flag icon
The design-it-twice approach not only improves your designs, but it also improves your design skills. The process of devising and comparing multiple approaches will teach you about the factors that make designs better or worse. Over time, this will make it easier for you to rule out bad designs and hone in on really great ones.
53%
Flag icon
comments are fundamental to abstractions.
53%
Flag icon
an abstraction is a simplified view of an entity, which preserves essential information but omits details that can safely be ignored.
53%
Flag icon
If users must read the code of a method in order to use it, then there is no abstraction:
53%
Flag icon
If you want to use abstractions to hide complexity, comments are essential.
54%
Flag icon
Good documentation helps with the last two of these issues. Documentation can reduce cognitive load by providing developers with the information they need to make changes and by making it easy for developers to ignore information that is irrelevant.
55%
Flag icon
Well-written comments are not failures. They increase the value of code and serve a fundamental role in defining abstractions and managing system complexity.