December 18th, 2024

Ideas from "A Philosophy of Software Design"

The blog post highlights insights from "A Philosophy of Software Design" by John Ousterhout, focusing on reducing complexity, reconsidering component modularity, and simplifying exception handling for better software design.

Read original articleLink Icon
AgreementSkepticismCuriosity
Ideas from "A Philosophy of Software Design"

The blog post discusses key insights from "A Philosophy of Software Design" by John Ousterhout, focusing on three main ideas that can help software engineers improve their design practices. The first idea emphasizes a zero-tolerance approach to complexity, highlighting that complexity accumulates over time and can lead to significant issues such as change amplification and increased cognitive load. The author suggests centralizing duplicated logic to reduce complexity. The second idea critiques the notion that smaller components are inherently better for modularity. Ousterhout argues that excessive subdivision can introduce unnecessary complexity and cognitive load, advocating for merging related functionalities instead. The third idea addresses the complexity introduced by exception handling, proposing techniques to minimize it, such as defining errors out of existence, masking exceptions, and aggregating exception handling. By implementing these strategies, developers can create more maintainable and understandable software systems.

- Emphasizing zero-tolerance towards complexity can prevent project issues.

- Smaller components may not always enhance modularity; merging related functionalities can simplify design.

- Exception handling can introduce significant complexity; reducing the number of exception handlers is beneficial.

- Centralizing duplicated logic can streamline code maintenance and reduce cognitive load.

- Techniques like error definition and exception masking can simplify error management in software systems.

AI: What people are saying
The comments on the blog post about "A Philosophy of Software Design" reflect a range of opinions and insights regarding software design principles.
  • Many commenters emphasize the importance of well-designed abstractions, noting that they should be easy to understand and use.
  • There is a debate about the balance between simplicity and modularity, with some arguing that excessive modularity can lead to unnecessary complexity.
  • Several commenters express a desire for more in-depth discussions on complex software design challenges beyond basic principles.
  • Some readers appreciate the book's insights but criticize its theoretical depth and consistency.
  • There is a recognition of differing perspectives based on programming backgrounds, particularly between functional programming and imperative programming.
Link Icon 22 comments
By @gregfjohnson - 4 months
This short book is (IMHO) one of the best on software design. To me the main point of the book is the importance of well-designed abstractions. The "surface area" of a well-designed abstraction is small, easy to understand, and helpful as you reason through your code when you use it. The underlying implementation may be deep and non-trivial, but you find that you don't have any need to worry about the underlying internal details.

In short:

A beautifully designed abstraction is easy to understand and use.

It is so trustworthy that you don't feel any need to worry about how it is implemented.

Finally, and most importantly, it enables you to reason with rigor and precision about the correctness of the code you are writing that makes use of it.

By @noelwelsh - 4 months
I come from an FP background, and this book was interesting to me as the author very clearly has a very different (imperative, systems) background. In some cases we have very different opinions, in some cases I'm completely agreed (e.g. "define errors out of existence" is extremely common in FP, usually under the term "make illegal states unrepresentable"), and in other cases I feel they were half-way to FP but couldn't quite get all the way there (e.g. the editor example is a classic interpreter, but they didn't make the connection IIRC.) I only skimmed the book and would like to go back for a more detailed review. Curious if anyone else with an FP background had the same or different experience.
By @abcde777666 - 4 months
A lot of these types of books and posts only deal with the low hanging fruits of software design difficulty, such as the provided discount service example.

The trouble is that kind of thing's pretty much software development common sense - only the inexperienced don't know it.

The true difficulties of software development are often must gnarlier in my experience.

For instance, making architectural choices for large and dynamic software systems, such as say a cutting edge game engine - that can be really hard to get right, and there's not always a lot of sage advice out there for how to navigate it - and not just for game engines but for any equally or more complex software.

I guess my point being - I'd love to see more effort into addressing the hard design stuff, and less repetition of what's already been established.

By @bvrmn - 4 months
Sadly article's author doesn't touch the main idea of the book: component's public API should be narrow as possible. John makes a great deal of that with concrete examples.
By @suzzer99 - 4 months
Something bugs me about that first example. We started with two classes that had trivial constructors, and changed them into classes that require an instance of DiscountService be supplied to a constructor. That doesn't feel like less complexity to me.

I'd probably just make applyDiscount a static utility method that the two classes import and invoke on their own, at least until it becomes obvious that something more involved is needed.

By @ricardobeat - 4 months
While I heartily agree with limiting complexity as a ground rule, the example given is not a great one.

First, it’s more about repetition/poor design than complexity. Second, creating a separate service class for applying a discount is adding unnecessary complexity. You’ll end up with a pile of DiscountService, TaxService, ShippingCostsService, and so on, and they will be sewn together like patchwork. It seems to be a common pattern in Java but surely there are better ways?

By @sarchertech - 4 months
It’s a great book. I feel like a lot of the midlevel engineers I’ve worked with over the years who read clean code and stopped there would benefit greatly from it.
By @recroad - 4 months
Here’s a recent interview with the author https://youtu.be/bopwQ_YV06g?si=S2YOVbXj3MJ2NlEG
By @WillAdams - 4 months
Agreed.

I read this book recently, one chapter at a time, and after each, reviewed the code for my current project, applying the principles to it in a re-write --- it helped a lot.

Highly recommended.

By @johnwatson11218 - 4 months
A good game is supposed to be "easy to learn and hard to master". I think software abstractions should have this property as well. Too often the next "fix" in a long chain of failed ideas in overly engineered software feels like the Batman games where one has to complete a mini tutorial to learn to use the "bat-whatever" for a single application/puzzle. Contrast this with the Borderlands franchise, I can learn to play Borderlands in 5 minutes and explore the skills tree and whatnot at my leisure if at all. You hear about "Deus ex machina" as a lazy trait in writing, but it is commonplace in enterprise software. Load Bearing Abstractions.
By @indoorcomic - 4 months
I disagree with the examples in the second idea. The "bad" example is easier to understand and maintain at a glance in my opinion. Looking at the RegisterUser method, I can immediately see the steps it takes to register a user, whereas the "good" example I have to think about it a bit more. Of course, this is a simple example so not much thinking needs to be done, but in a more realistic application I think this would hold much more truth. In projects I've worked on I've seen methods get incredibly bloated due to this. I certainly do agree that "splitting things up for the sake of splitting them up" can be bad practice, I'm just not sure this is the best example to demonstrate that.
By @vkazanov - 4 months
In the oop age of arch I felt like I had no mouth (and I had to scream).

This book, as well as the data oriented design approach, is what made things right for me.

By @Warwolt - 4 months
I read this book with some colleagues at a work book club, and I think it's interesting how split the opinion on the book is among readers.

My impression is that there's some good ideas in the book, but it suffers from not being thorough enough on a theoretical level. Many definitions given are NOT consistently used, the book frequently falls back on discussing things in a very OOP centric way, and a lot of stuff came across to me as just opinion pieces.

Some stuff I found was excellent, like the notion of module depth.

When reading reviews on Goodreads, there's a similar disparity between people who really liked it and people who are critical of it.

By @galaxyLogic - 4 months
“The idea behind exception aggregation is to handle many exceptions with a single piece of code; rather than writing distinct handlers for many individual exceptions, handle them all in one place with a single handler.”

This seems similar to how events are handled in a web-browser. Each element can handle its own event-handlers. But equally well there can be a single event-handler for each event-type in a containing element, perhaps at the top-level only.

If you define event-handlers of a given type for all DOM-elements of the page in a single location it becomes much more flexible to modify how and which events are handled and for which DOM-elements.

So we could say that "error" is just one of the event-types, errors can be or could be handled by the same mechanism as events in general are. Right? Or is there clear categorical difference between error-events and other types of events?

By @NomDePlum - 4 months
A similar article discussing the same book: https://blog.pragmaticengineer.com/a-philosophy-of-software-...
By @cdpd - 4 months
I think that the first example tackles such a naive approach about how to implement the discount code, IMHO I would ask more about the business logic around it. Why is a discount applied to both the order and the shipping? are they the same thing? What if the company applies discounts to only shipping and not orders itself?

Maybe it comes from experience, but I would focus on understanding the business side first and the see if the abstraction is feasible (To be honest the first approach in the example is not even bad given that they are two "independent" business entities)

By @onemoresoop - 4 months
I love this website’s format, it seems very pleasant to read.
By @ozgrakkurt - 4 months
Focusing on these things feels like focusing on training technique too much when going to the gym. In the end mostly what matters is getting things done and keeping it as simple as possible. And simple is very subjective, everyone’s simple is what is simple to them. Feels like everyone should pick a technique and master it then reuse it, there is not much point on finding a global simple/elegant
By @PunchTornado - 4 months
I view that some of his principles are against what uncle Bob is preaching. And I agree with him, especially on the focus to reduce complexity. Uncle Bob preaches modularity and encapsulation over everything and you end up with a thousand 2 liners classes and methods.
By @tpoacher - 4 months
The telegram channel is a great idea. Subscribed!