Architecting software systems with sound engineering principles becomes more and more difficult as the demand for ultra-rapid delivery of new features and bug fixes grows. This is especially true for enterprise software systems and SaaS application environments where the business hinges on the ability to deliver changes to a production environment for active users.
A Philosophy Of Software Design from John Ousterhout focuses on one thing: complexity in software systems. While this book is rather brief at a modest 178 pages Ousterhout offers a good summary of what complexity is, the risks that come with it in software design, red flags to identify it, and finally some approaches and ideas that can be used to reduce complexity in modern software engineering applications.
- Ousterhout's book opens up by defining the very nature of complexity in software systems and the inherent risks that come with it. He defines two main buckets of causes of complexity, dependencies, and obscurities, which are further dissected in later chapters for how to deal with them.
- Chapter 3 offers a discussion on the idea that “working code is not enough”. He defines two mindsets when it comes to programming: tactical vs. strategic. Tactical programming can be thought of as putting in some minimal amount of investment to get something working or fixed as easily as possible. In contrast, strategic programming is the mindset that the goal should be to produce both a great design while also producing code that works. Many organizations encourage the former, tactical mindset, by delivering features and bug-fixes as quickly as possible. Later on, in Chapter 19, Ousterhout points out that this is especially prevalent in agile environments metrics around delivering story points at the end of a short sprint cycle are the only ones measured. The end result here is a pile of complex code that is riddled with bugs and frustratingly hard to work on and extend. This leads to not only a slowdown overall delivery but the risk of more bugs.
- Throughout the chapters, Ousterhout highlights “Red Flags” that a programmer can use to identify potential complexity. An example of this is the “hard to pick name” Red Flag regarding the naming of variables and methods where this might be a signal that the underlying design is not a clean one. These practical and easy to follow tips are scattered throughout the chapters which I find useful and giving the book more of a handbook feel to it.
- In Chapter 7 the book discusses the challenges of what he calls “pass-through variables” which contribute to complexity in software design. Pass-through variables, he explains, is any variable that is passed down through a long chain of methods. Ousterhout's main point about these variables is that they add complexity because they force all of the caller methods to be aware of their existence. Ousterhout offers several approaches to eliminating pass-through variables such as leveraging a shared object between the topmost and bottommost methods, utilizing globals, and lastly leveraging a shared context that is passed around the instance of the application.
- In Chapter 10 Ousterhout dives into error handling stating that error handling is one of the worst sources of complexity in a software system. He introduces the idea of defining “errors out of existence” where the general idea is to reduce and even eliminate, if possible, occurrences where exceptions are reported back through the software stack. He offers a handful of approaches to defining errors out of existence: Removing errors and thrown exceptions and simply returning null/nil, masking exceptions at lower levels (hung processes, retries), aggregating exceptions, and simply crashing where needed rather than maintain complex error handling code. Exception handling is no doubt a difficult challenge in software applications with many nested modules where errors can occur and may or may not need to bubble up to the top of the stack. Aggregating and managing exceptions in a single place can be a dangerous approach if not managed properly. In any RESTful architecture, you want to maintain the standards of the protocol where you must always be able to return the proper HTTP status code rather than a generic HTTP 500 with a nested error message from the originating exception.
Comments serve as a canary in the coal mine of complexity.
- Chapters 14 and 15 focus more in-depth on code style and convention. Ousterhout covers the importance of choosing names carefully to reduce complexity in software where removing ambiguity and offering names that are more obvious makes code more readable and maintainable. This is somewhat in opposition to the style guide of Golang where short variable and method names are encouraged for readability. I enjoyed his take and approach to code comments. I can’t think of one time where I wrote a comment before I actually wrote a piece of code but Ousterhout explains how comments should be written first and be incorporated into the early stages of the design process.
- Ousterhout’s A philosophy Of Software Design offers a brief but thorough discussion on complexity in software design. The book covers the inherent risks of complexity in software systems, supplies some common red flags that can be used to identify it, and offers approaches to reducing and preventing it in any software system. I enjoyed this book and recommend it to any software engineer who is interested in learning more about sound principles of system architecture and reducing the common struggles we face every day in our software systems.