01 — The problem
Every team that ships code follows roughly the same loop. Someone writes code. They open a pull request. A reviewer looks at it. Once it passes review, it merges. For most of software history, the slow step was writing the code. Review was quick by comparison, because there wasn't much to review at once. The queue of open PRs stayed short almost by accident — code simply couldn't pile up faster than people could look at it.
AI-assisted coding breaks that accident. A developer working with an AI assistant can produce a working, review-ready PR in a fraction of the time it used to take. That sounds like a pure win. But writing code was never the bottleneck for the whole system — it was one stage in a pipeline, and the other stages didn't speed up. Review didn't get faster. Testing didn't get faster. The attention of the one senior engineer who understands the billing module didn't get faster.
So the PRs pile up.
A backlog of open PRs isn't just an untidy dashboard. It's a source of real, compounding cost:
- Reviewer fatigueSix PRs waiting for the same reviewer get a worse review than one PR waiting alone. Attention is finite, and it gets spread thin across the queue instead of focused on each change.
- Stale basesWhile a PR waits, other PRs merge ahead of it. The longer it sits, the further its starting point drifts from the current codebase, and the more likely it is to need a fiddly rebase before anyone can even look at it properly.
- Rework and re-reviewFeedback on a stale PR often means the author has to reconstruct context they've since forgotten, make the fix, and then wait in the queue a second time — for a second review of a change that should have been small.
None of this is a people problem. It's a systems problem. When one stage of a pipeline speeds up and the next stage doesn't, something has to give. The question is whether we choose what gives, or let it happen to us.
02 — The biscuit factory
An analogy from the factory floor
Picture a biscuit factory. The production line can bake and cut a thousand biscuits a minute. The wrapping machine downstream can only wrap eight hundred a minute. If the production line just runs flat out at its own top speed, biscuits pile up faster than they can be wrapped.
There are a few ways to handle this.
One: make the wrapping machine faster, or add more of them. This lets the oven run at full pace, and nothing upstream has to change. But the new machines cost money, and there's a question worth asking before you buy them: does this actually solve the problem, or just move it downstream? Can the shipping department load a thousand biscuits a minute onto vans? Can the retailers even shift them that fast?
Two: slow the production line down to eight hundred biscuits a minute. No pile-up, no staleness, no overflow. The oven can bake faster than that in isolation, but the pipeline's real maximum was never the oven's top speed — it's set by the slowest stage in the line, whichever one that turns out to be. Run at eight hundred and the whole system is finally working at its actual capacity, sustainably, rather than the oven's alone.
Three: build a bigger buffer. Let unwrapped biscuits pile up on a growing line of trays, and hope the wrapping machine eventually catches up. This works for a while, and it isn't nothing — a big enough buffer means the wrapping machine can keep running through a short gap, say while the ovens are being cleaned, without the whole line grinding to a halt. But it's a shock absorber, not a fix. However large the buffer is, it will fill if the wrapper can't match the oven's rate on average. A bigger buffer only buys time before option two anyway — production still has to come down to what the wrapper can sustain, or the pile takes up space, biscuits waiting around get stale, and the buffer eventually overflows regardless of how big it was.
Now suppose one wrapping machine jams, and capacity drops to five hundred a minute. A well-designed factory slows the oven down too, to match the machine that's actually struggling.
For that throttling to happen, the wrapping machine has to tell the production line how fast it can actually go, and the production line has to listen. That might be a snazzy automated sensor and control loop, or it might just be a human supervisor watching the jam light and turning a dial. Either way, that signal, flowing backward from the constrained stage to the stage feeding it, is what's called backpressure. It isn't a failure mode. It's the mechanism that keeps a pipeline with mismatched stages from falling over.
03 — Prior art
Backpressure isn't a new idea
This pattern shows up everywhere in systems that move things from a producer to a consumer. Three examples are worth knowing, because they show backpressure can be implemented in genuinely different ways — from a strict, continuous limit down to a simple, one-off “not right now.”
TCP flow control. Every time your computer receives data over the network, it sends back an acknowledgement — and tucked into that acknowledgement is a number: how much buffer space it has left, its “window size.” It's a running, constantly updated statement of capacity, not a one-off promise made at the start. A well-behaved sender never sends more than that window allows.
If a sender ignores it and sends more anyway, nothing dramatic happens: the receiver simply has nowhere to put the extra data, so it drops it, silently, exactly as it said it would. No error comes back. The sender only realises something went wrong when the acknowledgement it was expecting never arrives, and has to send that data again. The receiver isn't policing anyone. It just does precisely what it said it would do, and the space it doesn't have was never going to appear from wanting it to.
HTTP 429: Too Many Requests. A much blunter version of the same idea. Instead of a continuously updated number, the server just says no. Send it requests faster than it can handle and it replies with a 429 status code — sometimes with a “try again in N seconds” attached, sometimes not.
There's no fine-grained window here, no ongoing negotiation. It's a flat rejection after the fact, rather than a limit stated in advance. Coarse, but it works, and almost anyone who's called a rate-limited API has met it directly.
Pull-based streaming, like watching a video. Here the model flips entirely. When you stream a video, the server isn't blasting you the whole file as fast as your connection allows. Your video player has a buffer of limited size, and it only asks for the next chunk once there's room for it.
Skip forward to a point you haven't buffered yet, and you'll see the player pause and spin while it catches up — that's the buffer being empty and the player refusing to display what it doesn't have yet. Nothing is ever pushed faster than the player is ready to receive; the player is always the one asking. This is the same idea behind streaming APIs like Node.js streams, where a “high water mark” marks how full the consumer's buffer is allowed to get before it stops asking for more.
All three achieve the same outcome — the fast side never permanently outruns the slow side — by genuinely different means. One is a continuous, precise limit, silently enforced. One is a blunt rejection after the limit's already been crossed. One is the consumer never asking for more than it can hold in the first place. Worth keeping all three in mind, because a dev team can borrow from any of them.
04 — Applying it
Applying backpressure to a dev team
Here's the trap. AI coding assistants make it tempting to treat “not currently generating code” as wasted time — an opportunity cost, tokens left on the table, a feature sitting untouched in the backlog while you could be building it right now. But a PR that's finished and sitting in a review queue behind four other PRs isn't delivering value. It's inventory. And like the unwrapped biscuits, it's not neutral inventory quietly waiting its turn. It's actively getting worse the longer it sits:
- Every PR that merges ahead of it pushes its base further out of date, raising the odds of a conflict-riddled rebase before review can even start.
- The author's own memory of why they made each decision fades, so review comments that should take five minutes to address instead require reconstructing context from scratch.
- A reviewer facing a stack of stale PRs reviews faster and shallower than one facing a single fresh PR. The backlog doesn't just delay throughput — it degrades the quality of the review itself, the same way a rushed wrapping machine starts producing sloppier wrapping.
Producing more code just throws more biscuits at a wrapping machine that's already behind.
So what should actually throttle?
The honest answer is: not gut feeling. An individual developer, heads-down with an AI assistant, has no way to see how deep the review queue has become — same as the factory worker at the oven can't see the wrapping machine from the far end of the floor. If throttling depends on each person independently sensing “I should probably slow down,” it will happen too late, unevenly, or not at all. Backpressure has to be a visible signal, not a vibe.
That points to a concrete mechanism, borrowed straight from Kanban: a work-in-progress limit, set on the review queue rather than on any individual. Cap the number of PRs open against a given reviewer or team at once. When the cap is hit, that's the backpressure signal — same as the wrapping machine's jam light. It doesn't matter how fast the oven could run. Nobody starts a new PR until an old one clears.
When is it OK to produce more code?
When the review queue has room. That's it. That's the whole rule, and it's worth resisting the urge to make it more complicated than that.
When should we hold back?
When the queue is at or near its cap. Not “when I feel behind on review” — by the time it feels that way, the backlog has usually already done its damage.
What do we do with the slack time?
This is where it's worth remembering that “slow down” doesn't mean “stop.” The oven doesn't have to sit cold while the wrapping machine catches up — but running it flat out on more biscuits is exactly the wrong move. The useful moves are the ones that relieve pressure elsewhere in the pipeline rather than adding to the pile:
- Review someone else's PR. The most direct way to unblock the queue is to help clear it — the one item on this list that actually drains the bottleneck rather than just staying clear of it.
- Use the AI assistant upstream of the bottleneck instead of downstream of it. Spend the time on spec work, design docs, or breaking down the next backlog item, so that when review capacity frees up, the next PR is better-shaped and faster to review — rather than just being next in an identical queue.
- Do the work that never produces a PR at all. Research a tricky problem before committing to an approach. Read up on a new library or pattern. Dig into an analysis that informs a future decision. Invest in your own skills. None of this touches the review queue, and all of it makes the next PR you do write better.
Notice what's missing from that list: writing more tests, or paying down tech debt. Both are real, valuable work — but both land as a new PR in the same review queue we're trying to drain. They're still biscuits. Slack time is well spent on work that stays out of the pipeline until the queue has room again, or that directly shrinks the queue rather than adding to it.
None of this is about working less. It's about recognising that the production line was never the whole factory. A faster oven, pointed at a wrapping machine that's already behind, doesn't make more biscuits reach the shelf. It just makes a bigger mess in the middle.
Rate is one lever. It's not the only one.
05 — The other lever
Shrink the biscuits
Everything so far has been about rate — how many PRs go in, how many come out, how to throttle the gap between them. But there's a second variable sitting underneath all of it, and it's easy to miss because the factory analogy hides it: biscuits are all roughly the same size. PRs aren't.
A WIP limit caps how many PRs are open at once. It says nothing about how big each one is. Two teams can both run “max 3 PRs in review” and have wildly different queues — one made of small, focused changes that clear in an hour, the other made of sprawling PRs that each take a reviewer half a day to get through. Same limit, same number on the dashboard, completely different amount of pressure on the system.
That's because review time doesn't scale in a straight line with PR size. A fifty-line change is something a reviewer can hold in their head all at once. A five-hundred-line change usually isn't — it takes real effort to build a mental model of what's going on, and that effort gets paid again every time the reviewer is interrupted and has to reload it. Ten small PRs reviewed separately can genuinely cost less total time than one big PR ten times the size.
Smaller PRs also attack the harms named right back in section 1. A PR that's open for two hours barely has time to go stale. A PR that's open for two days has drifted noticeably from the base it started against. Small PRs move through review quickly almost by construction, which means they spend far less time exposed to the rebase-and-rework problem in the first place.
There's also a quieter benefit: small PRs make the queue itself trustworthy. A WIP limit is only a meaningful signal — a real jam light — if the units it's counting are roughly comparable in size. “Three PRs open” means very little if one of those three is secretly five features stapled together. Keep PRs small and uniform, and the count on the board actually means what it says.
None of this is new advice. Build the feature behind a flag and land it in slices. Let a changeset be deep or wide, but never both.
This matters more, not less, with AI-assisted coding. AI doesn't just make code faster to write — it makes extra scope almost free to add. Asking an assistant to also handle the edge case, also refactor the neighbouring function, also add the related feature costs the author almost nothing in the moment. So the natural failure mode isn't just “more PRs, more often.” It's one PR quietly ballooning to do five things at once, because nothing in the process pushed back on that while it was being written. That's the discipline AI-assisted coding makes hardest to hold onto — and, for exactly that reason, the one most worth insisting on.
Each one clears in under an hour. The column keeps moving.
Same “3” on the dashboard. The bottom card alone could sit here for days.
Rate and size are two separate dials, and they compound. Throttle how many PRs are in flight, and keep each one small — do both, and the queue stays short and legible. Do only the first, and you can still end up with a review backlog in everything but name.
06 — A new way to slice
Plan backwards, not forwards
Small, incremental changes have always been the goal. Nobody needed convincing of that. The trouble was never the advice — it was doing it.
Chunking a large piece of work means deciding how to slice it before you've actually built it. That's the hard part. You make your best guess at chunk one, chunk two, chunk three, and keep going — and it's only by chunk twenty, once the real shape of the problem has revealed itself, that you realise chunk three was built on the wrong foundation. That's not carelessness. Genuine foresight, before you've built the thing, is just hard to come by.
AI-assisted coding offers a way around this, and it works by flipping the order. Instead of planning the slices first and building second, build the whole changeset first. Let the AI assistant produce the entire thing, mistakes and all — not as a PR, just as a working draft. Only once it exists, and its real shape is fully visible, ask the AI a different question: if we'd known this shape from the start, how would we have built it incrementally?
That's a retrospective re-slice, done with full hindsight instead of blind foresight. The AI takes the finished work and decomposes it into a coherent, dependent train of smaller changesets — the sequence you'd have chosen on day one, if only you'd known on day one what you only found out by the end.
None of this is unfamiliar. Test-driven development already works this way at a small scale — you write a test, get it wrong, revise, and treat the misstep as a normal part of discovery rather than a failure. That's expected, and healthy. The difference is what we let cross into the shared history. A TDD cycle's false starts stay local, on one machine, in one head. A retrospective re-slice does the same thing at the scale of the whole changeset — what reaches the review queue looks as if it had been built by someone with perfect foresight from the start.
This isn't licence to open the whole train as five PRs at once. That just swaps one big backlog item for five small ones sitting in the same queue. The gate from section four still applies: peel off the first chunk, open it as a PR, and wait. Only once the review queue has room does the next chunk go up. The train gets released one link at a time, at the pace the queue can actually take.
What's new here isn't the value of small PRs. It's that re-planning with full hindsight, always the better version of chunking, used to be too expensive to redo after the work was already done. AI makes the redo cheap enough to do every time.
07 — Recap
What this comes down to
- AI-assisted coding makes producing PRs much faster, but it doesn't make reviewing them any faster. The gap between the two is a growing backlog, not a one-off surge.
- An open PR isn't neutral inventory quietly waiting its turn. The longer it sits, the more it costs — stale bases, forgotten context, shallower review.
- Backpressure is a signal fed back from the constrained stage to the one feeding it, so the producer throttles to match — rather than piling up work the next stage can't absorb.
- Real systems implement that signal in different ways: a continuous, silently-enforced limit (TCP), a blunt rejection after the fact (HTTP 429), or a consumer that simply never asks for more than it can hold (streaming).
- For a dev team, the equivalent is a WIP limit on the review queue: cap how many PRs can be in review at once, and don't open a new one until the cap has room.
- Rate isn't the only lever. PR size is a second, independent one — small PRs review faster, go stale less, and make the WIP-limit count itself trustworthy.
- AI makes this discipline harder to hold onto, not easier — extra scope is almost free to add, so the natural failure mode is one bloated PR instead of several small ones.
- AI also offers a way out that wasn't practical before: build the whole changeset first, then ask it to retrospectively re-slice the finished work into the incremental train you'd have written with full hindsight.
- That train still isn't opened all at once. Each chunk goes up only once the review queue has room for it — the WIP limit and the retrospective slice work together, not instead of each other.