Note: all page numbers below are for the Kindle version of the book.
This book was absolutely painful at the start. The 20th anniversary edition (1995) hasn't updated any of the references (obviously) because it's a retrospective about what the author has learned in developing a large software project. Unfortunately for those of us living in the 21st Century, that software project was the development of IBM's OS/360. Get ready for exciting discussions of microfiche, "map lights" in cars, and more. "Structured programming" is introduced as "another important set of new ideas" (146). I took notes while reading this book and at one point, I wrote:
basically unreadable due to aging poorly
Here are a few quotes to illustrate my point.
"If a separate machine is needed, it is a rather peculiar thing--it need not be fast, but it needs at least a million bytes of main storage, a hundred million bytes of on-line disk, and terminals. Only alphanumeric terminals are needed, but they must go much faster than the 15 characters per second that characterizes typewriters." (132)
"What high-level language should one use for system programming? The only reasonable candidate today is PL/I." (138)
"I am convinced that interactive systems will never displace batch systems for many applications." (139)
One particularly egregious example is where the author recommends adding comments to the source code of a program, but not too much, because it could significantly increase the size of the program on the disk (177). (Which I guess is technically true but no longer an issue on modern computers.) The author also recommends using arrows instead of words to illustrate program flow:
I can hardly imagine a single piece of advice which would make a program less readable and more cluttered. So not everything in this book has aged well.
...that being said, once you've struggled through the first few chapters, the book really does have some good advice. I'll lay out the most useful themes I found here.
"A basic principle of data processing teaches the folly of trying to maintain independent files in synchronism" (171). The author recommends against separating documentation and code, instead promoting the idea that the source code file itself should contain prose explanation of that code. What he calls "self-documenting programs" we would today call "well-commented code". Make sure you explain any code which isn't self-explanatory.
"As a principal objective, we must attempt to minimize the burden of documentation, the burder neither we nor our predecessors have been able to bear successfully." (172)
To that end, the author recommends that we "use the parts of the program that have to be there anyway": use whitespace and brackets to format code, clarify scope, and so on. Give your variables, methods, and classes sensible, self-explanatory names. "Show, don't tell" makes reading -- and writing -- a much less painful process.
Don't reinvent the wheel. "Refer to standard literature to document basic algorithms whenever possible" (176), rather than "rolling your own". If you have to extract a single algorithm from a large package to avoid importing the entire package, do so. Just refer to the original source so you can find it later if necessary.
Don't be afraid to be too verbose. If a bit of code is unclear, write a single-line comment. If a section or algorithm needs explanation, write a paragraph: "use line comments or remark anything that is not obvious" (176). It is far better to be too explicit than to be too vague. Your future self, returning to those comments, will be thankful.
One of the central theses of this book is that, once all of the incidental challenges of programming have been overcome or abstracted away with automatic memory and file management, expressive high-level languages, widely-available algorithms and data structures, and so on, what challenges are left for the programmer? What's left is simply expressing the problem to be solved in such a way that it can be interpreted by a machine. In other words:
"Representation is the essence of programming." (110)
In 2019, the most difficult part of programming is not learning syntax or managing resources or anything low-level, really. It's being able to completely and unambiguously express the problem you wish to solve, such that a machine can help you to arrive at a solution. Sure, collections and classes and lambdas may help you express the problem, maybe in a less error-prone or more comprehensive way, but they will not do your work for you. They are only tools. It is still up to you, as the programmer, to use those tools to create.
"We can write good or bad programs with any tool. Unless we teach people how to design, the languages matter very little." (221)
I had a teacher in high school who always used to say "repetition is your friend". What she meant was that humans need to see concepts and ideas repeated, sometimes in different ways, in order to fully understand and absorb them. Machines don't need this. If you write a function or a class definition once, you don't need to write it again.
"Software entities are more complex for their size than perhaps any other human construct, because no two parts are alike (at least above the statement level). If they are, we make the two similar parts into one, a subroutine..." (183)
The DRY (Don't Repeat Yourself) mantra espoused by many software developers promotes the idea that regularly-used chunks of code should be encapsulated in methods which are easily called, debugged, and documented, rather than copying and pasting those lines over and over throughout your code. Code organised in this way will never repeat anything larger than a statement, or a single line. Information theory says that this kind of code is maximally entropic. By repeating as little code as possible, we maximise the "information density" of that code.
This means that "good" code is, by definition, as complex as it can possibly be. I use the word "complex" here not to mean "confusing" or "obfuscated", or to mean that the code has a difficult-to-understand syntax. I mean that not a single chunk of code is repeated, everything is unique, everything carries new and different information that what was presented before. Crafting good software necessarily means crafting complex software:
"...a scaling-up of a software entity is not merely a repetition of the same elements in larger size; it is necessarily an increase in the number of different elements. In most cases, the elements interact with each other in some nonlinear fashion, and the complexity of the whole increases much more than linearly." (184)
Achieving a significant increase in programmer efficiency, then, is a matter of making it easier to express complex relationships and objects in fewer lines of code (272). Not better or faster editors, not new syntax or frameworks or languages per se, but the ability to express higher-level concepts in those frameworks and languages in an easier, less error-prone way. That is what will increase programmer efficiency in the future.
Software projects, like teams of people, increase supralinearly in complexity as they increase in size. Doubling the size of a program increases by a factor much greater than 2 the number of ways in which that API can be used, abused, and connected to other packages. Similarly, a team of 4 people has 9 possible communications channels among its members (unique sub-teams of 2 or more people). A team of 8 has 49. A team of 16, 225. Complexity increases quadratically.
When a project is built by a single person, maintaining conceptual integrity is relatively easy. Only one person needs to make sure the code is formatted uniformly, documentation uses the same verbiage, the UI is consistent, and so on. When a project team is as small as two people, communication is imperative to maintain this consistency across the project. So communication among teams is essential to maintaining conceptual integrity.
Early in the book, the author recommends a radical re-design of a development team, for projects with only a handful of people -- the surgeon's team. In this project structure, only one person is responsible for the conceptual vision of the project. This person, "the surgeon", does the structural and design work, while another skilled programmer helps by building secondary bits of code: lower-level subroutines, backend data management frameworks, and so on. The surgeon is free to focus on her "vision" for the project, while others support and aid the surgeon.
Of couse, this won't work for most teams (in spite of the author's recommendation), especially large ones, where one "visionary" would not be able to handle all the work required of them. So as much as possible, design decisions should be documented and explained, so that everyone involved in the project can refer to them. As I said earlier, it's always better to be overly explicit than overly vague.
"Preparing each document of this small set focuses thought and crystallizes discussion. The act of writing requires hundreds of mini-decisions, and it is the existence of these that distinguish clear, exact policies from fuzzy ones." (240)
But it is not necessary for everyone to know everything about the project. In fact, it's preferable if the Law of Demeter (or, "principle of least knowledge") is followed as closely as possible:
"...the goal of everyone seeing everything is totally wrong; parts should be encapsulated so that no one needs to or is allowed to see the internals of any parts other than his own, but should only see the interfaces." (236)
In other words, you don't need to know how a subroutine works, just that it does. You should focus on the inputs to and outputs from a function, not what it does internally. This increases reusability and modularity of code.
Although the traditional "waterfall" software development method is assumed throughout the book, the author acknowledges this in the 20th Anniversary edition, and more or less apologizes for it (265). Times change, and software development has evolved probably more drastically than any other industry in the history of civilization, all in less than 80 years.
With the benefit of hindsight, the author now recommends a more Agile-like framework: build an end-to-end system that does nothing first -- it only makes the correct subroutine calls in the correct order. Then, build out the subroutines one at a time, test, rinse, repeat. In this way, you will always have a working system (albeit with limited functionality) which can constantly be tested by users (202).
"The morale effects are startling. Enthusiasm jumps when there is a running system, even a simple one." (202)
In addition to "growing" software, we should also grow great software developers. Few companies nowadays are in the habit of putting in the effort to start with brand-new developers and growing them into great, experienced ones. The Mythical Man-Month offers a few tips for this process:
"Systematically identify the top designers as early as possible. The best are often not the most experienced. Assign a career mentor to be responsible for the development of the prospect, and keep a careful career file. Devise and maintain a career development plan... including carefully selected apprenticeships with top designers, episodes of advanced formal education, and short courses, all interspersed with solo design and technical leadership assignments." (204)
As a manager, it is not so much your duty to develop top talent as it is your duty to allow it to develop on its own (276). Provide space, facilitate team meals and meetings, take care of all the mundane aspects of the work, so that your star developers can get on with the work of developing. But most importantly, avoid micromanagement. Allow employees to take control of their own projects, set their own deadlines, and manage their own time and resources as much as possible -- you'll be surprised at the results:
"I can't emphasize enough the importance of empowerment." (279)
I was extremely skeptical at first, but after struggling through the first 20% or so, the rest of this book was full of great advice. I don't recommend it for early-career developers, as it seems to be geared mainly toward team leaders. Tips on managing teams of people, maintaining development projects across those teams, and empowering employees to perform at their peak can be found on nearly every page of this book. But maybe skip the first few chapters.