Hacker News new | comments | ask | show | jobs | submit login
Book Review: A Philosophy of Software Design (pathsensitive.com)
349 points by dailymorn 3 months ago | hide | past | web | favorite | 50 comments



This book has been my own personal technical revolution over the past few months. I've been programming professionally for 7 years now, so while I'm not the most experienced I'm certainly no one new either. Some takeaways from how it changed my flow:

1. It gave me specific goals in terms of my own improvement, and the chapter titles did a good job of being a stand-in for challenging my own intuition.

2. In mentoring other developers, those I shared some of the lessons in the book with saw huge improvement literally week over week. I saw, personally, one of my mentees go from producing code that was hard for me to step through without bringing up half a dozen issues, to producing code where my largest criticisms were ecosystem dependent.

3. It created common language for discussing with teammates why certain code felt wrong.

4. It significantly reduced time spent refactoring later. The subtleties in some of the points make a re-read valuable as well. One of the better examples of this, was "Make code just a little generic"- often, we attempt to code things as generic as possible, and end up making what is effectively code soup that doesn't help accomplish the problem. Instead, building specifically for this problem, but trying to spot places it can be made just a little bit more generic, has altogether eliminated times I know I would've had to refactor again later if I hadn't been thinking of it.

5. The book knows what it isn't. He even says right in the forward and on the related Google Talk[1], that this first version is probably not the most value it can produce, but that hopefully by version 3 it'll be there.

For my time and energy, it's been more influential on how I develop software than anything else I've read. Furthermore, despite coding in primarily functional and/or dynamic languages, the examples used never made that a problem despite always being based on C. I recommend it highly.

1. https://www.youtube.com/watch?v=bmSAYlu0NcY


> 4. It significantly reduced time spent refactoring later. The subtleties in some of the points make a re-read valuable as well. One of the better examples of this, was "Make code just a little generic"- often, we attempt to code things as generic as possible, and end up making what is effectively code soup that doesn't help accomplish the problem. Instead, building specifically for this problem, but trying to spot places it can be made just a little bit more generic, has altogether eliminated times I know I would've had to refactor again later if I hadn't been thinking of it.

Wow, I've often felt this during my time coding. Unnecessary abstractions often just make things more convoluted and increases boilerplate when adding new things. Great to see this being addressed.


This principle is well known: 'YAGNI'.


You Ain't Gonna Need It is a great principle, but I think this is slightly different, as it implies it's useful to abstract it a little... So maybe JAIL?

Just Abstract It a Little


Agreed. JAIL is YAGNI/KISS with a little bit of sanity sprinkled on top of it. You generally want to keep code simple and straightforward, but that doesn't mean avoiding the most obvious and most probably useful generalizations.


Yeah, I've had this discussion with a lot of developers over the years with regards to DRY as well. I've seen people refactor code to fix "DRY" problems and the code ended up increasing in size due to the "deduplication" and increasing in complexity. When asked, "well do not repeat yourself isn't about reducing LoC".

That is technically correct, but is it then about increasing code size and complexity? Sometimes


Yes, exactly! I think people follow the principle blindly and try and pull out code that happens to be the same in two places. But if you anticipate that they might change in the future it's sort of a meaningless abstraction that doesn't give much benefit and just complicates the code.

I think it's important to realise that these principles are good in general, but you can have too much of a good thing, and there are trade-offs involved as you take them more and more to the extreme. I can't say I'm an expert so I may be wrong about this, but it seems like you really need to get a sense of what's needed in each scenario - how should this bit of code work and is this communicated effectively? I find this takes practise to gain a "feel" for, but then that feels less teachable than simple principles.


Wow, that is a compelling endorsement! Thanks for sharing w/ this leve of detail.


I want to pile on to the other commenters on this review of your takeaways and thank you. This detail and enumerating the specifics really provides a very clear (and relatively concise) picture of what benefits this book has in store for someone. Thanks!


Interesting. I'm in a similar boat as you (5 years of professional experience) and I had decided to pass on my co-workers' reading group for this book, but based on your review I'm now reconsidering!


I always wanted to write a "Philosophy of Software Design", except I wouldn't dictate HOW to write software. Instead I'd focus on what questions to ask and what trade-offs to expect. It would be Yoda-ish: Yoda teaches you how to ask questions and how to find answers, not the answers themselves.

In the end: there is NO right way, only trade-offs. At least know what the tradeoffs are. My book would be a trade-off recognition training guide.

I would state my experience, though. Such as "In my experience, typical shops are more comfortable with pattern X over pattern Y. I have no idea why: it's a psychology question I don't have the funds to answer."

And there'd be a chapter on how to recognize and scrutinize fads.


Write it! Start with a blog post series if "a book" is too great a hill to climb!


Seconded. Start with a series of blog posts to gauge interest, level of effort etc.


This is a nice idea, I've been looking for things like this to read. But my interest is more about generic systems, not that much about end-user software.

You are talking about "trade-offs" and I had a similar idea about "Values". With this values you would be able to derive the "Rules" (the HOW you mentioned) depending on the context and chosen trade-offs.

About it I think the 17 unix philosophy rules are very interesting. https://en.wikipedia.org/wiki/Unix_philosophy#Eric_Raymond%E... and I also started to write a blog about it https://github.com/JpOnline/Blog/blob/master/language.md


Most of the time the answer is: "Because a human has to read and understand it".


Indeed, software design is a lot like writing technical instructions. One should think in terms of writing for humans, not so much machines.

But another key dimension is dealing with changes in requirements over time. One cannot over-value their forecasting/guessing ability, and have to hedge the design to some degree. Some of the GOF design pattern writing assumed too much regularity in future changes, for example. I'd like to describe counter examples that show how assumptions of future regularity can byte one in the proverbial output port.


Are you familiar with "the art of unix programming" by ESR? It's in the same vein as what you describe. It even has an appendix with buddhist-like dialogues!


While there are some nice tidbits, end-user software tends to have a different profile of needs than infrastructure software or middle-ware. I guess what I'm saying is that I'd focus more on end-user software in such a book.

That's not necessarily the same as UI design, but for example, less about generic API design to be used by a million other developers, and more about in-shop API or stack design for say 20 developers.


I haven't finished the book and I am not sure I will. I was quite disappointed because from the title and description I was expecting something deep, that presented more questions than answers, navigated constraints, attempted to discuss the nuances of defining the problem of designing itself. On the contrary, it is a collection of superfluous recommendations for Object Oriented programmers presented without much justification.

The writer uses a very narrow perspective and just "assumes" all this can be generalized. One of the most recurring topics in the book is "complexity", but it is just introduced with a narrow one sentence tautological definition without offering any interesting discussion. All kinds of assumptions are taken for granted and not discussed.

So overall, the book reads so far like a bland collection of "do this, don't do that blog-posts". Papers like "Out of the tarpit", speakers like Rich Hickey, etc. offer much more interesting discussions on the topic of design and complexity, and are definitely more argumentative and "philosophical".

I even feel bad for writing something in such a negative vibe about a book. But really its pretentious title lead to such disappointment...


Out of the tarpit. Moseley & Marks 2006

http://shaffner.us/cs/papers/tarpit.pdf


I've read his book cover to cover and have enjoyed and learnt from a number of the concepts he discusses.

That being said, after recently reading "Out of the tarpit" and being a long time Rich Hickey fan, I completely agree that the model of complexity presented by the book feels simplistic.

I can't quite put my finger on why though. Maybe cause complexity in software has a lot more to do with understanding the problem domain rather than just the structure of the code. IMO it was worth the read, but I was slightly disappointed and it was without a doubt not a be all end all reference. (And it would be unfair to expect it to be.)


I feel like this review is making an important point, but the point it's making is talking past the point Ousterhout is making, and so this isn't much of a rebuttal at all.

The idea behind "deep modules" could be expressed as learning an interface should save you work, not cost you work. There's a couple of deep insights in that. One is that every new interface you create imposes a cost on the programmer who is reading the code, and so you should be sparing in them. New functions and new classes are not free; each time you add one, it makes it that much harder for the maintenance programmer or library user to grok the API as a whole.

The other big insight is in embedding complexity in the code. This was a perspective shift I learned from talking with Ben Gomes (my boss's boss at the time, and now head of Search at Google) while complaining about how messy some of Google's code was. His argument was that the code is complex because the problem is complex. By embedding the complexity into the code, you let the computer deal with it on every search request instead of letting the user deal with it on every search request. The number of times a programmer will look at the messy code probably numbers in the single digits, while the number of searches that code gets per day numbers in the billions, so by making things a little better for the user at the cost of it being much worse for the programmer, you are saving millions of hours in mental tax for humanity.

As programmers, we all have a drive toward simplicity, but it's worth remembering that we exist within an economic reality too. If you want people to use your product, your product has to do something they don't want to do themselves, and if it's easy for them to whip up a quick tool that solves their problem, your product is doomed in the marketplace.

The point the review is making seems to much more about protocols, and the ability for interfaces to serve as a firewall that lets different implementations interoperate. If you don't specify that interface precisely, you get abstraction leakage, where an interface that seems simple at first actually can become very complicated.

This is a valid and interesting point too, but it's worth remembering that the vast majority of interfaces have only a single implementation. It's not necessary to specify that interface in precise detail as long as the user of that code has a reasonably intuitive view of it. They can always go look up the details as necessary, when the strange behavior occurs, while the "deep module" saves them a significant amount of work as long as they stick to the happy path.


Oh, thank you for posting that.

Your point about protocols and multiple implementations nailed down the reason I was frowning after reading that review.


> The number of times a programmer will look at the messy code probably numbers in the single digits, while the number of searches that code gets per day numbers in the billions, so by making things a little better for the user at the cost of it being much worse for the programmer, you are saving millions of hours in mental tax for humanity.

Thank you. I'm going to quote it, and keep quoting it, at the "developer time is more important than machine time" crowd - the one that then goes and externalizes all this "machine time", making it "user's time (and electricity)".


I think it's great but it shouldn't be quoted without the part about the complexity of the problem. Because often you have simple problems solved in a complex manner that also lead to slow/buggy code that angers the user.


By far the most interesting part of this is the contention:

interfaces should be simpler than their implementations

Which the author then goes to some lengths to absolutely reject.

I find this very interesting, because 1) this is the view I’ve always held and 2) I’m having trouble accepting the authors proposition than an interface and a formal specification are equivalent.

...if they are, certainly, the non-code ‘level 3 artifacts’ that define code behaviour but are not code, will be much more than the implementation in some cases.

However, I dispute more strongly that the principal is wrong.

Surely not every implementation is longer than its interface, but in general striving to avoid bloated hideous interfaces is ideal.

Certainly it’s a sliding scale... and the idea of ‘too simple’ an interface that is superficially simple seeming, but actually very complex (eg. the file I/O example) is food for thought.

...but I feel too much is made of rejecting the point that all interfaces should be simpler than their implementation absolutely.

Did the book really claim that?

Hm.

I want to actually read it myself now.


I agree with you that the review seems to have misunderstood the author on this point. I wrote a comment on the blog to explain more. I think the review mixes up the words abstraction and specification, especially once I dug a little bit into the correspondence on the draft he posted for the book author's feedback.


The reviewer seems to say that he has a higher level of programming mastery than the author of the book.

Thats where I could not listen to him anymore. I would entertain the idea that theoretically programming languages that have features like contracts or specifications could be more robust or effective in some ways or for some applications. And actually I am interested in seeing examples of feature rich applications written in languages with these types of features.

But I am not convinced of the practicality of these languages or the superiority of the reviewer. The proof is in the pudding. I would challenge the reviewer who has clearly placed their knowledge and skills above the creator of Tcl/Tk and RAMCloud to show the useful projects he has created that prove this superiority.


I like to be open minded but he does seem to have spent most of his time in academia, and not a lot of time spent on commercial software. So I wonder if its genius or hubris it can be hard to tell the difference.


JO (John Ousterhout) is so thoughtful and lucid.

My experience with him is firstly through Tcl, which I like more than he does now (time for a TIP: "-jo" switch to [unset] which removes complaint to unset non-existent variables)[0], and his book on it[1], and various papers. His work is at once both powerful and humble. Note in [0] how he without much emotion he suggests how Tcl came to be less prominent, and how even at Tcl's height, he explained without ego why it was there, and how it would leave[2]. His papers on threads[3] and comments on performance and testing[4] are also lovely reads. I encourage any developer or architect to read or re-read them.

[0] https://www.youtube.com/watch?v=bmSAYlu0NcY (thx `quaunaut)

[1] https://www.amazon.ca/Tcl-Tk-Toolkit-John-Ousterhout/dp/0201...

[2] https://vanderburg.org/old_pages/Tcl/war/0009.html (This is The Law, and it is good.)

[3] https://web.stanford.edu/~ouster/cgi-bin/papers/threads.pdf

[4] https://web.stanford.edu/~ouster/cgi-bin/sayings.php


I'm going to disagree witha lot of this review. Here's an example:

"In other words, take a conditional out of the spec for the function. But in Section 10.5, he tells us that we should add a conditional to the spec of a function, namely in making Java’s substring method defined for out-of-bounds indices. I’m not completely sure he’s wrong (as I discuss in my Strange Loop talk, it comes down to: is there a clean way to describe this behavior?), but I find his claims that this makes the code “simpler” only slightly more credible than his claims about the Unix file API."

Ousterhout contrasts that with the similar operation in Python. Substring throws exceptions if the indices are out of range, Python returns the part of the string that is in-range. For example, calling it witha start, end pair where start > end results in an empty string, not an exception; it's much easier to use in my experience. And I doubt the formal specification is any larger, to address the author's point.

The formal does of Unix I/O is a horror show, sure, but what does the spec of it plus all of the filesystem that can back it look like?


This sounds like the "limp versus die" design decision: http://wiki.c2.com/?LimpVersusDie

The "right" answer depends on the domain. For a web startup, you may want to live with occasional errors being ignored in order to get your product up quickly and grow market share. Early Amazon.com used to fudge up my orders, but such didn't end the company.

But a bank probably doesn't want the software to "guess" if there is a potential numerical problem. It would rather have the batch process "crash". Otherwise, it could generate thousands of bad transactions and get sued to Pluto.

People used to debate this with MySql's default "truncation" setting. MySql was popular with start-ups because you could get it up and change the tables quickly without dealing with persnickity details, living with stuff occasionally falling thru the cracks.


The difference is between total and partial functions. A total function can have well-defined results (thus, no guessing) and is much easier to use than a function that unnecessarily fails.


The greatest thing about this book is that it develops an antidote to the work of the gang of three (Martin, Fowler, Beck), which would have everybody write only 3 line functions TDD style if they could.

This can be seen for example in the intro chapter to Fowler's 2nd edition of Refactoring, where he pulverizes some code into submission.


That has been exactly my sentiment after reading the book (although I'm only anti-Uncle Bob, not against Fowler).


I'm enjoying Ousterhaut's "A Philosophy of Software Design", but it is striking how much it is shaped and limited by the languages that are implicitly considered (mostly, imperative OO languages).

One oddity is the view of the class as the only real scope at which one can build abstractions. The idea that one can use little abstractions to build big abstractions, without cluttering the final abstraction to be presented to the user, is nowhere in sight.

Most shockingly to me, I've found no reference to types as a design tool. Sure, precise names help you avoid confusing physical block ids and logical ones. But wouldn't a type-based distinction be way better?

- https://twitter.com/yminsky/status/1005066579929911296


Good point about using types. I should have mentioned that as a way to eliminate the block number problem in Section 14.1; I'll fix that in a future revision of the book.


That'll be a great addition to the book.

Can I also recommend Yaron Minsky's talk on OCaml, specifically the part where he talks about "making invalid states impossible": https://youtu.be/-J8YyfrSwTk?t=1079

I've found this to be the single most valuable programming technique I've learnt.

Richard Feldman dives deeper into the topic with a lot more examples in https://youtu.be/IcgmSRJHu_8?t=73


I honestly think it's just too esoteric to ever be considered "mainstream" simple.


Meta 1: I enjoyed his review. It got me interested in learning more about all these concepts I've never heard about: "Level 3 Software? What's that?". It is not clear to me if a lot of this terminology is of his creation or if it comes from literature.

Meta 2: What better way to establish yourself as an expert than disagreeing with a currently recognized one? Not saying he did not back up his claims. In fact, I want to read more of his materials now to check that up.

Meta 3: This reading also gets me excited about investigating model driven development more. In this post, he talks a bit about the value of strict types related to such modeling, but lately I've been reading and writing some Clojure code and I'm slightly swinging back to more dynamic typing as a more flexible way of writing modular systems.


Level 1, level 2 and level 3 are the authors conceptions. Another post of his details what he means... http://www.pathsensitive.com/2018/01/the-three-levels-of-sof...


What are some of the best design books you people would recommend for a junior developer?


This one. I also read Code Complete (2nd ed) when I was a junior and found it very helpful, but I don't know how well it has aged.

Object Design by Wirfs-Brock is a practical OOP book.

I would avoid anything with clean code in its name, they're generally dogmatic and obsessive about TDD.


How to Design Programs https://htdp.org/


> Thanks John Ousterhout for his extensive comments on this review

That's a bit meta, eh? The author of the book being reviewed reviewed a review of his book.


I'm a recent college grad from a good CS program. How do I get better at bugfixing and diagnosing code? To give a specific example: I was recently working with a Tensorflow code base for image recognition, and Tensorflow kept hanging (no error, just hanging) or giving me shape errors. I felt stuck. First, the lack of error made it hard to diagnose, and when I did get the shape error, I didn't understand why quickly. I find myself too dependent on stackoverflow for bugfixing and if someone hasn't solved it, I feel helpless. I've tried reading through the Tensorflow source but it's pretty verbose and hard to understand. Are there any books that give you concrete methods to really understand other people's code well and fix bugs?


In my understanding, to begin learning diagnosing issues in code you need to understand your stack very well. Try to invest time explicitly learning bits and pieces of how your platform works, in detail.

Once you learn few different platforms and spend some time diagnosing problems you will start recognizing recurring patterns in problems. Hopefully this will gradually become your ability to diagnose difficult issues on unfamiliar platforms.


Pair with a strong senior developer, and observe as they troubleshoot. Learn troubleshooting and debugging tools like gdb, ldd, nm, strace for Linux, or SysInternals & Visual Studio debugger for Windows. Troubleshooting, debugging and diagnostics are skills in themselves that will stand you in good stead going forward.


Juniors I know all need to focus on the error message: read it, understand it, and then apply the scientific method : formulate one hypothesis, test it, analyse results, rince and repeat. Each experiment should other a path to what's next.

They are also forbidden to stay stuck more than one hour without requesting feedback(actually that's a rule for everyone on my team).

Pairing with seniors is a great way to learn too, you'll discover tons about what makes your company's code special.

You can block stackoverflow.com, or at the very least avoid searching on it before having tried to __understand__, not solve, the issue.


I've been wanting to read this book but there's not a digital version yet. I wonder if it's to avoid pirate copies everywhere.




Applications are open for YC Summer 2019

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: