← The ADLC library
Foundations · 5

The bottleneck moved, from typing to verifying

Speeding up one stage of a pipeline does not speed up the pipeline. It moves the queue. Here is where the queue went, and why verification is a much harder constraint than production ever was.

There is a moment that happens on agent-heavy teams about three months in. Somebody looks at the numbers and says: we are shipping twice as many pull requests and roughly the same number of features. Where is it going?

The answer is visible if you stand in the right place. Go and look at the pull request queue on a Thursday afternoon. Count how many are open. Count how many have been open more than two days. Then look at who is expected to review them and how many other things that person is doing.

The work did not disappear. It moved into a queue in front of the one stage nobody made faster.

This is the oldest result in operations and it survives every reframing anyone has ever attempted. Improve a non-bottleneck stage and you do not improve throughput. You increase work in progress in front of the bottleneck, which makes everything worse in ways that are diffuse and hard to attribute: longer cycle times, more context switching, more stale branches, more merge conflicts, more things half-done. The team feels busier and delivers the same amount. Everyone reports feeling like they are working harder, which is true, and the conclusion drawn is usually that they need to work harder still.

Naming the constraint precisely

“Verification” is too vague to act on, so let me break it into the four things it actually contains, because they have different fixes and teams routinely attack the wrong one.

Does this do what the ticket said? Cheap in principle, almost never done directly. It requires the ticket to say something checkable. Most tickets do not, so this check silently becomes “does this look like a reasonable response to the ticket,” which is a different and much weaker question.

Does it break anything else? This is what your test suite is for, and it is the one part of verification that genuinely scales, because machines do it. If your suite is good, this is nearly free. If your suite is thin, this cost lands entirely on humans and on production.

Is it the right shape for this system? Architecture, layering, convention, whether this introduces a pattern you will regret. This is expensive, requires system-level context, and is exactly the kind of judgement that does not parallelise across a growing queue.

Was this the right thing to build at all? Almost never checked at review time, because by then the change exists and killing it feels wasteful. This check belongs upstream and mostly does not happen anywhere.

Notice the distribution. One of the four scales with machines. Three scale with senior human attention, which is the least elastic resource any engineering organisation has. You cannot hire it quickly, you cannot borrow it from another team, and it is already fully committed.

Does it do what the ticket said?

Cheap in principle. Requires the ticket to say something checkable, so in practice it silently becomes a judgement call.

Does it break anything else?

The test suite. The one component that genuinely scales, because machines do it.

Is it the right shape?

Architecture, layering, convention. Expensive, needs system-level context, cannot be delegated downward.

Should this exist at all?

Almost never asked at review time, because the change already exists and killing it feels wasteful.

One quadrant scales with machines. Three scale with senior human attention, which is the least elastic resource an engineering organisation has.

That is the constraint. Not “review.” Specifically: judgement-heavy verification performed by people who are scarce and already busy.

Why this constraint is harder than the last one

Production had a property that made it manageable: it was divisible. Two engineers wrote roughly twice as much code as one. Not exactly, Brooks was right about the coordination cost, but close enough that scaling by hiring worked for decades. The whole industry’s growth model was built on that divisibility.

Verification is much less divisible, for a few reasons that are worth separating.

It requires context that takes months to acquire. A new senior hire can write useful code in week two and cannot usefully review architectural fit until month four, because the thing being checked is conformance to a system they do not yet know.

It has poor parallelisation across a set. Ten changes reviewed by ten different people, each competently, does not equal one person reviewing all ten. The coherence question (do these ten changes together take us somewhere sensible) is only visible to someone holding all ten, and that person does not exist in the parallel arrangement.

And it degrades silently under load. This is the property that makes it genuinely dangerous. When production is overloaded, output drops and everyone can see it. When verification is overloaded, output does not drop. Approvals keep flowing at the same rate. What drops is the depth behind each one, and there is no instrument on it. A team can go from real review to rubber-stamping over about six weeks without a single meeting acknowledging that it happened, because every individual approval felt defensible at the time.

The failure mode of an overloaded verification stage is not a slowdown. It is a quiet drop in the value of the approval, and you find out months later through an incident.

Production overloaded

  • Output visibly drops
  • Everyone can see it
  • Divisible: two people write twice as much
  • New hire useful in week two
  • Failure is loud and immediate

Verification overloaded

  • Approvals continue at the same rate
  • Their value quietly falls
  • Poorly divisible: ten reviewers miss coherence
  • New hire useful at month four
  • Failure surfaces months later as an incident
The old constraint announced itself. The new one does not, which is what makes it dangerous rather than merely expensive.

The seductive wrong fixes

Four responses that feel right and mostly are not.

Hire more reviewers. Fights the divisibility problem head-on and loses, slowly, at high cost. It also takes two quarters to have any effect, which means the decision gets made in a crisis and lands after the crisis has changed shape.

Require more approvals per change. Intuitively this adds rigour. In practice it distributes responsibility until nobody holds it, and diffusion of responsibility is a well-documented way to make groups less careful, not more. Two required approvals on a queue that is already three times too long produces two shallow reviews instead of one real one.

Slow the agents down. Occasionally correct, honestly. If you cannot verify what you are producing, producing less is a legitimate response and is better than shipping unverified work. But it is a tourniquet, and teams that reach for it usually have not tried the thing that actually works.

Add more process. A checklist in the pull request template, a mandatory field, a gate. Process is a way of encoding attention, and attention is the thing you are short of. Adding steps to a stage that is already the constraint makes the constraint tighter. This is the single most common mistake and it is almost always made with good intentions by someone who has correctly diagnosed the problem and then reached for the only lever they know.

What actually relieves it

Two things, and they are unequal in importance.

Move the check earlier, and make it mechanical. The reason review is so expensive is that it is answering questions that were left open upstream. If the ticket had said, precisely, what condition constitutes success, then “does this do what was asked” becomes a check rather than a judgement, and it can be run without a human. This is what machine-verifiable acceptance criteria are actually for, and it is why they are the centre of the ADLC rather than a nice-to-have. They are not documentation hygiene. They are the only known way to remove a category of work from the constrained stage rather than shuffling it around inside it.

The cost is real and I will not pretend otherwise. Writing “the export endpoint returns 202 with a job id, and the job produces a CSV containing every non-deleted record for the requesting tenant, and requests from a different tenant get a 404” takes longer than writing “users can export their data.” Maybe ten minutes longer. It also happens once, upstream, by someone who is not the constraint, and it removes an ambiguity that would otherwise be resolved silently by an agent and then discovered expensively three weeks later.

That is the whole trade. Ten minutes of unpleasant precision from a product person, in exchange for removing an unbounded amount of downstream judgement from your scarcest people. It is a good trade and teams resist it fiercely, because the cost is visible and immediate and the benefit is invisible and deferred.

Precision upstreamTen minutes from a product person, stating what done means in checkable terms.
Mechanical checkAnswers "does it do what was asked" without a human.
Uneven human attentionWhat remains is architecture, fit and coherence, routed to the few who can judge it.
Constraint relievedNot by adding reviewers, which fights divisibility and loses.
The only two moves that work. Both relocate the check rather than adding capacity at the constrained stage.

Spend the remaining human attention unevenly. Once the mechanical checks handle “does it do what was asked” and “does it break anything,” what is left is architecture, fit and coherence. Those genuinely need people. So make sure people are looking at the changes where those questions have teeth, and not at the forty renames. This requires knowing which changes are which, which requires every change to be linked to something that says what it is for, which is the same plumbing as everything else in this series. Reading ground truth from the repository and tying each pull request to its ticket is not primarily about status reporting. It is about being able to route attention.

A note on the second-order effect

There is a compounding version of this that is worth watching for.

When verification is the constraint and it is under-supplied, teams unconsciously start selecting work that is easy to verify. Not deliberately, and nobody says it out loud. But the change that is hard to review sits in the queue longer, gets more pushback, and eventually gets simplified or dropped. Over a few quarters the team’s output shifts toward work that is legible to a rushed reviewer.

That is a real strategic distortion and it is invisible from inside. The team is shipping. Velocity looks fine. The work has quietly become smaller and more incremental because the constraint has a taste, and you have been feeding it.

Where this breaks down

Time to argue against myself, and there is more to argue here than usual.

The constraint may not be verification for you. I have written this as though it is universal and it is not. For plenty of teams the actual constraint is deploy frequency, or an approval process outside engineering, or waiting on another team, or a product function that cannot decide what it wants. Speeding up code generation moves the queue to whatever is genuinely slowest, and that varies enormously. Go and look at where work is actually sitting before accepting my answer. The method (find the queue) is more reliable than my conclusion.

Machine-verifiable criteria do not fit all work. Exploratory work, research spikes, design-led changes, performance investigations, anything where the goal is to learn rather than to deliver: these resist mechanical criteria, and forcing them into that shape makes the criteria absurd and the work worse. A team that mandates verifiability everywhere will quietly stop doing the work that most needs human judgement, which is a worse outcome than a messy board.

“Just write better criteria” is easy to say and organisationally hard. It requires the person writing tickets to think harder, and that person usually does not report to you, does not feel the downstream pain, and has their own pressures. Every failed attempt at this I am aware of failed on that boundary rather than on the technique. If you cannot get product to write checkable criteria, the engineering-side fixes available to you are much weaker, and honest advice is to solve the organisational problem first rather than route around it with tooling.

And moving the constraint upstream just relocates it again. If specification becomes the gate, then the person who writes specifications becomes the new bottleneck, and everything I said about verification degrading silently under load applies to them too. There is no configuration with no constraint. There is only choosing which constraint you would rather have, and I would rather have one at the stage where being wrong is cheapest to fix.

The takeaway

Making code generation fast did not make delivery fast. It moved the queue in front of verification, which is a harder constraint than production ever was: it needs context that takes months to build, it does not parallelise well across a set, and it degrades invisibly under load so you cannot see it failing.

The fix is not more reviewers or more process at the constrained stage. It is moving as much of the check as possible upstream into criteria a machine can evaluate, and then routing the irreducible human judgement at the changes that actually need it.

The next piece steps back from the pipeline to the principle underneath all of this: what “ground truth” actually means, why source control is the only artefact in your organisation that qualifies, and what follows once you take that seriously.