Windows on Black Boxes
What replaces reading the code.
Kent Beck wrote the book on test-first development, and JUnit with it. So when he joined Facebook in 2011 and shipped his first feature, he did the Kent Beck thing and wrote unit tests for it.
He caused a site event anyway. Some other coupled code he hadn’t found.
“Would unit tests have been better? Maybe,” he said, looking back on it. What caught bad code at Facebook wasn’t the test. It was everything downstream. Your dev machine ran the whole site, so you could change a colour and see the result in seconds. Then code review. Then internal rollout, where every employee used your feature that day. Then a phased rollout that capped the blast radius at a few million people. Then the deploy team, who could block you outright. And then, in Beck’s words, “you deploy stuff and you’d look at the results, like the observability, early observability stuff, so you get more feedback.”
Every layer has holes. Most days they don’t line up.
Facebook famously didn’t unit test most things at the time, and the industry called that reckless. What the industry missed, Beck says, is all the other layers most places didn’t have. Verification had moved out of the code and into the runtime, at a company big enough that reading every unit had stopped being possible.
Agents just made that condition universal, and pushed it down a level. Not the running system, which was always bigger than any one head. The implementation underneath it.
The escape hatch
John Ousterhout gave us the cleanest vocabulary for the other way out in A Philosophy of Software Design. He called it the deep module. A simple interface hiding a powerful implementation, so the caller presses the button and never needs to know what sits behind it. Information hiding, with a good ratio of functionality to interface.
Underneath that sat an assumption I never examined, and I don’t think Ousterhout had to. If the interface ever lied to you, you could open the box. Read the implementation. Find the coupled code Beck didn’t find. You didn’t need to, and that was the whole point, but the option was always there.
Agents spent the option. When a module gets rewritten between one run and the next, reading it stops paying, because you’d be building a mental model of something that won’t exist by the afternoon. Ousterhout’s box was one you didn’t need to open. This one you can’t, because the inside is different every time you look.
Not every codebase turns over that fast. Mine doesn’t, most days. But the direction only runs one way, and once that option is gone, something else has to establish that the interface is telling the truth. The only candidate left is the thing Beck watched work at Facebook. Verification after the fact, from outside. You cut a window in the box.
What makes a module deep now
A narrow interface, enough runtime observability to verify the behaviour from outside, and tests that pin that behaviour at the boundary. Ousterhout wrote the first of those when the implementation was a stable asset you could always fall back on. Agents turned it into a stream, and the other two stopped being things you bolt on around a module. They are part of what the module is.
The interface half matters more now, not less. A narrow interface is what lets an agent change one module without threading the implementation of everything around it through its context. Ousterhout wrote information hiding for a human who couldn’t hold the whole system in their head. It works the same for a machine with a context window.
Three people shipping about a million lines of code, Ryan Lopopolo’s team met this as a quality problem. Their agents couldn’t produce “modular software that decomposed appropriately, that was reliable and observable,” so they invested in giving the model that observability directly. Vaibhav Gupta met it as a language problem and built tracing into the language itself, so any function’s real behaviour is inspectable after the fact, by a human or by an agent. “Observability is not useful at all for things you already know,” he says. “Observability is useful in hindsight.”
Nobody writes the spec first
That’s one module. An application is a pile of them, and if the only record of what it’s supposed to do lives in the implementation, that record lives in the one thing an agent will rewrite by Thursday.
Writing it all down in advance doesn’t get you out. We gave up on waterfall because nobody can specify an application before they’ve felt it work. The spec arrives in passes. Each feature draws another line around what the thing is supposed to be, and the total spec only exists at the end, as the sum of every line anyone ever drew. That sum is the product. Nobody has ever held it in one document, and no ticket holds more than a slice of it.
Which makes the ticket the one artifact in a review whose truth expires the moment it’s satisfied. It was true for an afternoon. The line it drew is a promise to a user, and that stays. Whether the ticket got satisfied is checkable, and the agent already checked it. Nobody checks whether the promise that just landed is held by something that outlives the diff. A test that pins the new behaviour at the boundary. A trace that shows it happening in production.
Did this change what the system says about itself? If a feature adds a line to the spec and changes nothing about what the system can tell you afterwards, you’ve made a promise with no way to check whether you’re keeping it.
Observability and tests are where the product definition goes to live. Not because an agent can’t touch them (it will delete a test to make the suite go green) but because they are the only part of it where a change shows up as a change. The ticket is the note you wrote on the way there.
My review was missing two axes
I run my diffs through a review skill with two axes. Standards, which asks whether the code follows the rules, and Spec, which asks whether the diff does what the ticket wanted. The design is Matt Pocock’s, and so are the Fowler smells in it. I’d added the SOLID and the Ousterhout principles to his. Both axes read the code that gets rewritten and the ticket that expires. Neither one asks what the thing will tell you when it runs.
I’ve spent a year arguing that the mechanical checks belong in the harness so review can be about intent. I hadn’t noticed my own harness was blind to the two checks that survive. I had put Ousterhout’s principles into that skill and never asked what his idea needed now.
So I added the two that were missing and weighted all four. The report follows the weight:
Observability. After this code runs, can you tell what it did without reading it?
Tests. Is there evidence the behaviour was pinned by a test, at the boundary?
Implementation. Standards, smells, the Fowler list.
Spec. Does it do what was asked?
Spec sits fourth because a ticket holds one slice of the total spec, and that slice is spent the moment the diff satisfies it. What outlives the ticket is whatever axes one and two pinned down, the trace and the test.
The check that earns first place is the dark external call. A new call across a process boundary (HTTP, database, queue, LLM) with no span, no timing, no structured log. The boundary is where the failures live, and closing that gap is worth more than every smell underneath it. Then the cheap ones, like a catch that logs nothing and returns a bare null, stripping the cause on the way out.
Then I ran both orders over the same diff. Eleven commits, 83 files, a lead-capture quiz and a Telegram notifier I’d shipped in a day. The old order came back with nine findings, not one of them about what the code emits when it runs. Duplicated fetch boilerplate. A function called cleanMetadata that cleans nothing. A "use client" that didn’t need to be there. The new order’s first axis found that the leads module never imports the logger, so all five of its calls to Supabase cross the network in silence. And that the notifier logs telegram.sent, at info level, when Telegram has just rejected the message. My sale alerts could stop firing and the logs would tell me they went out.
The old order read that same file and told me I’d repeated myself.
The Tests axis leads with something narrower. A test removed, skipped, or an assertion quietly loosened. It’s the signature failure of agent-written code, which will cheerfully delete a test to make the suite go green, and Beck has complained about exactly this. The man who taught the industry to write the test first can’t get the machines to stop deleting them.
When is deleting a test allowed?
A deleted test is a claim that the product changed. Sometimes that claim is true. When a feature redraws a line someone drew earlier, the test that pinned the old line is now pinning a promise nobody is making, and the honest move is to change it or cut it. Every other test in the suite is still a live promise, and those hold still.
So the review question isn’t whether tests changed. It’s whether the spec changed with them. A deletion that traces back to a redrawn line is bookkeeping. A deletion that doesn’t is the suite dropping a promise on the floor, and it comes back green either way.
That’s the check, and it can only run on the diff. The skill reads it for a test removed, skipped, or an assertion loosened, quotes the assertion back at you, and asks which line of spec moved. Nothing about the test run will tell you a question stopped being asked.
The window
Beck’s coupled code got through. That day the holes lined up. Most days they didn’t, which is the only reason a company that barely unit tested could ship at all. The failures lived in the interactions, and the layers below him were watching the interactions.
Agent codebases have that property turned up. The unit is disposable, so the interactions are what you instrument.
Ousterhout told us to hide the implementation, and he was right. Agents have taken him further than he meant to go. The implementation is now hidden from the person who owns it (doubly so for agent-based applications), and it will be different tomorrow.
My skill still opens every diff looking for smells. It just does it fourth now, after it has asked what the code will tell me once it runs.
The diff in front of you is a box, and it won’t hold still. You put a window in it, and you watch.


