Logo

dev-resources.site

for different kinds of informations.

Lessons from A Philosophy of Software Design

Published at
1/15/2025
Categories
webdev
programming
books
softwareengineering
Author
thawkin3
Author
8 person written this
thawkin3
open
Lessons from A Philosophy of Software Design

I recently finished reading A Philosophy of Software Design by John Ousterhout. If you enjoyed Clean Code or The Pragmatic Programmer, you’ll likely enjoy this one too. It’s very similar to those books, has some overlapping content, and focuses primarily on what separates good software design from bad software design.

When I read tech books, I always take notes and highlight passages that resonate with me. It helps me retain the information I found meaningful. I’ve reproduced most of my notes below, and I hope that you’ll find something that resonates with you as well.

Enjoy!

On coding advice

When you disagree with someone’s coding advice (something in this book or in Clean Code), try to understand why you disagree with it.

“Every rule has its exceptions, and every principle has its limits. If you take any design idea to its extreme, you’ll probably end up in a bad place.”

Complexity

“Complexity is more apparent to readers than writers. If you write a piece of code and it seems simple to you, but other people think it is complex, then it is complex. When you find yourself in situations like this, it’s worth probing other developers to find out why the code seems complex to them; there are probably some interesting lessons to learn from the disconnect between your opinion and theirs. Your job as a developer is not just to create code that you can work with easily, but to create code that others can also work with easily.”

Symptoms of complexity

  • Change amplification: Seemingly simple changes require code modifications in many different places
  • Cognitive load: How much a developer needs to know in order to complete a task
  • Unknown unknowns: It’s not obvious which pieces of code must be modified to complete a task

Good design should be obvious.

Modules, interfaces, and implementations

Modules should be deep. Complex implementations should be hidden behind simple interfaces with sensible defaults that work for the most common use cases.

“The best features are the ones you get without even knowing they exist.”

“As a module developer, you should strive to make life as easy as possible for the users of your module, even if it means extra work for you.”

“Reduce the number of places where exceptions must be handled.”

Design approaches

“Designing software is hard, so it’s unlikely that your first thoughts about how to structure a module or system will produce the best design. You’ll end up with a much better result if you consider multiple options for each major design decision.”

“Try to pick approaches that are radically different from each other; you’ll learn more that way. Even if you are certain that there is only one reasonable approach, consider a second design anyway, no matter how bad you think it will be. It will be instructive to think about the weaknesses of that design and contrast them with the features of other designs.”

Comments and documentation

Comments should be used to explain things that can’t be expressed in code, like the rationale for why a certain decision was made.

“The biggest challenge with cross-module documentation is finding a place to put it where it will naturally be discovered by developers.”

Consistency, conventions, and coding styles

“Consistency creates cognitive leverage: once you have learned how something is done in one place, you can use that knowledge to immediately understand other places that use that same approach.”

Document coding conventions and coding style preferences. Automate and enforce what you can, like with ESLint and Prettier. When joining a new company or team, stick to existing conventions that are already in place. Refactoring or re-writing all the code to use a new convention is rarely worth it.

Tactical vs. strategic programming; also Agile

Tactical vs. strategic programming. Don’t just focus on getting a small change working if it makes future changes more difficult.

“One of the risks of agile development is that it can lead to tactical programming. Agile development tends to focus developers on features, not abstractions, and it encourages developers to put off design decisions in order to produce working software as soon as possible. For example, some agile practitioners argue that you shouldn’t implement general-purpose mechanisms right away; implement a minimal special-purpose mechanism to start with, and refactor into something more generic later, once you know that it’s needed. Although these arguments make sense to a degree, they are against an investment approach, and they encourage a more tactical style of programming. This can result in rapid accumulation of complexity.”

Importance of tests

“Tests, particularly unit tests, play an important role in software design because they facilitate refactoring. Without a test suite, it’s dangerous to make major structural changes to a system. There’s no easy way to find bugs, so it’s likely that bugs will go undetected until the new code is deployed, where they are much more expensive to find and fix. As a result, developers avoid refactoring in systems without good test suites; they try and minimize the number of code changes for each new feature or bug fix, which means that complexity accumulates and design mistakes don’t get corrected.

“With a good set of tests, developers can be more confident when refactoring because the test suite will find most bugs that are introduced. This encourages developers to make structural improvements to a system, which results in a better design. Unit tests are particularly valuable: they provide a higher degree of code coverage than system tests, so they are more likely to uncover any bugs.”

Performance improvements

Before making performance changes, measure what you need in order to create benchmarks for the existing performance. Our intuitions of what changes will improve performance are sometimes wrong, having either no meaningful impact or actually making performance worse.

Design code around the critical path. Performance improvements in other places that are not the biggest bottleneck will still result in a bottleneck when you hit that part of the path.

Conclusion and the benefits of good design

“The investments you make in good design will pay off quickly. The modules you defined carefully at the beginning of a project will save you time later as you reuse them over and over. The clear documentation that you wrote six months ago will save you time when you return to the code to add a new feature. The time you spent honing your design skills will also ay for itself: as your skills and experience grow, you will find that you can produce good designs more and more quickly. Good design doesn’t really take that much longer than quick-and-dirty design, once you know how.

“The reward for being a good designer is that you get to spend a larger fraction of your time in the design phase, which is fun. Poor designers spend most of their time chasing bugs in complicated and brittle code. If you improve your design skills, not only will you produce higher quality software more quickly, but the software development process will be more enjoyable.”

programming Article's
30 articles in total
Programming is the process of writing, testing, and maintaining code to create software applications for various purposes and platforms.
Favicon
What ((programming) language) should I learn this year, 2025 ?
Favicon
7 Developer Tools That Will Boost Your Workflow in 2025
Favicon
Lessons from A Philosophy of Software Design
Favicon
🕒 What’s your most productive time of the day?
Favicon
Designing for developers means designing for LLMs too
Favicon
Unique Symbols: How to Use Symbols for Type Safety
Favicon
Why Is Everyone Unhappy With JavaScript? | State of Javascript 2024 Survey
Favicon
Filling a 10 Million Image Grid with PHP for Internet History
Favicon
When AI Fails, Good Documentation Saves the Day 🤖📚
Favicon
The Language Server Protocol - Building DBChat (Part 5)
Favicon
Основы изучения Python: Руководство для начинающих
Favicon
GraphQL Transforming API Development
Favicon
Easy Discount Calculation: Tax, Fees & Discount Percentage Explained
Favicon
Example of using Late Static Binding in PHP.
Favicon
Top 5 Python Scripts to Automate Your Daily Tasks: Boost Productivity with Automation
Favicon
7 Mistakes Developers Make When Learning a New Framework (and How to Avoid Them)
Favicon
How to Resolve the 'Permission Denied' Error in PHP File Handling
Favicon
Python в 2025: стоит ли начинать с нуля? Личный опыт и рекомендации
Favicon
Cómo gestionar tus proyectos de software con Github
Favicon
2429. Minimize XOR
Favicon
Decreasing server load by using debouncing/throttle technique in reactjs
Favicon
➡️💡Guide, Innovate, Succeed: Becoming a Software Development Leader 🚀
Favicon
Debugging Adventure Day 1: What to Do When Your Code Doesn’t Work
Favicon
🚀 New Book Release: "Navigate the Automation Seas" – A Practical Guide to Building Automation Frameworks
Favicon
Булеві типи і вирази
Favicon
Build a Secure Password Generator with Javascript
Favicon
join my project semester simulator
Favicon
Как создать свой VPN и получить доступ ко всему?
Favicon
Revolutionary AI Model Self-Adapts Like Human Brain: Transformer Shows 15% Better Performance in Complex Tasks
Favicon
Breakthrough: Privacy-First AI Splits Tasks Across Devices to Match Central Model Performance

Featured ones: