Back Original

Ideas from "A Philosophy of Software Design"

Almost a month ago, I created a telegram channel with the goal of reading tech books consistently, and sharing summaries of them.

In this post, I will share what are the 3 ideas that resonated with me the most.

Idea 1: Zero-tolerance towards complexity

On the second chapter of the book, the author describes what is complexity and what are its symptoms:

The author argues that complexity is not caused by a single error, it accumulates. Sometimes we convince ourselves that a bit of complexity here won’t matter much, but if everyone on the project adopts this mindset, the project will become complex rapidly.

“In order to slow the growth of complexity, you must adopt a zero-tolerance philosophy”.

Example

Imagine a simple order processing system where you calculate shipping costs, and apply discounts. However, this system is poorly designed with duplicated logic across multiple services, leading to change amplification. Let’s say both the CheckoutService and the ShippingService use the same logic to calculate discounts, but it’s implemented separately in both places.

1
public class CheckoutService

Why is it bad?

How to improve it?

We can refactor the system to eliminate duplicated logic by encapsulating the discount logic in its own class. This way, if the discount logic changes, you only need to modify one place, reducing overall complexity.

1
public class DiscountService

To summarize, in the first example, we saw change amplification where a simple change to discount logic required modifying multiple services. By centralizing the logic in DiscountService, we eliminate this duplication, making it easier to maintain and evolve the system.

Idea 2: Smaller components are not necessarily better for modularity

“Given two pieces of functionality, should they be implemented together, or should their implementations be separated?” - This question was chapter’s 9 focus.

The author argues, while keeping in mind reducing the system’s complexity is our goal, that smaller components are not necessarily better for modularity, and mentions a few cons of splitting out functionality across more components:

The author also offers a few indications that two pieces of code should be merged.

The author mentions the common “clean tip”: “Split up any method longer than X lines.” He adds that “length by itself is rarely a good reason for splitting up a method.” […] Splitting up a method introduces additional interfaces, which add to complexity. […] You shouldn’t break up a method unless it makes the overall system simpler”.

Example

In this example, let’s say we have a user registration process in a system. The developer has over-split the logic into multiple methods, separating each step of the registration, such as validating the user, saving the user to the database, and sending a welcome email. While each method is doing its own thing, they all share information and are conceptually related. This over-splitting leads to unnecessary complexity and overhead.

1
public class UserService

Why is it bad?

How to improve it?

Simple, in that case we simply can “inline” the methods, like the following

1
public class UserService

To summarize, splitting up functionality just for the sake of making smaller methods can actually add complexity, as shown in the first example. By merging related steps that share information and are always used together, we reduce subdivision overhead, simplify the interface, and make the code easier to understand and maintain.

Idea 3: Exception handling accounts for a lot of complexity

In chapter 10 the author argues that “Exception handling is one of the worst sources of complexity in software systems.”.

There are two ways to handle exceptions:

The author mentions that aborting can add much more complexity. For example, if a data structure has been partially initialized, then an exception occurs - “The exception handling code must restore consistency, such as by unwinding any changes made before the exception occurred.”

The author notes how easy and tempting it is to throw an exception and let the caller handle it. He argues that, as the developer of a certain method, if you are having trouble handling a certain exception, there’s a good chance the caller won’t know how to deal with it either.

“The best way to reduce the complexity damage caused by exception handling is to reduce the number of places where exceptions have to be handled.”

From here, the author shares a few techniques on how to reduce the number of exception handlers.

Example

1
public class FileProcessor

Why is it bad?

Fix 1: Define Errors Out of Existence

The first technique is to design the system to avoid exceptions altogether. Instead of throwing exceptions for things like missing files, we can redesign the ReadConfigFile method to treat the absence of a file as a normal condition, not an exceptional one.

1
private Config ReadConfigFile(string filePath)

Why is it better?

Fix 2: Mask Exceptions

Next, we handle lower-level exceptions internally so that higher levels don’t need to worry about them. This is commonly used for network failures, file I/O, or similar situations where retrying or fallback mechanisms can mask the issue.

1
private void WriteDataToFile(ProcessedData data, string filePath)

Why is it better?

Fix 3: Exception Aggregation

Instead of writing separate exception handlers for every possible error, we can aggregate exceptions and handle them in one place. This avoids duplicating exception handling logic.

Given that we still throw IOException, here’s how you might want to aggregate the exceptions.

1
public void ProcessFile(string filePath)

Why is it better?

Conclusion

A Philosophy of Software Design emphasizes that complexity is the silent killer of software systems, accumulating through seemingly small decisions.

Note: I fed NotebookLLM the 33 page summary of the book, and it generated a 17 minutes long podcast that was pretty good honestly (above my expectations) - I shared it on the telegram channel.


Looking for a powerful, self-hosted backend for forms?