The Bottleneck Moved to Review
When producing code got cheap, the binding constraint became verification. The valuable engineer now reads critically, designs checks, and builds systems that verify. Here is why and how.
An agent can now write a working feature in the time it takes you to get coffee. Four hundred lines, tests included, plausible from top to bottom. The generating got cheap. Watch what happens next in almost every team: the pull request sits. It sits because the one part of the pipeline that did not get cheaper is the part where a human has to decide whether those four hundred lines are actually correct.
That is the whole story of engineering right now, compressed. Production went from expensive to nearly free. Verification did not move. And when one stage of a pipeline collapses in cost while the next one holds steady, the whole system’s throughput is now set by the stage that held steady. The bottleneck moved. It moved to review.
Generation got cheap. Review didn’t.
Think about what actually happened to the cost of producing code. A year or two ago, writing a non-trivial function was minutes of your attention, sometimes an hour. Now you describe it and it appears. The marginal cost of one more attempt fell toward zero. You can generate three implementations and pick one. You can regenerate the whole module because you changed your mind about a name.
Now think about what happened to the cost of being sure that code is right. Almost nothing. Reading code for correctness is still a human sitting down and building a mental model of what the code does, comparing it to what it should do, and hunting for the gap. That process runs at human speed. It did not get an order of magnitude faster because the code was typed by a model instead of a person. If anything it got slightly harder, and I will get to why.
“When you make one step in a pipeline a hundred times cheaper and leave the next step alone, you have not removed the bottleneck. You have relocated it, and now everything backs up against the step you ignored.
”
So the queue backs up at the human check. A team that was throughput-limited by how fast people could write code is now throughput-limited by how fast people can trust code. Most teams have not reorganized around this yet. They still treat review as a formality at the end, a rubber stamp, the thing you rush through to get to Friday. That was survivable when writing was the slow part and review was catching the occasional slip. It is not survivable now that writing is instant and review is the wall everything hits.
The “looks right” trap
Here is why unreviewed model output is more dangerous than unreviewed human output, and it is worth sitting with because it inverts an old intuition.
When a junior engineer writes code, the bugs tend to look like bugs. The code is a little clumsy, the edge case is visibly unhandled, the variable name gives away the confusion. The surface of the code carries signals about where it is weak, and an experienced reviewer reads those signals fast.
Model output does not carry those signals. It is fluent everywhere. The correct line and the subtly wrong line are written with exactly the same confidence, the same clean style, the same plausible naming. The code that handles the edge case and the code that silently drops it look identical on the surface. Fluency is uniform, and correctness is not, so fluency stops being a proxy for correctness. That is the trap: the output looks right in precisely the places it is wrong.
This is why “the tests pass and it looks fine” is now a liability disguised as reassurance. The tests were often written by the same model that wrote the code, against the same misunderstanding of the requirement. They pass because the code does what the model thought you wanted, which is not the same as what you wanted. Reading it quickly gives you a feeling of correctness that the fluency manufactured. The feeling is the trap. Unreviewed AI output is not neutral. It is a confident assertion of correctness that you have not actually checked, wired straight into your codebase.
Verification has to become a system
If review is the bottleneck, the answer is not “review harder.” A human reading every line more slowly and more suspiciously does not scale, and it burns out the exact people you need. The answer is to turn verification from an act into a system: layers of automated and structural checks that catch most of the wrongness before a human ever looks, so the scarce human judgment gets spent only where it is irreplaceable.
The core principle underneath all of it is one worth stating plainly, because it is doing the heavy lifting: the thing that produces an artifact should not be the only thing that certifies it. A model that wrote the code has already committed to an interpretation of the task. Asking it “is this correct?” invites it to defend that interpretation. You need a check that is independent of the producer, that can disagree with it. Separation of producer and checker is the design goal, and it shows up at every layer.
No single check catches everything. A verification system stacks cheap, fast, independent checks so that each layer removes a class of error, and only what survives all of them reaches expensive human attention.
Layer 2 · Mechanismhow it actually works
Types. The cheapest independent check there is. A type system rejects a whole category of wrongness (passing the wrong shape, forgetting a case in a union) without anyone reading the code. It runs in milliseconds and it does not care what the model intended. Make illegal states unrepresentable and the model literally cannot write them.
Tests, but adversarial. Tests are only a real check if they are independent of the producer’s misunderstanding. A test the model wrote to match its own code is not verification, it is an echo. Write the tests against the requirement yourself, or have a separate agent write tests from the spec without seeing the implementation, so the test and the code can genuinely disagree.
Property and invariant checks. Instead of asserting specific input-output pairs, assert things that must always hold: this function’s output is always sorted, this operation is reversible, the account balance never goes negative. Property-based testing throws hundreds of generated inputs at those invariants and finds the edge case you would never have thought to write a test for. This is high leverage precisely because it does not depend on you imagining the failing case.
Staged review. Not all changes deserve equal scrutiny. A config tweak and a change to the auth path are not the same risk. Route them differently: auto-merge the trivial and the fully-covered, demand careful human eyes on the security-sensitive and the irreversible. Spend the scarce resource where being wrong is expensive.
Layer 3 · Math & where it breaksgo deeper
Why the layered system beats one heroic reviewer:
P(bad change ships) ≈ P(producer wrong) × Π over layers ( P(layer misses | wrong) )Each independent layer multiplies down the odds that a wrong change survives. If types catch 40% of a class of errors, tests catch 60% of what is left, properties catch half of that, and the human catches most of the rest, the compound miss rate gets small fast, and it gets there without any single stage doing heroic work. The catch is the word independent: layers that all share the producer’s blind spot do not multiply, they just repeat. That is the whole case for separating producer from checker, restated as arithmetic.
You can stop after Layer 1 and still be correct about layers of verification, just less complete.
The skill shift: from writing fast to reading well
This reshapes what makes an engineer valuable, and the change is uncomfortable for a lot of people because it demotes the skill they spent years sharpening.
The old prestige was in production. The person who could write the clever code fastest was the strong one. That skill is being commoditized, quickly. The new prestige is in judgment: the person who can read a diff and see the flaw, who can look at a plausible-looking function and feel that the concurrency is wrong, who can design the check that would have caught this class of bug for good. Reading critically is now the higher-value half of the craft, and it is a genuinely different skill from writing.
“Writing code fast was the flex for a decade. It is being automated. Reading code critically and designing the checks that make output trustworthy is the skill that is getting more valuable, not less, and it is the one most of us practiced least.
”
Reading well is harder to fake and slower to learn. It rests on things a model does not hand you: a deep model of the system, a memory of how this exact class of bug bit you before, a sense of which invariants actually matter. It is also the skill that decides whether your context is even set up to produce good output in the first place, which is why engineering the context an agent sees and reviewing what it produces are two ends of the same job. You curate what goes in, you verify what comes out, and the model does the fast part in the middle.
What to build and practice now
This is not a wait-and-see shift. Here is what actually moves the needle, starting this week.
Make verification independent of the producer
The single highest-value change. Stop letting the thing that wrote the code be the only thing that vouches for it. Write your critical tests against the requirement yourself, or have a separate agent generate tests from the spec without seeing the implementation. If a test and the code came from the same misunderstanding, you have coverage numbers and no actual verification.
Push wrongness into cheaper layers
Every error you can move from human review into a type, a test, or an invariant is an error that gets caught for free forever after. Tighten the types until illegal states will not compile. Add property-based tests for the invariants that must always hold. The goal is that by the time a human looks, the machine-catchable mistakes are already gone.
Stage your review by risk
Stop reviewing everything with the same intensity. Auto-merge the trivial and fully-covered. Reserve slow, careful, adversarial human reading for the irreversible and the security-sensitive. You have a fixed budget of good judgment. Spend it where being wrong is expensive, not on the config change.
Practice reading, deliberately
Reading code critically is a trainable skill and almost nobody trains it on purpose. Review agent output with the assumption that something is subtly wrong and your job is to find it, not to approve it. Ask what edge case is silently dropped, what invariant is quietly broken, what the fluent surface is hiding. The suspicion is the skill.
Instrument production as the last check
No verification catches everything, so make production observable enough that the thing that slips through is caught in minutes, not by a customer in a week. Good logging, alerting on the invariants that matter, and fast rollback are the final independent layer. Verification does not end at merge.
The payoff, stated plainly
The pull request that sat while everyone was busy writing the next feature was the tell. The constraint was never really the typing, and now that the typing is free the constraint is naked: it is trust. The engineers and teams who win the next few years are the ones who see that clearly and rebuild around it, treating verification as the product and generation as the cheap input to it.
Producing an artifact is close to solved. Being sure it is right is the open problem, and being the person or the system that can answer it is where the value went. Reorganize accordingly. The one who reads and judges and designs the checks is not the bottleneck. That person is the whole point.
