Book Review: A Philosophy of Software Design (2020)
johz.bearblog.dev
johz.bearblog.dev
Depending on your background you're either going to love it or hate it. It's not to say what he's done is wrong or right, just your particular experience is going to colour if you agree with it or not.
For those that love it, if you integrate these principles and get your team to buy in, you're going to have a better software engineering experience and a good source of information to point to back to back up the way you think.
For me, these sort of books represent a maturing of the industry and recognising that software engineering is an abstract team activity that having the right shared principles and shared mental models is the difference between a joyful experience on a team or not.
Shameless plug: I'm also working on community project that captures the best principles in software engineering on https://principles.dev - It's a labour of love, but if you like what I've written above, please contact me. I'd love to get some feedback.
It made me more open to reading the book, knowing I wasn't being sold dogma, and gave me room to critically analyze the author's perspective, compared to my own background.
If the best thing you can say is, the author was humble...
1. Modules should be deep. It is better to have fewer deep modules than more shallow modules
2. The increments of development should be abstractions, not features. When you need an abstraction, invest time and design it clearly
There are many good advices, but on my opinion the book is not deep enough to have this promising title. Also, it's a crime to spend 30 pages of 170 on how to write comments.
3/5
* good software needs hardly any comments: small methods with good naming facilitate readability
* unit tests are superb documentary as every dev can see how it is used and how it works at runtime.
* meaningful documentation is hardly ever provided in source code: purpose of abstraction layers and modules, how one class/module relates to another and so on (design and architecture).
Viewed from the other end: commit messages are essentially comments over changesets. If you write those well, you can use the same approach to write good comments for your types, functions and modules.
See also https://news.ycombinator.com/item?id=27009308 on what I consider to be good style of commit messages (scale up or down, depending on the size of your commits).
They key is you want to be able to do something like that without losing crucial information. So anything that absolutely has to be there to understand what code is doing should be directly embedded in the code, that is, it needs to be a comment, not a commit message.
Documenting in comments is one way to achieve this, and it also brings two other benefits:
- High locality - you're likely to spot the documentation as you read the code it pertains to, because the comments are right there, mixed with the code.
- IDE support - interface-level comments are often automatically displayed in autocomplete and hover popups, so you can read them as you browse through suggestions and highlight interesting code fragments.
In my experience, having the documentation in the source code has very little effect on whether it gets stale. Unless you make checking for documentation changes an explicit step in the code review, documentation is going to get stale no matter where it is. And if you do have such a step in code review, then it's relatively immaterial whether the documentation is in a wiki or in formatted comments in the source.
> High locality - you're likely to spot the documentation as you read the code it pertains to, because the comments are right there, mixed with the code.
That actually brings to mind one of my complaints about relying on documentation that's inline with the source code. It's often too local. I can usually read a function and figure out what it's doing. Occasionally, when a function is doing something strange or counterintuitive, some documentation can be helpful, and I definitely acknowledge there's a role for comment-based documentation there.
More often, though, I don't want documentation to tell me what this or that function does, I want documentation to show me the big picture. What are all the components of this system? How do they communicate? How does user input propagate? Where does validation occur? These questions are almost never answered by comment-based documentation because of the locality principle that you cite.
In addition, the answers to these sorts of questions aren't likely to go out of date. After all, it's not like you're completely rearchitecting how validation works every week (and if you are, documentation is the least of your worries). These sorts of high-level questions are best answered on a wiki or some other tool that supports things like diagrams and well formatted prose text.
Yes, in an ideal world, we'd have both. Inline documentation which documents the design at a "micro" level and a wiki or some other knowledge-base which documents the design at a "macro" level. But we don't live in an ideal world. We live in a world where developers are pressed for time, and documentation is most often written after the fact. In this world, I would much rather have the wiki than the inline docs. I can, with a little bit of effort, figure out what each individual function is doing. It's the high-level "how it all fits together" design where I require assistance.
or perhaps I should name them "demo cases" or the like. Currently I am deeply sceptical about formats different from source code. "demo cases" require the dev to update them when they fail where as docs tend to get stale- (nearly) always.
Those unit tests have to be derived from somewhere, though, right? Hopefully, there are requirements docs -- but often those are too high-level, or omit details that were discovered during implementation, and in those cases I'm very appreciative of comments describing intent and purpose of a code snippet or function. Of course, like all things, comments rot without active maintenance and refactoring.
In a perfect world, code comments would be redundant, but they do provide a very low-friction way of documenting interesting bits while writing the code, while it takes a lot more effort to back-port information into requirements documentation.
Unit tests tell me how the code is supposed to behave given certain inputs, but without a statement of what the function is trying to accomplish I can't tell if any necessary unit tests are missing. Or if any are just accidentally working.
But (hopefully) most of the time good naming, parameters and return types and the context of the library does that.
But you are right, esp. complex methods or APIs require lots of documentation- but tbh I doubt it is in sources...
Care to elaborate what this means in practice?
For example, you build a Todo-list software (everybody does). You have requests from customers "I want to be notified 3 days before due date", "I want to be notified 1 day before due date" and "I don't need notifications".
OK, so you are adding a new setting "[ON/OFF] Notify me [X] days before due date".
Then you get feedback "I want to be notified when someone unassigned me" and "I want to be notified when someone assigns me".
OK. You're adding new setting "[ON/OF] Notify me about changes of my assignments"
Then you receive feedback like "I want to be notified about important tasks assigned to me only".
You say "Fuck it" and implement a notification engine where every user can set up own notification rules.
X notifications settings were collapsed into a new more abstract (but more complex) solution. You have to choose abstractions carefully and be aware that premature abstractization is as bad as premature optimization. This is hard.
Thank you!
Now I didn't read the book but I immediately thought of this: If we want to provide a feature, we should think of how our abstractions accommodate the feature. Are the assumptions or the interface of the abstractions in line with the functionality, or guarantees of the feature?
A good example of this would be HTTP REST. The interface is general and has clear, well defined semantics. It is a good abstraction for the web, since every request/response cycle has a clear way of reaching possible states and resources. It accommodates new features very well if they can be encoded in terms of HTTP verbs, hypertext representation and request/response cycles.
Thank you for so perfectly stating a concept I have been struggling with this week. I've been messing around with trying to build a Cocoa-bindings-ish ArrayController for the browser. I was trying to build it in an incremental way feature by feature and kept running into walls. The realization I eventually came to was that I needed to look at it as a larger system and design the whole thing rather than try and incrementally code it feature by feature. Stepping back and designing the whole abstraction was how I ended up moving forward.
I strongly disagree. Software engineering will be greatly improved, in my view, when comments are viewed with the same rigor as code. In code reviews, we've finally gotten to the point where we don't generally approve code unless there are unit tests to accompany it. Codebases will be even stronger when PRs and code reviews require inclusion of updates to the corresponding comments and docs and are examined with the same care and attention.
Important problems disappear when comments are kept in sync with the code: 1) technical debt goes down, 2) it's much easier for new devs to get on board with the code base, 3) refactoring is facilitated.
The problem is that everyone has different ideas about comments should be. Many devs prefer a minimal approach, others a very talkative approach, etc. This book is the first to lay out a systematic approach to commenting with an explanation for the preferences the author espouses. Nobody else does this to my knowledge.
Those 35 pages are gold. And if all you take from the book is a deepened appreciation for comments and a systemic approach to commenting, I contend you will have greatly benefited.
Book Review: A Philosophy of Software Design (2018) - https://news.ycombinator.com/item?id=26624057 - March 2021 (1 comment)
Book Review: A Philosophy of Software Design - https://news.ycombinator.com/item?id=18331219 - Oct 2018 (51 comments)
Notes on “A Philosophy of Software Design” - https://news.ycombinator.com/item?id=17906662 - Sept 2018 (32 comments)
[1]: https://freshman.tech/philosophy-of-software-design-summary/
I'd say half the book contained good advice, the other half was mediocre or dubious at best.
I'm curious to hear what others think who've read both books.
+ thoughts on complexity and how constant addition of new features adds to complexity + deep vs. shallow modules but at the same time...
- "..classitis": author criticizes the use of many small classes and functions/methods while I think form my experience SOLID principles are there for a reason- every method/class should have one purpose only.
- which leads me straight to my second point of critique so far: nomenclature. for several ideas exist established names already that are not used in the book.
I don't understand how you can be so confident that SOLID means you should have many small classes and functions/methods. The question of when we should carve off a piece of reality (natural or artificial) and call it one thing, or say that it does one thing, is an ancient philosophical question with no single right or easy answer.
I think sometimes people refuse to go past the words naming a practice or past the tldr; and it causes problems.
I would have liked to see Ousterhout make a more thorough argument if he was going to criticise TDD. His central criticism — that TDD results in what he calls tactical programming, prioritising the implementation of specific features over good design — is certainly defensible. However, I think he was too superficial in what he actually wrote on the TDD section, and consequently I don’t think he made a particularly convincing connection with the ideas developed earlier in the book.
I think you’re slightly unfairly misrepresenting his position on large or small classes. He makes a solid case that what he calls deep modules are better for managing complexity than shallow ones. He also identifies a correlation with size because small modules tend to be shallow. That’s not the same as arguing for large classes or against small classes just because of their size, though.
Instead, Ousterhout recommends designing the interface for the abstraction you're building before you start writing a test harness for it, and I can't agree enough with that statement.
If you write a good interface, testing it will be easy. If testing the interface isn't easy, then you have a bad interface. (This is relative to the complexity of the code invovled, obviously)
IMO the same caveat applies to Clean Architecture: it is study material to architects, rather than something you can copy-paste into a new project. The reason it's dauting, IMO, is because there are some unnecessary concepts there that are unrelated to the "grand idea", and those small things might make sense for Bob Martin but might not make sense to you.
If you want to understand it, I really like this article. I think it explains very well the "grand idea" of architectural templates like Clean/Hexagonal/Onion... and links it to Gary Berhnardt's Imperative-shell-functional-core: https://danuker.go.ro/the-grand-unified-theory-of-software-a...
Of course I also recommend Imperative-shell-functional-core itself: https://www.destroyallsoftware.com/talks/boundaries
Ousterhout is the inventor of one the most important unsung principle of software development: the Ousterhout's Dichotomy.
"Ousterhout's Dichotomy, proposed by John Ousterhout as a software development paradigm, asserts that the world of application development is divided into two languages: a compiled language focusing on disk access and efficiency such as C, and a higher-level, typeless or loosely-typed scripting language with a simple, basic syntax."
https://wiki.tcl-lang.org/page/Ousterhout%27s+Dichotomy
All your pandas, numpy, R belong to his
I think saying a book is "for enterprise" is a way to write them off. "I'm not working for big corp, I don't have to do that!" All coding principles are tools in your arsenal, not always appropriate but good to know. I'll grab this book on that principal.
I was expecting something philosophical, this is, something that actually connect the philosophy and software design disciplines. Outside of the title however, there is no philosophy in this book. Just general typical practical "good practice" tips of the kind you find in blog posts, without much empirical nor philosophical discussion for them.
Even if he did try to shoehorn a few bits of metaphysics in, most of the book would've had to lean more on the practical side of the domain.
It is an overused word.