More on this book
Community
Kindle Notes & Highlights
red flags: signs that a piece of code is probably more complicated than it needs to be.
Complexity is anything related to the structure of a software system that makes it hard to understand and modify the system.
If a software system is hard to understand and modify, then it is complicated; if it is easy to understand and modify, then it is simple.
Change amplification: The first symptom of complexity is that a seemingly simple change requires code modifications in many different places.
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.
One of the most important goals of good design is for a system to be obvious.
In an obvious system, a developer can quickly understand how the existing code works and what is required to make a change.
An obvious system is one where a developer can make a quick guess about what to do, without thinking very hard, and yet be c...
This highlight has been truncated due to consecutive passage length restrictions.
Complexity is caused by two things: dependencies and obscurity.
one of the goals of software design is to reduce the number of dependencies and to make the dependencies that remain as simple and obvious as possible.
The best way to reduce obscurity is by simplifying the system design.
As complexity increases, it leads to change amplification, a high cognitive load, and unknown unknowns.
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.
Strategic programming requires an investment mindset. Rather than taking the fastest path to finish your current project, you must invest time to improve the design of the system. These investments will slow you down a bit in the short term, but they will speed you up in the long term,
I suggest spending about 10–20% of your total development time on investments.
The goal of modular design is to minimize the dependencies between modules.
In order to identify and manage dependencies, we think of each module in two parts: an interface and an implementation.
The best modules are those whose interfaces are much simpler than their implementations.
An abstraction is a simplified view of an entity, which omits unimportant details.
An abstraction that omits important details is a false abstraction: it might appear simple, but in reality it isn’t.
The best modules are deep: they allow a lot of functionality to be accessed through a simple interface.
It is no simpler to think about the interface than to think about the full implementation.
Red Flag: Shallow Module
Small modules tend to be shallow.
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 accumulate to create tremendous complexity at the system level. Small classes also result in a verbose programming style, due to the boilerplate required for each class.
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. This maximizes the amount of complexity that is concealed.
“How can I reorganize these classes so that this particular piece of knowledge only affects a single class?”
If the affected classes are relatively small and closely tied to the leaked information, it may make sense to merge them into a single class. Another possible approach is to pull the information out of all of the affected classes and create a new class that encapsulates just that information. However, this approach will be effective only if you can find a simple interface that abstracts away from the details;
In temporal decomposition, the structure of a system corresponds to the time order in which operations will occur.
Red Flag: Information Leakage 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.
When designing modules, focus on the knowledge that’s needed to perform each task, not the order in which tasks occur.
Red Flag: Temporal Decomposition In temporal decomposition, execution order is reflected in the code structure: operations that happen at different times are in different methods or classes. If the same knowledge is used at different points in execution, it gets encoded in multiple places, resulting in information leakage.
rather than having separate methods for each of three steps of a computation, have a single method that performs the entire computation.
Whenever possible, classes should “do the right thing” without being explicitly asked.
Red Flag: Overexposure If the API for a commonly used feature forces users to learn about other features that are rarely used, this increases the cognitive load on users who don’t need the rarely used features.
(general-purpose APIs result in more information hiding).
When writing detailed code, one of the most effective ways to simplify the code is by eliminating special cases, so that the common-case code handles the edge cases as well.
In my experience, the sweet spot is to implement new modules in a somewhat general-purpose fashion. The phrase “somewhat general-purpose” means that the module’s functionality should reflect your current needs, but its interface should not.
One of the goals in class design is to allow each class to be developed independently, but the specialized approach tied the user interface and text classes together.
What is the simplest interface that will cover all my current needs? If you reduce the number of methods in an API without reducing its overall capabilities, then you are probably creating more general-purpose methods.
In how many situations will this method be used? If a method is designed for one particular use, such as the backspace method, that is a red flag that it may be too special-purpose.
Is this API easy to use for my current needs? This question can help you to determine when you have gone too far in making an API simple and general-purpose.

