I listened to
’ interview with Prof. Ousterhout.I have a lot more to say, but for now I’ll leave this one thought—Prof. Ousterhout & I have way more in common than we have differences, but those differences are pretty fundamental.
Thank you to new sponsor Postman
Build Smarter APIs with AI—Join POST/CON 25 in LA, June 3 & 4, 2025
Offering APIs is a service & a responsibility. Postman helps you carry this responsibility with testing, deployment, & documentation support. Their user conference, POST/CON 25, is where their community comes together to see what’s next.
Join two days of hands-on sessions, real stories from the field, and a community that actually gets what you do. You’ll walk away with new ideas, new connections, and probably a few new favorite tools.
Use code PMN25CAM7 for 25% off
Function Size
One of the differences is our preference on function size. He’s a lumper, I’m a splitter. I wrote earlier about how we shouldn’t talk about function length but rather distribution of function length, so I won’t belabor that point. Here’s a guess: if you looked at a function over its lifetime, I’m not sure if there would be much difference in lengths (or distribution of lengths) between Prof. Ousterhout’s functions & mine. We come to our conclusions through different thinking.
Example: Accidentally Mixed Concerns
Let’s say we have a process with 3 steps.
Processor
function process()
step1()
step2()
step3()
We code for a while & discover we need a file in step 1.
function step1()
…
file = …open file…
…
file.close()
So far, so good. Now we decide we also need the file in step 3, so we make the file an instance variable, open it in step 1, and close it in step 3.
Processor
file
function step1()
…
file = …open file…
…
function step3()
…
file.close()
Now we have exactly the situation we (Prof. Ousterhout & I, if I understand him correctly) don’t want—if we want to understand whether the external resources associated with the file are cleaned up, we have to look at several functions at once. In addition, if step2()
starts throwing an exception, the file won’t be cleaned up, a nasty bit of implicit coupling.
Longer Functions
A longer, less abstracted process()
function helps with this situation. By inlining the helper functions, we are able to analyze resource cleanup without jumping from function to function.
function process()
// step 1
…
file = …open file…
…
// step 2
…
// step 3
…
file.close()
Now we can understand the resource allocation situation & deal with it in one place. However, this comes at the cost of intertwingling all the other details in the midst of the resource allocation, muddling our analysis.
Is there a design that cleanly separates the resource allocation/deallocation from what is being done with the contents of the file? If so, we can regain the benefits of the original function proudly displaying its structure. That’s why I call One Pile an interim design:
We have intermixed concerns.
We put them all in once place.
Now we can separate them cleanly.
Foolproof Mechanisms
What if we had an interface to file opening which took a lambda as an argument, guaranteeing that the file would be closed regardless of what happened inside the lambda?
function process()
File.openWhile({file:
step1(file)
step2(file)
step3(file)
})
Now:
Our guarantee that opened files are closed happens in one place
Processing the file in steps 1, 2, & 3 happens in another
The discovery of such mechanisms is part of what Prof. Ousterhout means by design. Hey, me too!
In Pictures
Put more graphically, because that’s one of the ways I think, say we have a call tree:
Some change we want to make spans multiple functions (the functions are coupled, in Empirical terms).
One Pile suggests inlining all those functions to get to a single element.
But just until we have an idea for how to orthogonalize the concern causing the coupling.
You can’t predict when the insight will come that lets you put the coupling-causing concern somewhere else. I think 10 years is about my record from recognizing that I didn’t like complexity being mixed together & imagining the abstractions that let me clean up the situation. Better to wait for that inspiration than jam an abstraction in there that only ends up making things worse.
When I listened to the take, my immediate thought was that he is operating in a different domain. He's doing 1) academics and 2) kernel in C. This is a different problem domain than you have in business programming. This might not be the right line of thinking but at the same time I always go back to automobiles... auto mechanic, truck mechanic, train mechanic, racecar mechanic. Each domain requires different tools and thinking. Yes basic principles apply. Torque, heat, basic electronics, etc. But there are vast differences at higher levels.
If, then. While. On/Off. Set theory. Loop nesting. Etc. All the basics apply in all domains of programming. But at higher levels...
I'd be curious about your thoughts regarding what Prof. Ousterhout says about TDD. My understanding is he thinks of design as being something that can happen only upfront, and TDD kills the opportunity to design by focusing on the most immediate, shortest step possible.