The Pragmatic Engineer

The Philosophy of Software Design – with John Ousterhout

· Summarized

Share this episode
Anyone with this link can open it.
Share Link
Start at 0:00

Key takeaways

  • AI tools are enhancing low-level code generation but are not likely to replace high-level software design tasks.
  • Software design is a crucial skill that involves decomposition, focusing on deep modules, and striving for simplicity in interfaces.
  • Popular methodologies like TDD and the Single Responsibility Principle should be revisited critically to avoid superficial design.
  • Empathy in software design fosters better communication and understanding, ultimately resulting in superior systems.
  • Teaching software design effectively requires iterative learning and feedback, creating a robust foundation for students entering the industry.

Summary

The Impact of AI on Software Engineering

John Ousterhout discusses how the rise of AI tools and LLMs (like Cursor and Copilot) is revolutionizing low-level code generation. While AI aids autocomplete and accelerates development, he argues that it does not replace high-level design tasks, thereby increasing the importance of software design. As AI handles more low-level programming, engineers need to focus on creating robust designs.

Understanding Software Design

Ousterhout articulates his philosophy on software design, emphasizing the significance of decomposition and abstraction. He introduces the concepts of deep and shallow modules, pointing out that deep modules—those with minimal interfaces yet substantial internal complexity—are essential for managing software complexity effectively. He stresses that good design is less about producing short methods and more about creating clean, meaningful interfaces.

Critiquing Popular Practices

The conversation shifts to commonly accepted practices like Test Driven Development (TDD) and the Single Responsibility Principle. Ousterhout argues that TDD can lead to rushed and suboptimal designs due to its focus on writing passing tests instead of thoughtful architecture. He also contends that overemphasizing the Single Responsibility Principle may result in over-decomposition, making the overall system more complex. He supports the use of well-written comments to clarify complex code.

Empathy in Software Design

An unexpected theme emerges around empathy as a crucial trait for software designers. Ousterhout notes that designers must understand the perspectives of users and team members to build effective systems. This empathetic approach aids in reducing miscommunication and improving overall user experience with the software.

Lessons from Academia

Ousterhout reflects on his experiences transitioning from industry to academia, recounting how he teaches software design at Stanford. His class structure allows students to learn by doing and receive comprehensive feedback, leading to improved coding practices and awareness of design principles. He also notes the disparity in knowledge between academia and industry and how his teaching aims to bridge that gap.

Sponsors

  • CodeRabbit · Cut code review time and bugs in half. Use the code PRAGMATIC to get one month free.
  • Modal · The cloud platform for building AI applications.

Show notes

Supported by Our Partners

•⁠ CodeRabbit⁠⁠ — Cut code review time and bugs in half. Use the code PRAGMATIC to get one month free.

•⁠ Modal⁠ — The cloud platform for building AI applications.

—

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)

—

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

—

The Pragmatic Engineer deepdives relevant for this episode:

• Engineering Planning with RFCs, Design Documents and ADRs

• Paying down tech debt

• Software architect archetypes

• Building Bluesky: a distributed social network

—

See the transcript and other references from the episode at ⁠⁠https://newsletter.pragmaticengineer.com/podcast⁠⁠

—

Production and marketing by ⁠⁠⁠⁠⁠⁠⁠⁠https://penname.co/⁠⁠⁠⁠⁠⁠⁠⁠. For inquiries about sponsoring the podcast, email podcast@pragmaticengineer.com.



Get full access to The Pragmatic Engineer at newsletter.pragmaticengineer.com/subscribe

Follow this show on AudioBrevity and new episodes get summarized for you. Create a free account

Couldn't load the audio. A blocker may be stopping the show's tracking links. Open the file