Why did I want to read it?

Because it seems to be considered the modern “replacement” of the Clean Code book by Uncle Bob, which has not adapted to newer times in several dimensions.

What did I get out of it?

Preface

The overall goal is to reduce complexity; this is more important than any particular principle or idea you read here. (…) The most fundamental problem in computer science is problem decomposition: how to take a complex problem and divide it up into pieces that can be solved independently. Problem decomposition is the central design task that programmers face every day.

Sad state of affairs:

I have not been able to identify a single class in any university where problem decomposition is a central topic.

That is, modularity. He cites the paper of D. L. Parnas, On the criteria to be used in decomposing systems into modules | Communications of the ACM(1973), whose abstract says:

This paper discusses modularization as a mechanism for improving the flexibility and comprehensibility of a system while allowing the shortening of its development time.

Working Code Isn’t Enough (Strategic vs. Tactical Programming)

Tactical programming

Most programmers approach software development with a mindset I call tactical programming. In the tactical approach, your main focus is to get something working (…). At first glance this seems totally reasonable: what could be more important than writing code that works? However, tactical programming makes it nearly impossible to produce a good system design. The problem with tactical programming is that it is short-sighted. If you’re programming tactically, you’re trying to finish a task as quickly as possible. Perhaps you have a hard deadline. As a result, planning for the future isn’t a priority. You don’t spend much time looking for the best design; you just want to get something working soon. You tell yourself that it’s OK to add a bit of complexity or introduce a small kludge or two, if that allows the current task to be completed more quickly. This is how systems become complicated. As discussed in the previous chapter, complexity is incremental. It’s not one particular thing that makes a system complicated, but the accumulation of dozens or hundreds of small things. If you program tactically, each programming task will contribute a few of these complexities. Each of them probably seems like a reasonable compromise in order to finish the current task (p. 14)

Tactical tornados

A name for the type of developer I described in ✍️ Ask HN. How bad should the code be in a startup.!

Almost every software development organization has at least one developer who takes tactical programming to the extreme: a tactical tornado. The tactical tornado is a prolific programmer who pumps out code far faster than others but works in a totally tactical fashion (…) In some organizations, management treats tactical tornadoes as heroes. However, tactical tornadoes leave behind a wake of destruction. They are rarely considered heroes by the engineers who must work with their code in the future. Typically, other engineers must clean up the messes left behind by the tactical tornado, which makes it appear that those engineers (who are the real heroes) are making slower progress than the tactical tornado (p. 14)

Also has to do with the Theory of Constraints of đź“– Extreme Programming Explained:

The Theory of Constraints shares with other theories of organizational change the assumption that the whole organization is focused on overall throughput, not on micro-optimization. If everyone is trying to make sure his function is not seen as the constraint, no change will happen…

Link to original

Strategic programming

The most important thing is the long-term structure of the system. Most of the code in any system is written by extending the existing code base, so your most important job as a developer is to facilitate those future extensions. you should not think of “working code” as your primary goal, though of course your code must work. Your primary goal must be to produce a great design, which also happens to work. This is strategic programming.

Design-it-twice

Smart people and Refusing to stand on your own feet:

I have noticed that the design-it-twice principle is sometimes hard for really smart people to embrace. When they are growing up, smart people discover that their first quick idea about any problem is sufficient for a good grade; there is no need to consider a second or third possibility. This tends to result in bad work habits. However, as these people get older, they get promoted into environments with harder and harder problems. Eventually, everyone reaches a point where your first ideas are no longer good enough; if you want to get really great results, you have to consider a second possibility, or perhaps a third, no matter how smart you are. The design of large software. (p. 94)

Raw highlights

Preface

Highlight (blue) - Location 173

Highlight (blue) - Location 216

The overall goal is to reduce complexity ; this is more important than any particular principle or idea you read here .

1: Introduction

Highlight (blue) - Page 2 · Location 251

Unfortunately , the waterfall model rarely works well for software .

Highlight (blue) - Page 2 · Location 252

it isn’t possible to visualize the design for a large software system well enough to understand all of its implications before building anything .

Highlight (blue) - Page 2 · Location 255

developers try to patch around the problems without changing the overall design . This results in an explosion of complexity .

Highlight (blue) - Page 2 · Location 257

agile development ,

Highlight (blue) - Page 2 · Location 259

Each iteration exposes problems with the existing design , which are fixed before the next set of features is designed . By spreading out the design in this way , problems with the initial design can be fixed while the system is still small ;

2: The Nature of Complexity

Highlight (blue) - 2.1 Complexity defined > Page 6 · Location 326

Isolating complexity in a place where it will never be seen is almost as good as eliminating the complexity entirely .

Highlight (blue) - 2.2 Symptoms of complexity > Page 7 · Location 334

The first symptom of complexity is that a seemingly simple change requires code modifications in many different places .

Highlight (blue) - 2.2 Symptoms of complexity > Page 8 · Location 357

The third symptom of complexity is that it is not obvious which pieces of code must be modified to complete a task ,

3: Working Code Isn’t Enough

Highlight (blue) - Page 13 · Location 424

Many organizations encourage a tactical mindset , focused on getting features working as quickly as possible . However , if you want a good design , you must take a more strategic approach where you invest time to produce clean designs and fix problems .

Highlight (blue) - 3.1 Tactical programming > Page 14 · Location 446

Almost every software development organization has at least one developer who takes tactical programming to the extreme : a tactical tornado . The tactical tornado is a prolific programmer who pumps out code far faster than others but works in a totally tactical fashion . When it comes to implementing a quick feature , nobody gets it done faster than the tactical tornado . In some organizations , management treats tactical tornadoes as heroes . However , tactical tornadoes leave behind a wake of destruction . They are rarely considered heroes by the engineers who must work with their code in the future .

Highlight (blue) - 3.1 Tactical programming > Page 14 · Location 450

Typically , other engineers must clean up the messes left behind by the tactical tornado , which makes it appear that those engineers ( who are the real heroes ) are making slower progress than the tactical tornado .

Highlight (yellow) - 3.3 How much to invest? > Page 16 · Location 490

how long does it take before the strategic approach has paid for itself ?

Highlight (yellow) - 3.3 How much to invest? > Page 16 · Location 491

My personal opinion is that the time to payback is somewhere in the range of 6 – 18 months .

Highlight (yellow) - 3.4 Startups and investment > Page 17 · Location 496

In some environments there are strong forces working against the strategic approach .

Highlight (yellow) - 3.4 Startups and investment > Page 17 · Location 500

If you are in a company leaning in this direction , you should realize that once a code base turns to spaghetti , it is nearly impossible to fix . You will probably pay high development costs for the life of the product .

Highlight (blue) - 3.4 Startups and investment > Page 17 · Location 500

you should realize that once a code base turns to spaghetti , it is nearly impossible to fix .

Highlight (yellow) - 3.4 Startups and investment > Page 17 · Location 501

Furthermore , the payoff for good ( or bad ) design comes pretty quickly , so there’s a good chance that the tactical approach won’t even speed up your first product release .

Highlight (blue) - 3.4 Startups and investment > Page 17 · Location 502

the payoff for good ( or bad ) design comes pretty quickly ,

4: Modules Should Be Deep

It is very interesting to see

Highlight (yellow) - 4.1 Modular design > Page 20 · Location 549

A developer should not need to understand the implementations of modules other than the one he or she is working in .

Highlight (yellow) - 4.2 What’s in an interface? > Page 21 · Location 571

In general , if a developer needs to know a particular piece of information in order to use a module , then that information is part of the module’s interface .

Highlight (yellow) - 4.3 Abstractions > Page 23 · Location 600

The best modules are deep : they allow a lot of functionality to be accessed through a simple interface . A shallow module is one with a relatively complex interface , but not much functionality : it doesn’t hide much complexity .

Highlight (blue) - 4.4 Deep modules > Page 23 · Location 608

The cost of a module ( in terms of system complexity ) is its interface .

Highlight (blue) - 4.4 Deep modules > Page 23 · Location 612

five basic system calls for I / O , with

Highlight (blue) - 4.4 Deep modules > Page 24 · Location 626

A modern implementation of the Unix I / O interface requires hundreds of thousands of lines of code , which address complex issues such as :

Highlight (blue) - 4.4 Deep modules > Page 25 · Location 639

easy to use , yet they hide significant implementation complexity .

Highlight (blue) - 4.6 Classitis > Page 26 · Location 661

the value of deep classes is not widely appreciated today . The conventional wisdom in programming is that classes should be small , not deep .

Highlight (blue) - 4.6 Classitis > Page 26 · Location 667

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 .

Highlight (blue) - 4.7 Examples: Java and Unix I/O > Page 27 · Location 690

Providing choice is good , but interfaces should be designed to make the common case as simple as possible

5: Information Hiding (and Leakage)

Highlight (blue) - 5.2 Information leakage > Page 31 · Location 747

One of the best skills you can learn as a software designer is a high level of sensitivity to information leakage . If you encounter information leakage between classes , ask yourself “ How can I reorganize these classes so that this particular piece of knowledge only affects a single class ? ”

Highlight (blue) - 5.3 Temporal decomposition > Page 31 · Location 754

common cause of information leakage is a design style I call temporal decomposition .

Highlight (blue) - 5.3 Temporal decomposition > Page 32 · Location 756

With temporal decomposition , this application might be broken into three classes : one to read the file , another to perform the modifications , and a third to write out the new version . Both the file reading and file writing steps have knowledge about the file format , which results in information leakage . The solution is to combine the core mechanisms for reading and writing files into a single class .

Highlight (blue) - 5.3 Temporal decomposition > Page 32 · Location 767

focus on the knowledge that’s needed to perform each task , not the order in which tasks occur .

Highlight (blue) - 5.5 Example: too many classes > Page 34 · Location 799

information hiding can often be improved by making a class slightly larger .

Highlight (blue) - 5.5 Example: too many classes > Page 34 · Location 800

bring together all of the code related to a particular capability ( such as parsing an HTTP request ) ,

Highlight (blue) - 5.6 Example: HTTP parameter handling > Page 35 · Location 833

it’s important to avoid exposing internal data structures as much as possible .

Highlight (blue) - 5.7 Example: defaults in HTTP responses > Page 36 · Location 858

In the rare cases where a caller needs to override a default , it will have to know about the value , and it can invoke a special method to modify

Note - 5.7 Example: defaults in HTTP responses > Page 36 · Location 859

Instead of specifying a default in the interface

Highlight (blue) - 5.7 Example: defaults in HTTP responses > Page 36 · Location 862

The best features are the ones you get without even knowing they exist .

Highlight (blue) - 5.8 Information hiding within a class > Page 37 · Location 870

try to minimize the number of places where each instance variable is used .

Highlight (blue) - 5.10: Conclusion > Page 38 · Location 883

try not to be influenced by the order in which operations will occur at runtime ; that will lead you down the path of temporal decomposition , which will result in information leakage and shallow modules . Instead , think about the different pieces of knowledge that are needed to carry out the tasks of your application , and design each module to encapsulate one or a few of those pieces of knowledge .

6: General-Purpose Modules are Deeper

Highlight (blue) - Page 39 · Location 893

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 .

Highlight (blue) - Page 39 · Location 894

Conversely , code that is more general - purpose is simpler , cleaner , and easier to understand .

Highlight (blue) - 6.1 Make classes somewhat general-purpose > Page 40 · Location 917

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 . Instead , the interface should be general enough to support multiple uses .

Highlight (blue) - 6.1 Make classes somewhat general-purpose > Page 40 · Location 919

easy to use for today’s needs without being tied specifically to them .

Highlight (blue) - 6.6 Push specialization upwards (and downwards!) > Page 45 · Location 1014

specialized code should be cleanly separated from general - purpose code . This can be done by pushing the specialized code either up or down in the software stack .

7: Different Layer, Different Abstraction

Highlight (blue) - 7.3 Decorators > Page 55 · Location 1214

The motivation for decorators is to separate special - purpose extensions of a class from a more generic core . However , decorator classes tend to be shallow :

Highlight (blue) - 7.5 Pass-through variables > Page 57 · Location 1267

The solution I use most often is to introduce a context object as in

Highlight (blue) - 7.5 Pass-through variables > Page 59 · Location 1280

Unfortunately , the context will probably be needed in many places , so it can potentially become a pass - through variable . To reduce the number of methods that must be aware of it , a reference to the context can be saved in most of the system’s major objects .

Highlight (blue) - 7.5 Pass-through variables > Page 59 · Location 1284

the context is available everywhere , but it only appears as an explicit argument in constructors .

Highlight (blue) - 7.5 Pass-through variables > Page 59 · Location 1291

Without discipline , a context can turn into a huge grab - bag of data that creates nonobvious dependencies throughout the system .

Highlight (blue) - 7.5 Pass-through variables > Page 59 · Location 1293

Unfortunately , I haven’t found a better solution than contexts .

8: Pull Complexity Downwards

Highlight (blue) - Page 61 · Location 1309

it is more important for a module to have a simple interface than a simple implementation .

Highlight (blue) - 8.2 Example: configuration parameters > Page 63 · Location 1345

you should avoid configuration parameters as much as possible . Before exporting a configuration parameter , ask yourself : “ will users ( or higher - level modules ) be able to determine a better value than we can determine here ? ”

9: Better Together Or Better Apart?

Highlight (blue) - Page 65 · Location 1370

Some complexity comes just from the number of components :

Highlight (blue) - 9.7 Splitting and joining methods > Page 73 · Location 1514

suppose a method contains five 20 - line blocks of code that are executed in order . If the blocks are relatively independent , then the method can be read and understood one block at a time ; there’s not much benefit in moving each of the blocks into a separate method . If the blocks have complex interactions , it’s even more important to keep them together so readers can see all of the code at once ; if each block is in a separate method , readers will have to flip back and forth between these spread - out methods in order to understand how they work together .

Note - 9.7 Splitting and joining methods > Page 73 · Location 1518

But i guess it has value to summarize what the block is doing in a function

Highlight (blue) - 9.7 Splitting and joining methods > Page 74 · Location 1532

you make a split of this form and then find yourself flipping back and forth between the parent and child to understand how they work together , that is a red flag ( “ Conjoined Methods ” ) indicating that the split was probably a bad idea .

Highlight (blue) - 9.8 A different opinion: Clean Code > Page 76 · Location 1555

A different opinion : Clean Code

Highlight (blue) - 9.8 A different opinion: Clean Code > Page 76 · Location 1564

A more important issue is : does breaking up a function reduce the overall complexity of the system ? In

Highlight (blue) - 9.8 A different opinion: Clean Code > Page 76 · Location 1568

Don’t sacrifice depth for length .

10: Define Errors Out Of Existence

Highlight (blue) - Page 77 · Location 1578

reduce the number of places where exceptions must be handled ;

Highlight (blue) - 10.1 Why exceptions add complexity > Page 80 · Location 1642

recent study found that more than 90 % of catastrophic failures in distributed data - intensive systems were caused by incorrect error handling1 . When exception handling code fails , it’s difficult to debug the problem , since it occurs so infrequently .

Highlight (blue) - 10.2 Too many exceptions > Page 80 · Location 1662

The exceptions thrown by a class are part of its interface ;

11: Design it Twice

Highlight (blue) - Page 91 · Location 1861

Try to pick approaches that are radically different from each other ; you’ll learn more that way . Even if you are certain that there is only one reasonable approach , consider a second design anyway , no matter how bad you think it will be . It will be instructive to think about the weaknesses of that design and contrast them with the features of other designs .

Highlight (blue) - Page 92 · Location 1884

for the implementation , the most important things are simplicity and performance .

Highlight (blue) - Page 94 · Location 1890

I have noticed that the design - it - twice principle is sometimes hard for really smart people to embrace . When they are growing up , smart people discover that their first quick idea about any problem is sufficient for a good grade ; there is no need to consider a second or third possibility . This tends to result in bad work habits . However , as these people get older , they get promoted into environments with harder and harder problems . Eventually , everyone reaches a point where your first ideas are no longer good enough ; if you want to get really great results , you have to consider a second possibility , or perhaps a third , no matter how smart you are .

Highlight (blue) - Page 94 · Location 1895

The design of large software

Highlight (blue) - Page 94 · Location 1895

systems falls in this category : no - one is good enough to get it right with their first try .

12: Why Write Comments? The Four Excuses

Highlight (blue) - 12.1 Good code is self-documenting > Page 96 · Location 1926

there is still a significant amount of design information that can’t be represented in code . For example , only a small part of a class’s interface , such as the signatures of its methods , can be specified formally in the code . The informal aspects of an interface , such as a high - level description of what each method does or the meaning of its result , can only be described in comments . There are many other examples of things that can’t be described in the code , such as the rationale for a particular design decision , or the conditions under which it makes sense to call a particular method .

Highlight (blue) - 12.1 Good code is self-documenting > Page 96 · Location 1937

Moreover , comments are fundamental to abstractions . Recall from Chapter 4 that the goal of abstractions is to hide complexity : an abstraction is a simplified view of an entity , which preserves essential information but omits details that can safely be ignored . If users must read the code of a method in order to use it , then there is no abstraction : all of the complexity of the method is exposed .

Highlight (blue) - 12.6 A different opinion: comments are failures > Page 99 · Location 1992

In his book Clean Code , Robert Martin takes a more negative view of comments :

Highlight (blue) - 12.6 A different opinion: comments are failures > Page 100 · Location 2001

One of the purposes of comments is to make it it unnecessary to read the code :

Highlight (blue) - 12.6 A different opinion: comments are failures > Page 100 · Location 2004

Instead of writing a comment to explain what is happening in a block of code in a method , Martin suggests pulling that block out into a separate method ( with no comments ) and using the name of the method as a replacement for the comment . This results in long names such as isLeastRelevantMultipleOfNextLargerPrimeFactor .

Highlight (blue) - 12.6 A different opinion: comments are failures > Page 100 · Location 2007

And , with this approach , developers end up effectively retyping the documentation for a method every time they invoke it !

13: Comments Should Describe Things that Aren’t Obvious from the Code

Highlight (blue) - Page 101 · Location 2023

The idea of an abstraction is to provide a simple way of thinking about something , but code is so detailed that it can be hard to see the abstraction just from reading the code . Comments can provide a simpler , higher - level view ( “ after this method is invoked , network traffic will be limited to maxBandwidth bytes per second ” ) .

Highlight (blue) - Page 101 · Location 2025

Even if this information can be deduced by reading the code , we don’t want to force users of a module to do that : reading the code is time - consuming and forces them to consider a lot of information that isn’t needed to use the module .

Highlight (blue) - 13.1 Pick conventions > Page 103 · Location 2049

it is easier to comment everything rather than spend energy worrying about whether a comment is needed . Implementation

Highlight (blue) - 13.2 Don’t repeat the code > Page 105 · Location 2112

A first step towards writing good comments is to use different words in the comment from those in the name of the entity being described .

Highlight (blue) - 13.3 Lower-level comments add precision > Page 107 · Location 2168

The comment will be both shorter and more useful if it describes what the variable represents rather than mirroring the code structure :

Highlight (blue) - 13.4 Higher-level comments enhance intuition > Page 109 · Location 2240

it can be very helpful to describe the conditions under which the method is most likely to be invoked ( especially if the method is only invoked in unusual situations ) .

Highlight (blue) - 13.7 Cross-module design decisions > Page 117 · Location 2460

The biggest challenge with cross - module documentation is finding a place to put it where it will naturally be discovered by developers .

Highlight (blue) - 13.7 Cross-module design decisions > Page 118 · Location 2505

I have recently been experimenting with an approach where cross - module issues are documented in a central file called designNotes . The

Highlight (blue) - 13.7 Cross-module design decisions > Page 119 · Location 2519

Then , in any piece of code that relates to one of these issues there is a short comment referring to the designNotes file : / / See ” Zombies ” in designNotes .

14: Choosing Names

Highlight (blue) - 14.6 A different opinion: Go style guide > Page 130 · Location 2740

“ The greater the distance between a name’s declaration and its uses , the longer the name should be . ”

15: Write The Comments First

Highlight (blue) - Page 131 · Location 2755

The best time to write comments is at the beginning of the process , as you write the code .

Highlight (blue) - 15.3 Comments are a design tool > Page 133 · Location 2787

comments at the beginning is that it improves the system design . Comments provide the only way to fully capture abstractions , and good abstractions are fundamental to good system design .

Highlight (blue) - 15.3 Comments are a design tool > Page 133 · Location 2803

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 .

16: Modifying Existing Code

Highlight (blue) - 16.2 Maintaining comments: keep the comments near the code > Page 139 · Location 2866

The best way to ensure that comments get updated is to position them close to the code they describe ,

Highlight (blue) - 16.6 Higher-level comments are easier to maintain > Page 142 · Location 2930

comments are easier to maintain if they are higher - level and more abstract than the code .

18: Code Should be Obvious

Highlight (blue) - 18.2 Things that make code less obvious > Page 150 · Location 3093

Event - driven programming makes it hard to follow the flow of control .

Highlight (blue) - 19.1 Object-oriented programming and inheritance > Page 154 · Location 3187

If there is no viable alternative to implementation inheritance , try to separate the state managed by the parent class from that managed by subclasses . One way to do this is for certain instance variables to be managed entirely by methods in the parent class , with subclasses using them only in a read - only fashion or through other methods in the parent class . This applies the notion of information hiding within the class hierarchy to reduce dependencies .

Highlight (blue) - 19.2 Agile development > Page 156 · Location 3209

Developing incrementally is generally a good idea , but the increments of development should be abstractions , not features . It’s fine to put off all thoughts about a particular abstraction until it’s needed by a feature . Once you need the abstraction , invest the time to design it cleanly ;

Highlight (blue) - 19.4 Test-driven development > Page 157 · Location 3236

Although I am a strong advocate of unit testing , I am not a fan of test - driven development . The problem with test - driven development is that it focuses attention on getting specific features working , rather than finding the best design .

Highlight (blue) - 19.4 Test-driven development > Page 157 · Location 3240

As mentioned in Section 19.2 , the units of development should be abstractions , not features . Once you discover the need for an abstraction , don’t create the abstraction in pieces over time ; design it all at once ( or at least enough to provide a reasonably comprehensive set of core functions ) . This is more likely to produce a clean design whose pieces fit together well .

22: Conclusion

Highlight (blue) - Page 176 · Location 3516

The reward for being a good designer is that you get to spend a larger fraction of your time in the design phase , which is fun . Poor designers spend most of their time chasing bugs in complicated and brittle code .