More on this book
Community
Kindle Notes & Highlights
The most fundamental problem in computer science is problem decomposition:
The overall goal is to reduce complexity;
The first approach is to eliminate complexity by making code simpler and more obvious.
The second approach to complexity is to encapsulate it, so that programmers can work on a system without being exposed to all of its complexity at once. This approach is called modular design.
design was concentrated at the beginning of a project,
The extreme of this approach is called the waterfall model,
divided into discrete phases such as requirements definition, design, coding, t...
This highlight has been truncated due to consecutive passage length restrictions.
in which the initial design focuses on a small subset of the overall functionality. This subset is designed, implemented, and then evaluated.
Each iteration exposes problems with the existing design, which are fixed before the next set of features is designed.
Incremental development means that software design is never done.
Design happens continuously over the life of a system:
The ability to recognize complexity is a crucial design skill.
Complexity is anything related to the structure of a software system that makes it hard to understand and modify the system.
Isolating complexity in a place where it will never be seen is almost as good as eliminating the complexity entirely.
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.
Change amplification: The first symptom of complexity is that a seemingly simple change requires code modifications in many different places.
Cognitive load: The second symptom of complexity is cognitive load, which refers to how much a developer needs to know in order to complete a task.
Sometimes an approach that requires more lines of code is actually simpler, because it reduces cognitive load.
Unknown unknowns: 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.
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.
One of the most important goals of good design is for a system to be obvious.
Complexity is caused by two things: dependencies and obscurity.
For the purposes of this book, a dependency exists when a given piece of code cannot be understood and modified in isolation;
In many cases, obscurity comes about because of inadequate documentation;
However, obscurity is also a design issue. If a system has a clean and obvious design, then it will need less documentation.
Dependencies lead to change amplification and a high cognitive load. Obscurity creates unknown unknowns, and also contributes to cognitive load.
The incremental nature of complexity makes it hard to control.
In the tactical approach, your main focus is to get something working,
kludge
that working code isn’t enough.
The most important thing is the long-term structure of the system.
Your primary goal must be to produce a great design, which also happens to work.
it’s worth taking a little extra time to find a simple design for each new class;
Writing good documentation is another example of a proactive investment.
I suggest spending about 10–20% of your total development time on investments.
“Move fast with solid infrastructure”
developers only need to face a small fraction of the overall complexity at any given time. This approach is called modular design,
a software system is decomposed into a collection of modules that are relatively independent.
The goal of modular design is to minimize the dependencies between modules.
Typically, 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.
The best modules are those whose interfaces are much simpler than their implementations.
First, a simple interface minimizes the complexity that a module imposes on the rest of the system.
The formal parts of an interface are specified explicitly in the code,
An abstraction is a simplified view of an entity, which omits unimportant details.
First, it can include details that are not really important;
The second error is when an abstraction omits details that really are important. This results in obscurity:
The best modules are those that provide powerful functionality yet have simple interfaces.
a shallow module is one whose interface is relatively complex in comparison to the functionality that it provides.
interfaces should be designed to make the common case as simple as possible
The most important technique for achieving deep modules is information hiding.