Stream the Latest Episode
Listen and watch now on YouTube, Spotify, and Apple. See the episode transcript at the top of this page, and a summary at the bottom.
Brought to You By
CodeRabbit — Cut code review time and bugs in half. Use the code PRAGMATIC to get one month free.
—
In This Episode
How will AI tools change software engineering? Tools like Cursor, Windsurf and Copilot are getting better at autocomplete, generating tests and documentation. But what is changing, when it comes to software design?
Stanford professor John Ousterhout thinks not much. In fact, he believes that great software design is becoming even more important as AI tools become more capable in generating code.
In this episode of The Pragmatic Engineer, John joins me to talk about why design still matters and how most teams struggle to get it right. We dive into his book A Philosophy of Software Design, unpack the difference between top-down and bottom-up approaches, and explore why some popular advice, like writing short methods or relying heavily on TDD, does not hold up, according to John.
We also explore:
The differences between working in industry vs. academia
Why John believes software design will become more important as AI capabilities expand
The top-down and bottoms-up design approaches – and why you should use both
John’s “design it twice” principle
Why deep modules are essential for good software design
Best practices for special cases and exceptions
The undervalued trait of empathy in design thinking
Why John advocates for doing some design upfront
John’s criticisms of the single-responsibility principle, TDD, and why he’s a fan of well-written comments
And much more!
As a fun fact: when we recorded this podcast, John was busy contributing to the Linux kernel: adding support to the Homa Transport Protocol – a protocol invented by one of his PhD students. John wanted to make this protocol available more widely, and is putting in the work to do so. What a legend! (We previously covered how Linux is built and how to contribute to the Linux kernel)
Takeaways
Topics I found especially interesting in our conversation:
1. The explosion of AI coding could make software design more important than before. Currently, AI coding tools and agents are akin to “tactical tornadoes” that code fast, fix issues fast… while creating new issues and adding tech debt. John doesn’t see the current tools being able to replace high-level design. And so software design could be more important than before – thanks to more code being written than before!
2. Software design is a decomposition problem. How do you take a large system and divide it into smaller units that you can implement relatively independently?
John believes that the most important idea for all of computer science is just this – decomposition. If you can break up complicated problems into smaller parts: you can solve so many problems!
3. Test Driven Development (TDD) works against good software design. John firmly believes that TDD is counter-productive because it forces thinking about the small details before thinking about the high-level design. This observation could explain why TDD has not gained much traction in the last decade or so!
John sees some value in TDD in specific cases. Most commonly: when fixing a bug, it’s helpful to write a test first that the bug breaks; and then fixing the bug fixes it.
The Pragmatic Engineer deepdives relevant for this episode
Timestamps
(00:00) Intro
(02:00) Why John transitioned back to academia
(03:47) Working in academia vs. industry
(07:20) Tactical tornadoes vs. 10x engineers
(11:59) Long-term impact of AI-assisted coding
(14:24) An overview of software design
(15:28) Why TDD and Design Patterns are less popular now
(17:04) Two general approaches to designing software
(18:56) Two ways to deal with complexity
(19:56) A case for not going with your first idea
(23:24) How Uber used design docs
(26:44) Deep modules vs. shallow modules
(28:25) Best practices for error handling
(33:31) The role of empathy in the design process
(36:15) How John uses design reviews
(38:10) The value of in-person planning and using old-school whiteboards
(39:50) Leading a planning argument session and the places it works best
(42:20) The value of doing some design upfront
(46:12) Why John wrote A Philosophy of Software of Design
(48:40) An overview of John’s class at Stanford
(52:20) A tough learning from early in Gergely’s career
(55:48) Why John disagrees with Robert Martin on short methods
(1:10:40) John’s current coding project in the Linux Kernel
(1:14:13) Updates to A Philosophy of Software Design in the second edition
(1:19:12) Rapid fire round
(1:01:08) John’s criticisms of TDD and what he favors instead
(1:05:30) Why John supports the use of comments and how to use them correctly
(1:09:20) How John uses ChatGPT to help explain code in the Linux Kernel
A summary of the conversation
Impact of AI on software engineering: tactical applications
John sees AI tools improving code autocompletion and facilitating the generation of low-level code.
→ thus software engineers will dedicate more time to high-level design tasks.
John uses ChatGPT to assist in understanding the Linux kernel codebase, highlighting a practical application of AI in navigating complex existing systems.
AI coding tools as “tactical tornadoes?” AI code generation could mirror the work of "tactical tornadoes" who prioritize quick output, often leading to maintainability challenges.
Principles of good software design
Software design is a process of decomposition: breaking down large systems into manageable units for independent implementation.
To manage complexity: eliminate it by
Avoiding special cases
Or hiding complexity through modular design!
“Design it twice:” John advocates for this. For example when he designed the API for the Tk Toolkit: the second design proved superior.
Deep modules: creating deep modules with simple interfaces masks significant internal functionality. This helps manage complexity.
Error handling:
The tactical approach is trying to "define errors out of existence" by designing systems to prevent certain errors from occurring. Be careful of simply ignoring necessary error checks though!
When designing interfaces: consider the caller's perspective
Design reviews and discussions: these are important to get more viewpoints and when evaluating design tradeoffs.
John mentions a specific whiteboarding technique for achieving consensus in discussions – consider trying it out!
John’s disagreements with practices outlined in the book Clean Code by Robert C. Martin
Short methods: John is against the extreme application of short methods advocated in Clean Code. He argues that excessive decomposition can increase interface complexity and reduce understandability when methods are tightly coupled. He favors grouping-related functionality for better depth.
Test-Driven Development (TDD): John is concerned that TDD gets in the way of good software design. Instead of TDD, he suggests focusing development on abstractions rather than individual tests. The one place when writing tests first is helpful: when fixing bugs!
Comments: John disagrees with minimizing them. Comments are important for documenting interfaces (explaining how to use a module) and member variables (explaining their purpose). While AI tools might assist in understanding uncommented code, they don't eliminate the need for clear, informative comments!
Teaching software design at Stanford
John’s software design course at Stanford uses a pedagogical approach modeled after English writing classes, emphasizing feedback and revision.
Students undertake significant projects: one example is implementing the Raft consensus protocol
Extensive code reviews: these are part of the course! And a key part. John personally reviews every line of student code and provides detailed feedback (wow!!)
Students are encouraged to compare different solutions to the same problem developed by their peers. This helps learning through observing alternative design choices and their consequences.
What John is currently working on
John is currently busy contributing to the Linux Kernel (!!) and is currently engaged in the practical application of software design principles through his work on a Linux kernel implementation of the Homa Transport Protocol – a new transport protocol invented by one of his PhD students
The process of upstreaming Homa into the Linux kernel involves direct engagement with the kernel development community through code submissions and responses to feedback, illustrating real-world code review and integration processes.
Where to find John Ousterhout:
• X: https://x.com/johnousterhout
• Website: https://engineering.stanford.edu/people/john-ousterhout
Mentions during the episode:
• UC Berkeley: https://www.berkeley.edu/
• Sun Microsystems: https://simple.wikipedia.org/wiki/Sun_Microsystems
• Stanford University: https://www.stanford.edu/
• A Philosophy of Software Design: https://www.amazon.com/Philosophy-Software-Design-2nd/dp/173210221X/r
• TDD (test-driven development): https://en.wikipedia.org/wiki/Test-driven_development
• Design Patterns: https://en.wikipedia.org/wiki/Design_Patterns
• Engineering Planning with RFCs, Design Documents and ADRs: https://newsletter.pragmaticengineer.com/p/rfcs-and-design-docs
• Tk: https://en.wikipedia.org/wiki/Tk_(software)
• Waterfall methodology: https://www.atlassian.com/agile/project-management/waterfall-methodology
• Robert "Uncle Bob" Martin and John Ousterhout’s discussion: https://github.com/johnousterhout/aposd-vs-clean-code/blob/main/README.md
• Clean Code: A Handbook of Agile Software Craftsmanship: https://www.amazon.com/dp/0132350882
• Bob Martin on X: https://x.com/unclebobmartin
• Single-responsibility principle: https://en.wikipedia.org/wiki/Single-responsibility_principle
• The Linux Kernel Archives: https://www.kernel.org/
• How Linux is built with Greg Kroah-Hartman: https://newsletter.pragmaticengineer.com/p/how-linux-is-built-with-greg-kroah
• Homa: A Receiver-Driven Low-Latency Transport Protocol Using Network Priorities: https://people.csail.mit.edu/alizadeh/papers/homa-sigcomm18.pdf
• Behnam Montazeri on LinkedIn: https://www.linkedin.com/in/behnam-montazeri-639a8a29/
• TCP: https://en.wikipedia.org/wiki/Transmission_Control_Protocol
• Resources from John’s website: https://web.stanford.edu/~ouster/cgi-bin/aposd.php
• A Philosophy of Software Design: My Take (and a Book Review): https://blog.pragmaticengineer.com/a-philosophy-of-software-design-review/
—
Production and marketing by Pen Name. For inquiries about sponsoring the podcast, email podcast@pragmaticengineer.com.
Share this post