A Philosophy of Software Design
Rate it:
Read between January 13 - January 18, 2021
7%
Flag icon
the greatest limitation in writing software is our ability to understand the systems we are creating.
9%
Flag icon
Complexity is anything related to the structure of a software system that makes it hard to understand and modify the system.
10%
Flag icon
Isolating complexity in a place where it will never be seen is almost as good as eliminating the complexity entirely.
10%
Flag icon
Sometimes an approach that requires more lines of code is actually simpler, because it reduces cognitive load.
11%
Flag icon
An unknown unknown means that there is something you need to know, but there is no way for you to find out what it is, or even whether there is an issue.
12%
Flag icon
Dependencies lead to change amplification and a high cognitive load. Obscurity creates unknown unknowns, and also contributes to cognitive load.
12%
Flag icon
It’s easy to convince yourself that a little bit of complexity introduced by your current change is no big deal. However, if every developer takes this approach for every change, complexity accumulates rapidly. Once complexity has accumulated, it is hard to eliminate, since fixing a single dependency or obscurity will not, by itself, make a big difference. In order to slow the growth of complexity, you must adopt a “zero tolerance” philosophy,
13%
Flag icon
Most programmers approach software development with a mindset I call tactical programming. In the tactical approach, your main focus is to get something working, such as a new feature or a bug fix.
13%
Flag icon
complexity is incremental.
13%
Flag icon
you program tactically, each programming task will contribute a few of these complexities.
13%
Flag icon
The first step towards becoming a good software designer is to realize that working code isn’t enough.
13%
Flag icon
Your primary goal must be to produce a great design, which also happens to work. This is strategic programming.
14%
Flag icon
the best approach is to make lots of small investments on a continual basis. I suggest spending about 10–20% of your total development time on investments.
14%
Flag icon
That extra time will result in a better software design, and you will start experiencing the benefits within a few months. It won’t be long before you’re developing at least 10–20% faster than you would if you had programmed tactically. At this point your investments become free: the benefits from your past investments will save enough time to cover the cost of future investments.
14%
Flag icon
15%
Flag icon
good design eventually pays for itself, and sooner than you might think.
15%
Flag icon
think of investment as something to do today, not tomorrow.
16%
Flag icon
The best modules are those whose interfaces are much simpler than their implementations.
19%
Flag icon
interfaces should be designed to make the common case as simple as possible
21%
Flag icon
Information leakage occurs when the same knowledge is used in multiple places, such as two different classes that both understand the format of a particular type of file.
22%
Flag icon
When designing modules, focus on the knowledge that’s needed to perform each task, not the order in which tasks occur.
22%
Flag icon
information hiding can often be improved by making a class slightly larger.
25%
Flag icon
If you take the special-purpose approach and discover additional uses later, you can always refactor it to make it general-purpose. The special-purpose approach seems consistent with an incremental approach to software development.
29%
Flag icon
A pass-through method is one that does nothing except pass its arguments to another method, usually with the same API as the pass-through method. This typically indicates that there is not a clean division of responsibility between the classes.
33%
Flag icon
it is more important for a module to have a simple interface than a simple implementation.
35%
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.
37%
Flag icon
When you encounter a class that includes both general-purpose and special-purpose features for the same abstraction, see if the class can be separated into two classes, one containing the general-purpose features, and the other layered on top of it to provide the special-purpose features.
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.
50%
Flag icon
Designing software is hard, so it’s unlikely that your first thoughts about how to structure a module or system will produce the best design.
53%
Flag icon
If users must read the code of a method in order to use it, then there is no abstraction:
54%
Flag icon
comments should describe things that aren’t obvious from the code.
55%
Flag icon
Developers should be able to understand the abstraction provided by a module without reading any code other than its externally visible declarations. The only way to do this is by supplementing the declarations with comments.
57%
Flag icon
If the information in a comment is already obvious from the code next to the comment, then the comment isn’t helpful. One example of this is when the comment uses the same words that make up the name of the thing it is describing.
57%
Flag icon
use different words in the comment from those in the name of the entity being described.
57%
Flag icon
Comments augment the code by providing information at a different level of detail.
61%
Flag icon
you want code that presents good abstractions, you must document those abstractions with comments.
61%
Flag icon
If interface comments must also describe the implementation, then the class or method is shallow.
66%
Flag icon
The main goal of implementation comments is to help readers understand what the code is doing (not how it does it).
69%
Flag icon
When following the rule that comments should describe things that aren’t obvious from the code, “obvious” is from the perspective of someone reading your code for the first time (not you). When writing comments, try to put yourself in the mindset of the reader and ask yourself what are the key things he or she will need to know. If your code is undergoing review and a reviewer tells you that something is not obvious, don’t argue with them; if a reader thinks it’s not obvious, then it’s not obvious. Instead of arguing, try to understand what they found confusing and see if you can clarify that, ...more
76%
Flag icon
The comment that describes a method or variable should be simple and yet complete. If you find it difficult to write such a comment, that’s an indicator that there may be a problem with the design of the thing you are describing.
78%
Flag icon
Ideally, 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.
78%
Flag icon
an investment mindset sometimes conflicts with the realities of commercial software development.
78%
Flag icon
The best way to ensure that comments get updated is to position them close to the code they describe,
80%
Flag icon
If information is already documented someplace outside your program, don’t repeat the documentation inside the program; just reference the external documentation.
82%
Flag icon
Having a “better idea” is not a sufficient excuse to introduce inconsistencies.
86%
Flag icon
software should be designed for ease of reading, not ease of writing.
88%
Flag icon
the increments of development should be abstractions, not features.
89%
Flag icon
The problem with test-driven development is that it focuses attention on getting specific features working, rather than finding the best design. This is tactical programming pure and simple, with all of its disadvantages. Test-driven development is too incremental: at any point in time, it’s tempting to just hack in the next feature to make the next test pass. There’s no obvious time to do design, so it’s easy to end up with a mess.
90%
Flag icon
Whenever you encounter a proposal for a new software development paradigm, challenge it from the standpoint of complexity:
99%
Flag icon
Complexity is incremental: you have to sweat the small stuff (see p. 11). Working code isn’t enough (see p. 14). Make continual small investments to improve system design (see p. 15). Modules should be deep (see p. 22) Interfaces should be designed to make the most common usage as simple as possible (see p. 27). It’s more important for a module to have a simple interface than a simple implementation (see pp. 55, 71). General-purpose modules are deeper (see p. 39). Separate general-purpose and special-purpose code (see p. 62). Different layers should have different abstractions (see p. 45). ...more