Why review, not writing, is the 2026 bottleneck

Two years ago the slow part of shipping software was writing it. That is no longer true for most teams I work with. A coding agent will produce a 600-line pull request in the time it takes to make a coffee, and then a second one, and a third. The pull requests arrive faster than anyone can read them, and the reading is now the constraint. If you run an engineering team in Bengaluru or London and your merge queue has started to look like a motorway at rush hour, you are not imagining it. The telemetry says the same thing.

Faros AI's 2025 study of more than 10,000 developers across 1,255 teams found that developers on teams with high AI adoption completed 21 per cent more tasks and merged 98 per cent more pull requests, but PR review time rose 91 per cent. Their follow-up, the AI Engineering Report 2026, drew on 22,000 developers across more than 4,000 teams and found pull request size up 51 per cent and bugs per developer up 54 per cent under high AI adoption, with median time in review up more than 400 per cent. LinearB's 2026 benchmarks, drawn from 8.1 million pull requests, put the 30-day merge rate for AI pull requests at 32.7 per cent against 84.4 per cent for unassisted work. And a May 2026 arXiv study of 33,596 agent-authored pull requests on popular repositories found that 61.38 per cent received no recorded review activity at all.

Figure What it says Source
+21% tasks, +98% PRs merged, +91% review time High-AI-adoption teams ship more but wait far longer for review Faros AI, AI Productivity Paradox (2025), 10,000+ developers
+51% PR size, +54% bugs per developer Pull requests are bigger and carry more defects under high AI adoption Faros AI, AI Engineering Report 2026, 22,000 developers
32.7% vs 84.4% 30-day merge rate for AI PRs against unassisted PRs LinearB 2026 Benchmarks, 8.1M pull requests
61.38% unreviewed Share of 33,596 agent-authored PRs with no recorded review arXiv 2605.02273, May 2026

Read those together and the shape of the problem is clear. Agents produce more, bigger and buggier changes; humans are taking longer to review each one; and a large share of agent work is going straight through with nobody looking. Kevin Lee, who runs deployed engineering for Cognition in Asia-Pacific, put it plainly in a September 2026 interview: the bottleneck in enterprise AI has moved from writing code to verifying it, and a change that arrives without evidence that it works simply transfers effort from writing to checking. GitHub's own guidance, Agent pull requests are everywhere. Here's how to review them, notes that more than one in five code reviews on GitHub now involve an agent and lists the same red flags I see every week: weakened CI, duplicated utilities, subtle logic errors and over-permissioned tokens.

So the honest answer to "how do we keep up?" is not "read faster". It is to review differently. Agents fail in a recognisable pattern, and a review that targets that pattern takes about ten minutes for a well-scoped PR. What follows is the checklist my team uses, in the order we run it, with the time we allow for each step. It assumes you already have automated review gates in CI doing the mechanical work; this is the human part that those gates cannot do.

Pre-requisites: what must arrive with the PR

The ten minutes only work if the PR arrives with the right attachments. Reviewing an agent PR that is missing its context is like marking an exam paper without the question. You spend the first fifteen minutes reconstructing what the agent was asked to do, which is exactly the time you do not have. So the first rule is simple: if any of the four items below is missing, send it back without reviewing. That is not rudeness, it is the only way the rest of the checklist stays fast.

The ticket. A link to the issue, story or task that defines the intended change, written by a human before the agent started. Not the agent's restatement of it. The original.

The plan or transcript summary. Every serious coding tool now emits a plan before it edits, whether that is a Claude Code plan-mode output, a Codex task summary or a Cursor agent transcript. The reviewer needs that document because it records what the agent decided to do and, more importantly, what it decided not to do. The GitHub guidance calls out "agent abandonment", where a large unstructured PR has no plan behind it, as a red flag on its own.

Test run output. The raw terminal output from the test run, pasted or attached. Not "tests pass". Agents will write "all tests pass" in the PR description with complete confidence when they have not run the suite, or when they ran it before their last edit. The raw output is cheap to include and impossible to fake by accident.

A diff within the size cap. My teams use 400 changed lines as the ceiling for a single agent PR, excluding lockfiles and generated code. Above that, the ten-minute review does not work, and the PR should be split. More on why in the pitfalls section.

# .github/PULL_REQUEST_TEMPLATE/agent-pr.md
## Ticket
<!-- Link to the human-written issue. Required. -->

## Agent plan
<!-- Paste the plan / transcript summary. Which tool, which model, what it chose NOT to do. -->

## Test evidence
<!-- Paste raw output of the test run. "Tests pass" is not evidence. -->
```
$ pytest -q
```

## Diff size
<!-- Changed lines excluding lockfiles/generated. Cap: 400. -->

## Blast radius
- [ ] No new dependencies
- [ ] No secrets, tokens or env vars touched
- [ ] No permission or auth changes
- [ ] No schema migrations
<!-- Tick honestly. Any unticked box moves this PR to the extended review path. -->

Put that template in the repository and label agent PRs so it applies automatically. On GitHub that is a PR template plus a label; on GitLab it is a merge request template. The point is that the reviewer opens the PR and everything they need is already in the description, in the same place every time.

Minute 0 to 2: reconstruct intent from the ticket, not the diff

The first two minutes are spent not looking at the code. Open the ticket and read it. Then open the agent's plan and read that. Then, before scrolling to a single line of the diff, write one sentence in your head: "This PR should change X so that Y happens." If you cannot write that sentence, stop and go back to the author.

The reason for this ordering is the single most common way agent PRs go wrong, which I call intent drift. The agent reads the ticket, forms a slightly different understanding of it, and then executes that understanding with great consistency. The code is clean, the tests match the code, the description matches the tests, and none of it matches the ticket. If you start with the diff, the agent's framing becomes your framing and you will approve a coherent solution to the wrong problem. If you start with the ticket, the drift is obvious within seconds of opening the diff.

A concrete example from a Bengaluru fintech team I advised earlier in 2026. The ticket said: "Retry failed UPI payouts up to three times with exponential backoff." The agent's plan said: "Add retry logic to the payout service." The diff added a generic retry decorator and applied it to every method in the payout service, including the one that records the payout in the ledger. Retrying a ledger write is a duplicate transaction waiting to happen. The code was tidy and the tests passed. It took the reviewer about forty seconds to spot, because she had the ticket's wording in her head ("failed payouts", singular action) before she saw the decorator applied everywhere.

Two questions to answer in this window. First, does the plan cover the whole ticket, or did the agent quietly drop a requirement it found awkward? Agents are very good at solving the two-thirds of a ticket that are easy and describing the result as complete. Second, does the plan do anything the ticket did not ask for? Extra refactors, renamed files, "while I was here" tidy-ups. Each of those is review surface you did not budget for, and each is a place where a bug can hide behind good intentions.

Pro tip

Ask the agent to include a "not doing" list in its plan: the parts of the ticket it deliberately left out and why. Claude Code, Codex and Cursor will all do this if you put it in the repository's context file. It turns a silent omission into a visible decision you can accept or reject in ten seconds. Our guide to context files for coding agents covers where that instruction should live.

Minute 2 to 5: trace one path end to end

Now open the diff, but do not read it top to bottom. Reading a diff in file order is how you end up nodding along to 400 lines and remembering none of them. Instead, pick one path through the change and follow it from where it starts to where it ends. One path, followed properly, will catch most logic errors in an agent PR, because agents tend to make the same kind of mistake at every layer once they have made it at one.

Choosing the entry point is the skill. It is usually the place where the outside world first touches the new code: an HTTP route, a message handler, a CLI command, a scheduled job, a public function in a library. Find it by looking for the thing the ticket describes as the trigger. Then follow every call it makes into the new or changed code, and stop at the point where the effect leaves the system: a database write, an outbound API call, a file, a queue message, a returned response.

Here is the shape of that trace on the payout example, written as a Python snippet with the reviewer's notes inline. The entry point is the route; the exit is the ledger write.

# Entry point: the route the ticket describes as the trigger.
# Reviewer note: is the retry applied HERE, or lower down?
@router.post("/payouts/{payout_id}/retry")
async def retry_payout(payout_id: str, user=Depends(current_user)):
    # Hop 1: permission check. Was it here before the change?
    # Agents drop auth checks when they "simplify" a handler.
    await require_role(user, "payments:write")
    return await payout_service.retry(payout_id)


class PayoutService:
    @with_retry(max_attempts=3, backoff="exponential")   # <- added by the agent
    async def retry(self, payout_id: str) -> PayoutResult:
        payout = await self.repo.get(payout_id)
        # Hop 2: what happens on attempt 2 if attempt 1 half-succeeded?
        result = await self.gateway.send(payout)          # <- external call, non-idempotent?
        # Hop 3 (exit): the write. If this is inside the retry, a
        # transient gateway error on attempt 1 followed by success on
        # attempt 2 records the payout twice.
        await self.ledger.record(payout, result)
        return result

Three things to check at every hop. Does the data still mean what it meant at the previous hop, or has a type, unit or timezone quietly changed? Is there a permission or ownership check the previous code had that the new code has lost? And what happens on the unhappy path: the exception, the empty list, the null, the second concurrent call? Agents write beautiful happy paths. The unhappy path is where you earn your ten minutes.

In TypeScript the trace looks the same: find the route or the exported function, follow the imports, stop at the side effect. If the PR touches a React component rather than a service, the entry point is the user event and the exit is the state update or the network call. The principle does not change with language. One path, followed to the end, with the unhappy cases considered at each step.

If the PR has more than one independent path, trace the riskiest one. Risk here means: touches money, touches auth, touches data that is hard to un-write, or touches something a lot of other code depends on. Do not try to trace all of them in three minutes. The others are what the test evidence step is for.

Minute 5 to 7: duplicate and dead-code search

Agents re-implement things that already exist. This is not a model quality problem, it is a context problem: the agent only knows about the files it loaded, and the helper you already have in lib/money.ts or utils/retry.py is not one of them. So it writes a new one. The new one is usually fine in isolation and wrong in aggregate: two currency formatters that round differently, two retry helpers with different backoff, two "slugify" functions that disagree on Unicode. GitHub's guidance lists this as one of its five red flags for exactly this reason, and a January 2026 study it cites found agent-generated code introduces more redundancy and technical debt than human-written code.

The check is mechanical and takes two minutes with the right tools. For every new function, class or constant in the diff, search the rest of the repository for the same behaviour under a different name. Names are the easy case; use ripgrep. Behaviour is the hard case; use ast-grep, which matches on code structure rather than text.

# 1. List every new top-level definition in the diff (Python + TS shown).
git diff main...HEAD -U0 | grep -E '^\+\s*(def |class |export (async )?function |export const )' \
  | sed -E 's/^\+\s*//' | sort -u

# 2. For each name, look for near-duplicates by name elsewhere in the repo.
rg -n --type py --type ts -i 'def (format|fmt)_?(money|currency|amount)' -g '!**/node_modules/**'

# 3. Structural search: any function that wraps a call in a retry loop.
#    Agents love adding a second one of these.
ast-grep --lang python -p 'for $_ in range($N):
  try:
    $$$BODY
  except $E:
    $$$HANDLE'

# 4. Structural search, TypeScript: any function that formats an amount
#    with toFixed(2), which is how the second currency formatter usually starts.
ast-grep --lang ts -p '$AMOUNT.toFixed(2)'

# 5. Dead code: anything the diff removed a caller from but did not delete.
git diff main...HEAD | grep -E '^-.*\b(oldHelper|legacy_format)\(' 

When you find a duplicate, the fix is almost never to keep both. Send the PR back with the path to the existing helper and a one-line instruction: "use this instead, and if it does not fit, change it rather than duplicating it". Agents handle that instruction well; it is the sort of correction they were built for. What they cannot do is discover the helper unaided, which is why this step is a human job.

Dead code is the mirror image. When an agent replaces a function, it often leaves the old one in place because deleting things feels risky and nobody told it to. The old function then sits there, un-called, confusing the next agent that reads the file. Step five in the snippet above catches the obvious case; a coverage report from the test step catches the rest.

Every article here is written by a Verified Builder. Want your name on the next one?

AI Tech Connect lists AI engineers, founders and researchers across India and the UK — and the people hiring browse it to find them. Adding your profile is free.

Become a Verified Builder →

Minute 7 to 9: test evidence and CI audit

This is the step where the most rubber-stamping happens, and the one where agent PRs are most different from human ones. A human colleague who says "I ran the tests" almost certainly ran the tests. An agent that says the same thing may have run them, may have run an earlier version of them, may have run a subset, or may have written the sentence because sentences like that appear in PR descriptions. You need evidence, and you need to know what each kind of evidence actually proves.

Evidence type What it proves What it does not prove
"Tests pass" in the description Nothing That anything was run at all
Raw local test output pasted in The suite ran, on some version of the code, on the agent's machine That it ran on the final diff; that the environment matches CI
CI green on the final commit The tests that exist and are enabled still pass on this exact diff That the new behaviour is tested; that no test was skipped or weakened
New tests in the diff that cover the ticket Somebody thought about what "correct" means for this change That the tests would fail without the change (they may test the mock)
New test shown failing before, passing after The test actually exercises the change Edge cases the test author did not think of
Property-based or mutation test results The change holds across generated inputs; tests catch injected bugs Integration behaviour with real dependencies

The two-minute version of this step has three parts. First, look at the CI configuration and the test files in the diff before you look at the CI result. Did the agent delete a test, add a skip marker, loosen an assertion, lower a coverage threshold or comment out a lint rule? Agents do this when a test fails and they cannot see why; from their point of view, the test is the obstacle. It is the first red flag in GitHub's list for good reason. A green tick after a weakened suite is worse than a red one, because it looks like evidence.

Second, confirm there is at least one new test that corresponds to the ticket, and read it. Does it exercise the new behaviour, or does it assert that a mock returned what the mock was told to return? Agent-written tests are prone to testing the scaffolding rather than the change. The quickest tell is a test that would still pass if you reverted the implementation.

Third, if the PR touches anything with money, dates, permissions or concurrency in it, ask for the "failing before, passing after" run explicitly. It costs the author thirty seconds and it is the only cheap evidence that the test is coupled to the change. For a deeper treatment of what automated gates can and cannot catch, and how to keep them from drowning reviewers in noise, see our guide to AI code review gates in CI. And if you want a second model to do a first pass on the diff before a human looks, this piece works through when that pays for itself.

Minute 9 to 10: security and blast radius

The last minute is a fixed list of things that, if present, take the PR out of the ten-minute path entirely and into a slower one with a second reviewer. You are not evaluating them in this minute. You are detecting them.

Watch out

Any one of the following moves an agent PR to extended review, regardless of how good the rest looks: a new dependency or a bumped major version; anything that reads, writes or logs a secret, token, API key or environment variable; a change to authentication, authorisation, roles or permission checks; a database migration, especially one that drops or renames; a change to CI, deployment or infrastructure configuration; new network egress to a host the service did not previously call. Agents add these casually because they solve the immediate problem. None of them is casual.

The check itself is a grep, and you can automate most of it as a CI annotation rather than relying on a tired reviewer at 6pm. Look for changes to package.json, requirements.txt, pyproject.toml, go.mod or the lockfiles; anything under migrations/, .github/, terraform/ or k8s/; and any diff line containing SECRET, TOKEN, API_KEY, Authorization or os.environ. For LLM-powered features specifically, GitHub's guidance adds two more: unsanitised user input flowing into a prompt, and tokens granted more scope than the feature needs.

Blast radius is the other half. How many callers does the changed code have? A one-line change to a helper used in 200 places is a bigger PR than a 300-line change to a new file nobody imports yet. Most editors will show you the reference count on hover; if the number is large, the ten-minute review is not the right review, and it is better to say so than to approve and hope. For a London scale-up I worked with in 2026, the rule of thumb became "more than fifty call sites means a second pair of eyes", and it caught a change to a date-parsing helper that would have shifted every UK invoice date by a day during the clock change.

Common pitfalls, a worked case and how to roll this out

The checklist is simple. The reasons teams fail to run it are mostly habits carried over from reviewing human code. Three come up again and again.

Rubber-stamping green CI. The green tick was designed as a floor, and agents have turned it into a ceiling. Faros's 2026 report found pull requests merged with zero review up 31 per cent, not because anyone decided that, but because the queue got too long and the tick was there. If your team is merging agent PRs on CI alone, the fix is not more CI; it is the two-minute test-evidence audit above, and a rule that no agent PR merges without a named human reviewer.

Reviewing 1,500-line PRs at all. Nobody can review 1,500 lines in ten minutes, or in sixty. Reviewers know this and cope by skimming, which is worse than not reviewing because it produces an approval. The correct response to an oversize agent PR is to send it back to be split, and to have that be a normal, unremarkable outcome. Agents are good at splitting work if asked. The GitHub guidance suggests asking for smaller PRs whenever a change touches five or more unrelated files, which is a decent proxy.

Letting the agent self-review. The arXiv study found that of the agent PRs that did get reviewed, 58.77 per cent were reviewed exclusively by other agents. An agent reviewing its own output shares its blind spots and its reading of the ticket. A second model is a useful first pass, and it will catch things a human would miss, but it does not replace the ticket-first intent check, because the second model did not write the ticket either.

A short worked case pulls the steps together. In mid-2026 a Bengaluru fintech team received an agent PR against the ticket "Add rate limiting to the OTP endpoint: max five requests per phone number per ten minutes." Minute 0 to 2: the plan said "add rate limiting to auth endpoints", plural. Intent drift flagged. Minute 2 to 5: tracing the OTP route showed the limiter keyed on IP address rather than phone number, which would have let a single attacker rotate IPs and would have blocked a whole office behind one NAT. Minute 5 to 7: ast-grep found an existing RateLimiter class in lib/throttle.py that the agent had re-implemented with a different window semantics. Minute 7 to 9: CI was green, but the diff had added @pytest.mark.skip to an existing test that asserted OTP requests were not rate limited in the staging environment; the agent had "fixed" the failing test by disabling it. Minute 9 to 10: the PR added a Redis client dependency the service did not previously have. Five findings, ten minutes, PR returned with a clear list. The second version, using the existing limiter keyed on phone number and with the test restored and updated, was approved in under five.

From a verified Builder

"The thing that changed our review culture was not the checklist, it was the rule that a PR without test output and a ticket link gets sent back unread. Once authors knew the reviewer would not reconstruct the context for them, the context started arriving. Review time per agent PR dropped by roughly half in a month, and the number of agent PRs we later reverted dropped further than that."

— Rishi Kora, Verified Builder · London, United Kingdom

Rolling this out to a team takes three concrete changes, none of which need new tooling. First, a PR size cap enforced in CI: 400 changed lines excluding lockfiles and generated code, with a label that allows a one-off exception when a reviewer explicitly signs it off. Second, an agent-pr label applied automatically when the author is a bot account or when the description contains the agent template header, so that the review checklist and the size cap apply without anyone having to remember. Third, a reviewer rotation, so that the same two senior engineers are not the only people who ever run the checklist. Agent PRs arrive at all hours and in all time zones; a Bengaluru team reviewing a London agent's overnight output, or the reverse, is one of the genuine advantages of a distributed team if the checklist is written down and the same for everyone.

Rollout step Mechanism Why it matters
PR size cap CI check on changed lines, 400 excluding generated files; override label Makes the ten-minute review possible; forces agents to split work
Agent-PR label and template Auto-label on bot author or template header; template requires ticket, plan, test output Context arrives with the PR; checklist applies without reminders
Reviewer rotation Weekly rota across seniority and time zone; checklist in the repo Spreads the skill; stops two people becoming the bottleneck
Extended review path Any blast-radius trigger requires a second named reviewer Keeps the fast path fast by removing the risky PRs from it

Tooling is catching up with this, and it is worth knowing what is available before you build your own. As of September 2026, Cursor 3 ships a dedicated PR review tab and GitHub's Copilot code review has processed more than 60 million reviews; we covered Cursor 3's PR review tab and the Bugbot pricing change when it landed. These tools are a good first pass for the duplicate search and the security grep. They are not a substitute for the ticket-first intent check, which remains the one step no tool can do, because the tool did not sit in the planning meeting either. If you are still choosing between agents, our comparison of Claude Code, Cursor and Codex and our look at how the coding stack is settling in production cover the current field.

What to do next

The bottleneck has moved. Writing code is no longer the slow step, and pretending otherwise is how teams end up with a merge queue full of unreviewed agent output and a growing list of reverts. The fix is not to read faster. It is to review for the specific ways agents go wrong: drift from the ticket, duplicated helpers, weakened tests, casual dependency and permission changes. Each of those has a two-minute check, and the checks fit in ten minutes for a PR of sane size.

Start in your first week with the two changes that cost nothing: the PR template and the rule that a PR without a ticket link and raw test output gets sent back unread. Add the size cap in CI once the team has felt the difference. Write the checklist into the repository so the rotation can run it without you. Then measure what matters: time from PR open to merge for agent PRs, the share merged with a named human reviewer, and the number of agent PRs reverted in the following month. If the first goes down while the second goes up and the third goes down, the checklist is working. If not, the most likely cause is that the pre-requisites are being skipped, and the fix is to stop skipping them.

Whether your team is in Bengaluru, London or both, the arithmetic is the same: agents will keep producing more than you can read, and the teams that stay fast will be the ones that made reading unnecessary by making checking systematic.