- Tags:: 📚Books, Coding best practices
- Author:: John Ousterhout
- Liked:: 8
- Link:: Amazon.com: A Philosophy of Software Design, 2nd Edition: 9781732102217: Ousterhout, John: Libros
- Source date:: 2021-07-26
- Finished date:: 2022-10-17
- Cover::
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 .
19: Software Trends
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 .