A Philosophy of Software Design
Rate it:
Open Preview
Read between June 7 - July 14, 2023
4%
Flag icon
People have been writing programs for electronic computers for more than 80 years, but there has been surprisingly little conversation about how to design those programs or what good programs should look like.
5%
Flag icon
The most fundamental problem in computer science is problem decomposition:
6%
Flag icon
I recommend that you take the suggestions in this book with a grain of salt. The overall goal is to reduce complexity; this is more important than any particular principle or idea you read here.
6%
Flag icon
the greatest limitation in writing software is our ability to understand the systems we are creating.
7%
Flag icon
Incremental development also means continuous redesign.
8%
Flag icon
It is easier to tell whether a design is simple than it is to create a simple design, but once you can recognize that a system is too complicated, you can use that ability to guide your design philosophy towards simplicity.
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 what a developer experiences at a particular point in time when trying to achieve a particular goal. It doesn’t necessarily relate to the overall size or functionality of the system.
9%
Flag icon
Isolating complexity in a place where it will never be seen is almost as good as eliminating the complexity entirely.
9%
Flag icon
Complexity is more apparent to readers than writers. If you write a piece of code and it seems simple to you, but other people think it is complex, then it is complex.
9%
Flag icon
The first symptom of complexity is that a seemingly simple change requires code modifications in many different places.
9%
Flag icon
One of the goals of good design is to reduce the amount of code that is affected by each design decision, so design changes don’t require very many code modifications.
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.
10%
Flag icon
One of the most important goals of good design is for a system to be obvious. This is the opposite of high cognitive load and unknown unknowns.
10%
Flag icon
Complexity is caused by two things: dependencies and obscurity.
10%
Flag icon
a dependency exists when a given piece of code cannot be understood and modified in isolation; the code relates in some way to other code, and the other code must be considered and/or modified if the given code is changed.
11%
Flag icon
The need for extensive documentation is often a red flag that the design isn’t quite right.
12%
Flag icon
The first step towards becoming a good software designer is to realize that working code isn’t enough. It’s not acceptable to introduce unnecessary complexities in order to finish your current task faster. The most important thing is the long-term structure of the system.
13%
Flag icon
If you program strategically, you will continually make small improvements to the system design. This is the opposite of tactical programming, where you are continually adding small bits of complexity that cause problems in the future.
13%
Flag icon
The term technical debt is often used to describe the problems caused by tactical programming. By programming tactically you are borrowing time from the future: development will go more quickly now, but more slowly later on.
15%
Flag icon
the interface describes what the module does but not how it does it. The implementation consists of the code that carries out the promises made by the interface. A developer working in a particular module must understand the interface and implementation of that module, plus the interfaces of any other modules invoked by the given module. A developer should not need to understand the implementations of modules other than the one he or she is working in.
15%
Flag icon
For the purposes of this book, a module is any unit of code that has an interface and an implementation.
15%
Flag icon
The best modules are those whose interfaces are much simpler than their implementations.
16%
Flag icon
The term abstraction is closely related to the idea of modular design. An abstraction is a simplified view of an entity, which omits unimportant details. Abstractions are useful because they make it easier for us to think about and manipulate complex things.
16%
Flag icon
The key to designing abstractions is to understand what is important, and to look for designs that minimize the amount of information that is important.
16%
Flag icon
The best modules are those that provide powerful functionality yet have simple interfaces. I use the term deep to describe such modules.
18%
Flag icon
The extreme of the “classes should be small” approach is a syndrome I call classitis, which stems from the mistaken view that “classes are good, so more classes are better.” In systems suffering from classitis, developers are encouraged to minimize the amount of functionality in each new class: if you want more functionality, introduce more classes. Classitis may result in classes that are individually simple, but it increases the complexity of the overall system. Small classes don’t contribute much functionality, so there have to be a lot of them, each with its own interface. These interfaces ...more
19%
Flag icon
Perhaps the Java developers would argue that not everyone wants to use buffering for file I/O, so it shouldn’t be built into the base mechanism. They might argue that it’s better to keep buffering separate, so people can choose whether or not to use it. Providing choice is good, but interfaces should be designed to make the common case as simple as possible
19%
Flag icon
Users of a module need only understand the abstraction provided by its interface. The most important issue in designing classes and other modules is to make them deep, so that they have simple interfaces for the common use cases, yet still provide significant functionality.
19%
Flag icon
One of the most important techniques for achieving deep modules is information hiding.
20%
Flag icon
Information hiding reduces complexity in two ways. First, it simplifies the interface to a module.
20%
Flag icon
Second, information hiding makes it easier to evolve the system.
20%
Flag icon
The opposite of information hiding is information leakage. Information leakage occurs when a design decision is reflected in multiple modules.
20%
Flag icon
Information leakage is one of the most important red flags in software design. One of the best skills you can learn as a software designer is a high level of sensitivity to information leakage.
21%
Flag icon
One common cause of information leakage is a design style I call temporal decomposition. In temporal decomposition, the structure of a system corresponds to the time order in which operations will occur.
21%
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.
23%
Flag icon
Defaults illustrate the principle that interfaces should be designed to make the common case as simple as possible.
24%
Flag icon
Try to design the private methods within a class so that each method encapsulates some information or capability and hides it from the rest of the class.
24%
Flag icon
As a software designer, your goal should be to minimize the amount of information needed outside a module; for example, if a module can automatically adjust its configuration, that is better than exposing configuration parameters. But, it’s important to recognize which information is needed outside a module and make sure it is exposed.
24%
Flag icon
I have found over and over that specialization leads to complexity; I now think that over-specialization may be the single greatest cause of complexity in software.
25%
Flag icon
What particularly surprised me is that general-purpose interfaces are simpler and deeper than special-purpose ones, and they result in less code in the implementation.
25%
Flag icon
It turns out that even if you use a class in a special-purpose way, it’s less work to build it in a general-purpose way.
25%
Flag icon
general-purpose is still better even if you don’t reuse the class.
25%
Flag icon
the sweet spot is to implement new modules in a somewhat general-purpose fashion.
25%
Flag icon
The interface should be easy to use for today’s needs without being tied specifically to them.
26%
Flag icon
With the general-purpose text API, the code to implement user interface functions such as delete and backspace is a bit longer than with the original approach using a specialized text API. However, the new code is more obvious than the old code.
27%
Flag icon
The backspace method in the original version of the text class was a false abstraction. It purported to hide information about which characters are deleted, but the user interface module really needs to know this; user interface developers are likely to read the code of the backspace method in order to confirm its precise behavior.
« Prev 1 3