---
title: How the Claude Code team reviews code that Claude writes
description: Claude writes about 90% of Claude Code, and the team still reviews every change. Most of the reading moved to machines: six layers of checks, risk tiers set by the change, and a round cap that made review converge. Notes on the team's October post, next to my own loop.
date: 2026-10-26
tags: AI coding agents, code review, Claude Code, software engineering
---

Claude writes about 90% of Claude Code, and the team still reviews every change. What changed is who does the reading. Claude checks its own work before a pull request exists, a panel of agents reviews every PR, and a person's approval is required only where the risk is. From June to September, merged pull requests more than doubled while the share that had to be reverted fell by more than 40%.

That's from [How the Claude Code team does code review](https://claude.dev/blog/solving-code-review-at-anthropic/), posted on the claude.dev blog on October 15 by Addy Osmani, Morgan Westlee Lunt and Qing Wang. Addy republished it as [an X article](https://x.com/addyosmani/status/2114604912520921136) on October 26. Earlier this month I wrote up another answer to the same problem, Overmind's: [review the plan, not the pull request](/notes/review-the-plan-not-the-pull-request.html). The Claude Code team kept review after the code. They just stopped treating it as one gate.

## Reading every line stopped being the test

When agents write code, the number of changes explodes. In Anthropic's monorepo, changes landing per day went from about 2,500 in May to more than 30,000 in September. Claude Code lives in its own repository, but it's under the same pressure. Nobody reads that line by line.

So the team went back to what review is for. It does two jobs: find and fix problems before they ship, and spread an understanding of the codebase across the team. Most of the post is about the first one.

Their key line is that "did a human read the code?" is an imprecise proxy. The real question is what could go wrong with this change, and which check, automated or human, is the right one to catch it.

That moved review in time too. It used to be a gate at the end of the road: write the code, open a PR, wait for a colleague. Now it runs more like spellcheck while you write. Most changes have been reviewed and fixed by Claude at least once before a person is asked to look.

The goal is concrete. Most PRs in the Claude Code repos (the CLI, the desktop and web apps, the mobile apps) should merge with at most one person in the loop, and that person can be the author. They track human approvals per PR at the median and the 95th percentile, human comments, and how long a PR waits on a person. Reverts and incidents are the guardrail, because human time has two sides: the time spent reviewing, and the time spent fixing what bad code breaks. If reverts go up, they haven't saved time, only moved it.

<figure class="fd-fig cr-ask" aria-labelledby="cr1-cap" data-desc="Where review sits, before and now. Before, review was a gate at the end: a person writes the code, opens a pull request, waits for a colleague, and the colleague reads it. That answers one question: did a human read the code? Now it runs like spellcheck while you write: Claude checks and fixes the change at least once, agents review the pull request, and a person looks where the risk is. That answers a different question: what could go wrong, and which check catches it? The team tracks human approvals per pull request at the median and the 95th percentile, human comments, and the time a pull request waits on a person, with the revert and incident rates as the guardrail. The goal: most pull requests merge with at most one person in the loop, who can be the author.">
<div class="mp-two-row">
<div class="mp-two-card">
<p class="mp-two-who">Before</p>
<p class="mp-two-name">A gate at the end</p>
<ol class="rp-flow">
<li><span class="rp-who">Person</span>Writes the code</li>
<li><span class="rp-who">Person</span>Opens a pull request</li>
<li class="cr-wait"><span class="rp-who">Person</span>Waits for a colleague</li>
<li class="rp-rev"><span class="rp-who">Person</span>Reads every line</li>
</ol>
<p class="cr-q"><span class="rp-k">The question</span>Did a human read the code?</p>
</div>
<p class="mp-two-vs">vs</p>
<div class="mp-two-card mp-two-peter">
<p class="mp-two-who">Now</p>
<p class="mp-two-name">Spellcheck while you write</p>
<ol class="rp-flow">
<li><span class="rp-who rp-who-ai">Claude</span>Checks and fixes it, at least once</li>
<li><span class="rp-who rp-who-ai">Agents</span>Review the pull request</li>
<li class="rp-rev"><span class="rp-who">Person</span>Looks where the risk is</li>
</ol>
<p class="cr-q"><span class="rp-k">The question</span>What could go wrong, and which check catches it?</p>
</div>
</div>
<p class="rp-metrics"><span class="rp-k">Tracked</span><span>approvals per PR, median and p95</span><span>human comments</span><span>time waiting on a person</span></p>
<p class="rp-metrics"><span class="rp-k">Guardrail</span><span>revert rate</span><span>incident rate</span></p>
<p class="mp-strip"><b>Goal</b><span>Most pull requests merge with at most one person in the loop, who can be the author.</span></p>
<figcaption id="cr1-cap">The same change, reviewed at the end or all the way through. The marker rows are where a person reviews. The goal and the numbers that track it are the team's own.</figcaption>
</figure>

## Six layers, then rollback

A typical change, from a laptop or a Claude session in Slack, crosses six layers of checks: verification before the PR, required CI, the AI review, a security review, a human approval scaled by risk, and a merge queue with feature flags. Under all of them sits fast rollback.

Each layer catches some problems and misses others. Few get through all six, and rollback handles the rest. Most of the path is automated, and a person's attention goes where the risk is.

<figure class="fd-fig cr-layers" aria-labelledby="cr2-cap" data-desc="Six layers of checks with rollback under them, drawn as slices with holes. Six problems come in at the top, one per column. Verification before the pull request stops the first. Required CI, meaning lint, types, tests and the build, stops the second. The AI review, where agents hunt and vote, stops the third. The separate security review stops the fourth. Human approval, with none, one or two people depending on risk, stops the fifth. The sixth problem slips through every hole, past the merge queue and the feature flags, and fast rollback catches it: flags switched off, the change reverted. Five of the layers are automated, one is a person, and rollback is recovery. Which layer catches which problem is a schematic: the post gives no counts per layer.">
<div class="cr-cheese" role="img" aria-label="Six problems fall through six layers. Verification, required CI, the AI review, the security review and human approval each stop one. The merge queue and flags stop none in this sketch, and the last problem reaches fast rollback.">
<p class="cr-in"><span>Problems in</span><span class="cr-cells"><i></i><i></i><i></i><i></i><i></i><i></i></span></p>
<p class="cr-row cr-auto"><span class="cr-name">Verification<em>before the PR</em></span><span class="cr-cells"><i class="x"></i><i class="h"></i><i class="h"></i><i class="h"></i><i class="h"></i><i class="h"></i></span></p>
<p class="cr-row cr-auto"><span class="cr-name">Required CI<em>lint, types, tests, build</em></span><span class="cr-cells"><i></i><i class="x"></i><i class="h"></i><i class="h"></i><i class="h"></i><i class="h"></i></span></p>
<p class="cr-row cr-auto"><span class="cr-name">AI review<em>agents hunt and vote</em></span><span class="cr-cells"><i></i><i></i><i class="x"></i><i class="h"></i><i class="h"></i><i class="h"></i></span></p>
<p class="cr-row cr-auto"><span class="cr-name">Security review<em>a separate pass</em></span><span class="cr-cells"><i></i><i></i><i></i><i class="x"></i><i class="h"></i><i class="h"></i></span></p>
<p class="cr-row cr-person"><span class="cr-name">Human approval<em>none, one or two people</em></span><span class="cr-cells"><i></i><i></i><i></i><i></i><i class="x"></i><i class="h"></i></span></p>
<p class="cr-row cr-auto"><span class="cr-name">Merge queue, flags<em>checks again on main</em></span><span class="cr-cells"><i></i><i></i><i></i><i></i><i></i><i class="h"></i></span></p>
<p class="cr-row cr-floor"><span class="cr-name">Fast rollback<em>flags off, revert</em></span><span class="cr-cells"><i></i><i></i><i></i><i></i><i></i><i class="x"></i></span></p>
</div>
<ul class="cr-key">
<li><i class="cr-k-auto"></i>automated</li>
<li><i class="cr-k-person"></i>a person, scaled by risk</li>
<li><i class="cr-k-floor"></i>recovery</li>
<li><i class="cr-k-x"></i>a problem stopped</li>
</ul>
<figcaption id="cr2-cap">The post's own picture is layers, not one gate. Which layer stops which problem is a schematic: it gives no counts per layer.</figcaption>
</figure>

## Before the PR, show the output

Most review now happens before a pull request exists. Claude gets something it can run, like tests, a build or a screenshot to compare against the design, and has to show the output instead of saying it's done. Then a subagent reviews the diff with fresh context. Many people on the team also run internal skills before opening a PR: one hunts for bugs, one simplifies the diff, one checks the behaviour end to end.

Boris Cherny, who created Claude Code, calls giving Claude a way to verify its work probably the most important thing for good results. He estimates it makes the final result two to three times better.

When the reviewer keeps flagging the same thing, the team fixes it at the source: in the skill, with a lint rule, or in two instruction files. `CLAUDE.md` says what the project is: the stack, the conventions, the goals and the non-goals. Every review loads it, so it stays short and points to other docs. The reviewer also flags code that breaks one of its conventions, and changes that make a line in it out of date.

`REVIEW.md` is written by code owners, to tell the reviewer what matters in their part of the code, and the team checks separately whether the reviewer follows it. The good ones are short. They say what failure looks like in that repo (for docs it's "it lies", for config it's "it misroutes"), cap the nits, list paths to skip, and ask for a file and line as evidence instead of a guess from a name. They also ask for a summary that starts with a count and says plainly when there's nothing to fix.

<figure class="fd-fig cr-pre" aria-labelledby="cr3-cap" data-desc="Review before the pull request, in three steps. One: run it, meaning tests, a build, or a screenshot compared against the design. Two: show it, the output instead of a claim that it's done. Three: fresh eyes, a subagent reviews the diff with a clean context. Boris Cherny's estimate: a way to verify its work makes the result two to three times better. Then the two files that fix repeated findings at the source. CLAUDE.md, loaded by every review: the stack and the conventions, the goals and the non-goals, kept short and pointing to other docs; the reviewer flags code that breaks a convention and changes that make a line out of date. REVIEW.md, written by code owners: what failure looks like here, such as docs that lie and config that misroutes, a cap on nits, paths to skip, a file and line as evidence instead of a guess from a name, and a summary that starts with a count and says plainly when there's nothing to fix. The team checks separately whether the reviewer follows it.">
<ol class="mp-trio">
<li><p class="mp-trio-n"><b>1</b>Run it</p><p class="mp-trio-what">Tests, a build, or a screenshot against the design</p></li>
<li><p class="mp-trio-n"><b>2</b>Show it</p><p class="mp-trio-what">The output, not "done"</p></li>
<li><p class="mp-trio-n"><b>3</b>Fresh eyes</p><p class="mp-trio-what">A subagent reviews the diff with a clean context</p></li>
</ol>
<p class="mp-strip"><b>Boris Cherny</b><span>His estimate: a way to verify its work makes the result two to three times better.</span></p>
<div class="mp-two-row">
<div class="mp-two-card">
<p class="mp-two-who">Every review loads it</p>
<p class="mp-two-name cr-file">CLAUDE.md</p>
<ul class="cr-doc">
<li>The stack and the conventions</li>
<li>The goals and the non-goals</li>
<li>Short, pointing to other docs</li>
</ul>
<p class="mp-two-line">The reviewer flags code that breaks a convention, and a change that makes a line out of date</p>
</div>
<p class="mp-two-vs">and</p>
<div class="mp-two-card mp-two-peter">
<p class="mp-two-who">Code owners write it</p>
<p class="mp-two-name cr-file">REVIEW.md</p>
<ul class="cr-doc">
<li>What failure looks like here: docs that lie, config that misroutes</li>
<li>A cap on nits</li>
<li>Paths to skip</li>
<li>A file and line as evidence, not a guess from a name</li>
<li>A summary that starts with a count, and says plainly when there's nothing to fix</li>
</ul>
<p class="mp-two-line">Checked separately: does the reviewer follow it?</p>
</div>
</div>
<figcaption id="cr3-cap">Proof before review, then the two files a repeated finding goes into. The REVIEW.md lines are the post's description of a good one.</figcaption>
</figure>

## The reviewer votes before it comments

Every pull request gets a Claude review before a person looks. It reads the diff against the whole repo and the PR's stated intent, and asks whether the change solves its problem, fits the system, or breaks something else. On a bug fix, it looks for the root cause.

Findings get checked before anyone sees them. Several agents hunt for bugs at the same time. A separate group votes on each finding, and it's posted only if enough of them agree it's real. Findings that are only about style, diagnostics or cleanup are dropped; the team A/B tested those filters on its own PRs first. What's left lands as inline comments sorted by severity, each tagged Important, Nit or Pre-existing, so an old bug doesn't look like the author's fault.

When the research preview launched, the share of internal PRs getting substantive review comments went from 16% to 54%. Most PRs over 1,000 lines get findings, and most small ones come back clean. Admins set each repo to Quick, Standard or Deep.

Every finding has to be answered: fix it, or resolve the thread with a reason. A separate security review works the same way, and an open finding from either one blocks the merge. CI blocks too, with dozens of checks on a typical CLI pull request. The PR template asks how the change was tested and how to undo it. Claude can fill that in, but the person who asked for the change signs off on it.

<figure class="fd-fig cr-bot" aria-labelledby="cr4-cap" data-desc="How the AI reviewer filters its own findings, in four steps. One, hunt: several agents look for bugs at the same time, drawn as nine candidate findings. Two, vote: a separate group votes on each finding, and only the ones enough voters agree on survive, drawn as five. Three, filter: findings only about style, diagnostics or cleanup are dropped, drawn as three. Four, post: what's left goes on the pull request as inline comments sorted by severity, tagged Important, Nit or Pre-existing. When the research preview launched, internal pull requests with substantive review comments went from 16 percent to 54 percent. Every finding is fixed or resolved with a reason, and an open finding from the code review or the security review blocks the merge.">
<ol class="cr-funnel">
<li><p class="cr-fn-n"><b>1</b>Hunt</p><p class="cr-sq" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></p><p class="cr-fn-s">Several agents look for bugs at once</p></li>
<li><p class="cr-fn-n"><b>2</b>Vote</p><p class="cr-sq" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i><i class="o"></i><i class="o"></i><i class="o"></i><i class="o"></i></p><p class="cr-fn-s">Posted only if enough voters agree it's real</p></li>
<li><p class="cr-fn-n"><b>3</b>Filter</p><p class="cr-sq" aria-hidden="true"><i></i><i></i><i></i><i class="o"></i><i class="o"></i></p><p class="cr-fn-s">Style, diagnostics and cleanup dropped</p></li>
<li><p class="cr-fn-n"><b>4</b>Post</p><p class="cr-tags"><span class="cr-imp">Important</span><span class="cr-nit">Nit</span><span class="cr-pre">Pre-existing</span></p><p class="cr-fn-s">Inline, sorted by severity</p></li>
</ol>
<div class="rp-bars">
<p class="rp-bars-h">Internal PRs with substantive review comments</p>
<p class="rp-bar"><span>Before the research preview</span><i class="cr-w-16"></i><b>16%</b></p>
<p class="rp-bar rp-bar-win"><span>With Code Review</span><i class="cr-w-54"></i><b>54%</b></p>
</div>
<p class="mp-strip"><b>Every finding</b><span>Fixed, or resolved with a reason. An open one from this review or the security review blocks the merge.</span></p>
<figcaption id="cr4-cap">The squares are a schematic: the post gives no counts per step. The two bars are to scale on a 0 to 100% axis.</figcaption>
</figure>

## The change sets the tier, not the author

A docs fix and an auto-updater change don't carry the same risk, so they don't get the same review. The post has three tiers:

- Low: docs, copy, internal-only features, additive changes behind a flag. No human approval once automated review and security checks are green.
- Medium: ordinary product changes, and sensitive areas like caching, the auto-updater or system prompts. One person.
- High: the highest-risk code, plus a small set of paths that always need two. Two people.

A classifier reads the change and picks the tier. A second classifier asks whether this particular change needs a person even when its tier says it doesn't. Paths that owners list as sensitive, and any CI workflow file, always need a person.

The rules around the table are what make it hold. Only authors on the repo's trusted list get the lighter paths; everyone else needs a second person. An agent's approval never counts as a person's. Whoever prompted an agent-written PR counts as one of its required reviewers, as if they'd typed the code. And an approval covers one commit: an agent's push, or a merge someone resolved by hand, needs a new one.

<figure class="fd-fig cr-tier-fig" aria-labelledby="cr5-cap" data-desc="Human approval scaled by risk, from the post's table. Low risk: docs, copy, internal-only features and additive changes behind a flag need no person once automated review and security checks are green. Medium risk: ordinary product changes and sensitive areas such as caching, the auto-updater or system prompts need one person. High risk: the highest-risk code and a small set of paths need two people. A classifier reads the change and sets the tier, and a second one asks whether this change needs a person anyway. Paths that owners list as sensitive, and any CI workflow file, always need a person. An agent's approval never fills a person's slot. An approval covers one commit: an agent's push or a merge resolved by hand needs a new one. On an agent-written pull request, the person who prompted it counts as one of the required reviewers.">
<div class="cr-tiers">
<div class="cr-tier">
<p class="cr-tier-k">Low</p>
<p class="cr-heads" aria-hidden="true"><svg class="cr-head cr-head-none" viewBox="0 0 24 30"><circle cx="12" cy="8" r="6"/><path d="M2,29 Q2,17 12,17 Q22,17 22,29 Z"/></svg></p>
<p class="cr-tier-n">No person</p>
<p class="cr-tier-s">once automated review and security checks are green</p>
<p class="cr-tier-e">Docs, copy, internal-only features, additive changes behind a flag</p>
</div>
<div class="cr-tier">
<p class="cr-tier-k">Medium</p>
<p class="cr-heads" aria-hidden="true"><svg class="cr-head" viewBox="0 0 24 30"><circle cx="12" cy="8" r="6"/><path d="M2,29 Q2,17 12,17 Q22,17 22,29 Z"/></svg></p>
<p class="cr-tier-n">One person</p>
<p class="cr-tier-e">Ordinary product changes, and sensitive areas: caching, the auto-updater, system prompts</p>
</div>
<div class="cr-tier cr-tier-hi">
<p class="cr-tier-k">High</p>
<p class="cr-heads" aria-hidden="true"><svg class="cr-head" viewBox="0 0 24 30"><circle cx="12" cy="8" r="6"/><path d="M2,29 Q2,17 12,17 Q22,17 22,29 Z"/></svg><svg class="cr-head" viewBox="0 0 24 30"><circle cx="12" cy="8" r="6"/><path d="M2,29 Q2,17 12,17 Q22,17 22,29 Z"/></svg></p>
<p class="cr-tier-n">Two people</p>
<p class="cr-tier-e">The highest-risk code, and a small set of paths that always need two</p>
</div>
</div>
<div class="rp-pair">
<p><span class="rp-k">Sets the tier</span>The change, read by a classifier. A second one asks if it needs a person anyway.</p>
<p><span class="rp-k">Always a person</span>Paths that owners list as sensitive, and any CI workflow file</p>
</div>
<div class="rp-pair">
<p><span class="rp-k">An agent's approval</span>Never fills a person's slot</p>
<p><span class="rp-k">One approval covers</span>One commit. An agent's push or a merge resolved by hand needs a new one.</p>
</div>
<p class="mp-strip"><b>On the hook</b><span>On an agent-written PR, the person who prompted it is one of the required reviewers.</span></p>
<figcaption id="cr5-cap">The tier table from the post, then the rules around it. Only authors on a repo's trusted list get the lighter paths.</figcaption>
</figure>

## Incidents are the test set

The team built the reviewer's test set from real failures. They collected past PRs that caused incidents, with their fix-forwards and reverts, and had engineers who know that code confirm each bug and where it lived. The reviewer runs against that set every day. Each new incident adds a case, and every change to the reviewer's prompts, tools or model is scored against all of them.

Catching more bugs is easy if you don't mind flagging non-bugs, so they measure noise too. Every comment is sorted by what the person on the PR did with it: fixed it, said it was wrong, said the behaviour was intentional, or decided it didn't matter. They also run the reviewer on PRs known to be clean, and count the reports that would have led someone to make a harmful change. The priority is written down: real bugs first, fewer review rounds second.

<figure class="fd-fig cr-eval" aria-labelledby="cr6-cap" data-desc="The reviewer's eval, built from incidents, as two connected rows. The set grows: an incident or a revert, a change that broke something, is checked by engineers who know the code, who confirm the bug and where it lived, and it is added to the set, one case per incident. The reviewer changes: someone changes its prompts, tools or model, the daily eval run scores it against every case in the set, and if the score went up the change is kept; if not, it is dropped. Noise is measured by what people did with each review comment: fixed it, said it was wrong, said it was intentional, or decided it didn't matter. The written priority: real bugs first, fewer review rounds second.">
<div class="cr-loop">
<p class="cr-lane-h">The set grows</p>
<ol class="cr-lane">
<li><b>Incident or revert</b>A change that broke something</li>
<li><b>Expert-checked</b>Engineers who know the code confirm the bug and where it lived</li>
<li><b>Added to the set</b>One case per incident</li>
</ol>
<p class="cr-feed">Every case, every day</p>
<p class="cr-lane-h">The reviewer changes</p>
<ol class="cr-lane cr-lane-ai">
<li><b>Change the reviewer</b>Its prompts, tools or model</li>
<li><b>Daily eval run</b>Scored against every case</li>
<li><b>Score went up?</b>Keep it. If not, drop it.</li>
</ol>
</div>
<p class="rp-metrics"><span class="rp-k">Noise</span><span>fixed it</span><span>said it was wrong</span><span>intentional</span><span>didn't matter</span></p>
<p class="mp-strip"><b>Priority</b><span>Real bugs first, fewer review rounds second. Written down.</span></p>
<figcaption id="cr6-cap">Incidents feed the set, the set scores every change to the reviewer, and what people do with the comments keeps the noise in check.</figcaption>
</figure>

## Review has to converge

Noise costs more when the author is an agent. A person waves off a nit in seconds. An agent fixes it, which means another commit and another round of review. An agent author and an agent reviewer can keep each other busy forever: one CLI pull request went through more than fifty rounds overnight before its author stopped it.

When the team ran git blame on what the reviewer kept flagging, about 59% of it was code that had been there since the reviewer's first pass. It wasn't finding new bugs. It was finding a few more old ones each round. They'd made it worse themselves, by running several rounds by default because each pass catches something new.

So they changed re-review:

1. A round cap. After three rounds, the reviewer asks the author to step back and reconsider the approach.
2. Diff-only re-review after the first few passes.
3. Flag the class of bug, so the author fixes every instance, not one line.
4. No repeats of findings already posted.

Old bugs still get hunted, just not on your PR: review agents also run nightly over existing code. After the change, PRs passing after one review-and-fix round went from 65% to 78%, PRs needing five or more rounds fell from 8.9% to 1.5%, and the mean number of reviews to pass fell from 2.08 to 1.32.

<figure class="fd-fig cr-conv" aria-labelledby="cr7-cap" data-desc="Review that converges. The four changes to re-review: one, a round cap, so after three rounds the reviewer asks the author to step back and rethink the approach; two, diff only, so after the first few passes only the new diff is reviewed; three, the class of bug, so the author fixes every instance and not one line; four, no repeats of a finding already posted. The results on internal pull requests, before and after the convergence work: passing after one review and fix round went from 65 to 78 percent; needing five or more rounds fell from 8.9 to 1.5 percent; mean reviews to pass fell from 2.08 to 1.32. Before the change, one CLI pull request went more than fifty rounds overnight before its author stopped it.">
<ol class="rp-steps">
<li><p class="rp-step-n"><b>01</b>A round cap</p><p class="rp-step-s">After three rounds, step back and rethink the approach</p></li>
<li><p class="rp-step-n"><b>02</b>Diff only</p><p class="rp-step-s">After the first few passes, review just the new diff</p></li>
<li><p class="rp-step-n"><b>03</b>The class of bug</p><p class="rp-step-s">So the author fixes every instance, not one line</p></li>
<li><p class="rp-step-n"><b>04</b>No repeats</p><p class="rp-step-s">A finding already posted isn't posted again</p></li>
</ol>
<div class="rp-bars">
<p class="rp-bars-h">Pass after one review and fix round</p>
<p class="rp-bar"><span>Before</span><i class="cr-w-65"></i><b>65%</b></p>
<p class="rp-bar rp-bar-win"><span>After</span><i class="cr-w-full"></i><b>78%</b></p>
<p class="rp-bars-h cr-next">Need five or more rounds</p>
<p class="rp-bar"><span>Before</span><i class="cr-w-full"></i><b>8.9%</b></p>
<p class="rp-bar rp-bar-win"><span>After</span><i class="cr-w-15"></i><b>1.5%</b></p>
<p class="rp-bars-h cr-next">Mean reviews to pass</p>
<p class="rp-bar"><span>Before</span><i class="cr-w-full"></i><b>2.08</b></p>
<p class="rp-bar rp-bar-win"><span>After</span><i class="cr-w-132"></i><b>1.32</b></p>
</div>
<p class="mp-strip"><b>Overnight</b><span>One CLI pull request went more than fifty rounds before its author stopped it.</span></p>
<figcaption id="cr7-cap">The four changes, then the team's internal numbers before and after. Each pair of bars is to scale against its larger value.</figcaption>
</figure>

## People keep five jobs

Pulling a senior engineer out of deep work to catch a routine mistake costs far more than an automated review. So people keep the jobs only people can do:

- Deciding whether to build it at all: direction, taste and scope. One team that declared its review bottleneck solved still keeps human review for changes from outside the team, so someone asks whether the change should exist.
- Reviewing risky code like billing, the auto-updater and CI workflows.
- Reviewing architecture that sets a pattern others will copy.
- Reading the evidence: test output, the command that ran and what it returned, a screenshot, the reviewer's findings.
- Owning the outcome. A person is accountable for every change that ships, whoever typed it.

The evidence job comes with a rule worth stealing: "If you can't say what the checks would miss, ask for a real review rather than a quick approval."

<figure class="fd-fig cr-people" aria-labelledby="cr8-cap" data-desc="The five jobs people keep. One: whether to build it, meaning direction, taste and scope. Two: risky code, such as billing, the auto-updater and CI workflows. Three: architecture, the choices that set a pattern others copy. Four: the evidence, meaning the test output, the command and what it returned, a screenshot, and the reviewer's findings. Five: the outcome, because a person answers for every change that ships, whoever typed it. The rule that goes with the evidence: if you can't say what the checks would miss, ask for a real review rather than a quick approval.">
<ol class="cr-jobs">
<li><b>Whether to build it</b><span>Direction, taste and scope</span></li>
<li><b>Risky code</b><span>Billing, the auto-updater, CI workflows</span></li>
<li><b>Architecture</b><span>Choices that set a pattern others copy</span></li>
<li><b>The evidence</b><span>Test output, the command and what it returned, a screenshot, the findings</span></li>
<li><b>The outcome</b><span>A person answers for every change that ships, whoever typed it</span></li>
</ol>
<p class="mp-strip"><b>The rule</b><span>If you can't say what the checks would miss, ask for a real review rather than a quick approval.</span></p>
<figcaption id="cr8-cap">What stays with people once the routine reading moved to machines, in the post's order.</figcaption>
</figure>

## You land it, you own it

Review's second job was spreading context. Reading a teammate's diff is how you learn the code. The team has no formal replacement, and says nobody keeps a complete model of Claude Code in their head, or needs to. When someone needs a part of the codebase, they ask Claude for an explainer of exactly that slice. It takes minutes and matches the code as it is today. A one-line copy change teaches you almost nothing. An explainer for the module you're about to change teaches you a lot.

Ownership works the same way. If you land a small or medium change and it breaks, you usually fix it. That doesn't go badly because changes are small, flags and reverts and staged releases undo most mistakes in minutes, the blast radius sets the bar, regressions show up fast in telemetry and internal use, and incident reviews look at the system, not the person.

Cheap code also makes it cheap to change course. When someone in the channel points at an earlier decision or a simpler route, the author closes or reshapes the PR, and it doesn't feel like a loss. The trick is to raise it early in the thread, not as a blocking comment on a finished PR.

<figure class="fd-fig cr-own" aria-labelledby="cr9-cap" data-desc="How the team learns the code once machines do most of the reading. Before: read the diffs, but a one-line copy change teaches almost nothing. Now: ask Claude for an explainer of exactly the slice you're about to change; it takes minutes and matches the code as it is today. Why owning your own merges works: changes are small, everything is reversible, the blast radius sets the bar, regressions surface fast, and nobody is blamed. Changing course: raise it early in the thread, not as a blocking comment on a finished pull request.">
<div class="mp-two-row">
<div class="mp-two-card">
<p class="mp-two-who">Learning the code, before</p>
<p class="mp-two-name">Read the diffs</p>
<svg class="mp-two-art" viewBox="0 0 300 92" aria-hidden="true"><rect class="rp-doc" x="96" y="4" width="108" height="84"/><rect class="rp-doc-h" x="96" y="4" width="108" height="14"/><rect class="mp-ln" x="106" y="28" width="80" height="4"/><rect class="mp-ln" x="106" y="38" width="66" height="4"/><rect class="cr-diff" x="106" y="48" width="74" height="5"/><rect class="mp-ln" x="106" y="60" width="84" height="4"/><rect class="mp-ln" x="106" y="70" width="58" height="4"/></svg>
<p class="mp-two-line">A one-line copy change teaches almost nothing</p>
</div>
<p class="mp-two-vs">vs</p>
<div class="mp-two-card mp-two-peter">
<p class="mp-two-who">Learning the code, now</p>
<p class="mp-two-name">Ask for an explainer</p>
<svg class="mp-two-art" viewBox="0 0 300 92" aria-hidden="true"><rect class="rp-doc" x="96" y="4" width="108" height="84"/><rect class="rp-doc-h" x="96" y="4" width="108" height="14"/><rect class="cr-node" x="108" y="28" width="30" height="18"/><rect class="cr-node cr-node-on" x="162" y="28" width="30" height="18"/><rect class="cr-node" x="135" y="62" width="30" height="18"/><path class="cr-edge" d="M138,37 H162 M123,46 L143,62 M177,46 L157,62"/></svg>
<p class="mp-two-line">Exactly the slice you're about to change, in minutes, as the code is today</p>
</div>
</div>
<p class="rp-metrics"><span class="rp-k">Owning it works because</span><span>small changes</span><span>everything reversible</span><span>blast radius sets the bar</span><span>regressions surface fast</span><span>no blame</span></p>
<p class="mp-strip"><b>Changing course</b><span>Raise it early in the thread, not as a blocking comment on a finished PR.</span></p>
<figcaption id="cr9-cap">A diff where one line changed, next to an explainer of the part you're about to touch.</figcaption>
</figure>

## Next to my own loop

My loop is on [How I work with AI](/ai.html): an issue first, an agent builds on its own branch, it proves the change works, a model from another vendor gives a second opinion, and a pull request closes it. Two of my rules there say what this post says. "Check what the model produced, not what it says it did" is their "show the output instead of saying it's done". "An empty review is not a pass" is their `REVIEW.md` asking for a summary that says plainly when there's nothing to fix.

My Forge pipeline already caps rounds harder than they do. A blocking finding from its reviewer gets one more fix, then the pull request opens. That's in [the OpenShell note](/notes/openshell-sandbox-inside-a-sandbox.html).

The difference is the last step. On that page every change ends as a pull request I read and merge, and in [my software factory note](/notes/software-factory-2022-to-lights-off.html) the slow part of my setup is my review queue. Their tier table is aimed right at that: a docs fix needs nobody once the checks are green, and a person's time goes to billing, CI and the auto-updater.

<figure class="fd-fig cr-mine" aria-labelledby="cr10-cap" data-desc="The Claude Code team's process next to my loop as it's written on my How I work with AI page, one row per topic. Proof first: they have Claude show the output, tests, a build or a screenshot; I ask for the build and tests, and screenshots in a real browser at 320 pixels and on desktop. The AI review: their Claude agents hunt, vote, then post by severity; in my loop a model from another vendor reviews the change. When nothing is found: their summary says so plainly, with a count; my rule is that an empty review is not a pass. Rounds: they allow three, then the author rethinks the approach; my Forge pipeline gives a blocking finding one more fix. Who approves: for them the change sets the tier, no one, one or two people; in my loop it's every pull request, me. That last row is where my review queue builds up, and the tier table is the piece aimed at it.">
<div class="cr-vs">
<div class="cr-vs-row"><p class="cr-vs-k">Proof first</p><p><span class="rp-k">The team</span>Claude shows the output: tests, a build, a screenshot</p><p><span class="rp-k">Me</span>Build and tests, screenshots in a real browser at 320 px and on desktop</p></div>
<div class="cr-vs-row"><p class="cr-vs-k">AI review</p><p><span class="rp-k">The team</span>Claude agents hunt, vote, then post by severity</p><p><span class="rp-k">Me</span>A model from another vendor reviews the change</p></div>
<div class="cr-vs-row"><p class="cr-vs-k">Nothing found</p><p><span class="rp-k">The team</span>The summary says so plainly, with a count</p><p><span class="rp-k">Me</span>An empty review is not a pass</p></div>
<div class="cr-vs-row"><p class="cr-vs-k">Rounds</p><p><span class="rp-k">The team</span>Three, then rethink the approach</p><p><span class="rp-k">Me</span>Forge: one more fix after a blocking finding</p></div>
<div class="cr-vs-row cr-vs-gap"><p class="cr-vs-k">Who approves</p><p><span class="rp-k">The team</span>The change sets the tier: no one, one or two people</p><p><span class="rp-k">Me</span>Every pull request, me</p></div>
</div>
<p class="mp-strip"><b>My queue</b><span>The last row is where it builds up. The tier table is the piece aimed at it.</span></p>
<figcaption id="cr10-cap">Their layers next to my loop as it's written on /ai.html. The marker row is the one that differs.</figcaption>
</figure>

## Try it

The post ends with a checklist, and most of it works with tools you already have:

- Before the PR: give your agent checks it can run, listed in a short `CLAUDE.md`, plus hooks for formatters and linters. Run `/code-review` and `/security-review` locally, or a fresh-context subagent. Start a `REVIEW.md` and move repeated comments into it.
- On every PR: require CI, add Testing, Risk and How to revert to the PR template, put an AI reviewer on one repo and tune `REVIEW.md` for a few weeks before widening it, and require every review thread resolved. Cap the rounds, and re-review only the new diff after the first few.
- Approval: write down risk tiers set by the change, start strict and loosen with evidence. Use CODEOWNERS and branch protection so auth, billing, CI, release workflows and installers always need a person. Never let a bot's approval fill a human slot, and dismiss approvals on new commits.
- After the merge: a merge queue, feature flags, internal use first, staged releases and a one-step rollback. Turn incidents and reverts into an eval set, and measure human time with reverts as the guardrail.

<figure class="fd-fig cr-adopt-fig" aria-labelledby="cr11-cap" data-desc="The post's adoption checklist in four columns. Before the pull request: runnable checks in a short CLAUDE.md, hooks for formatters and linters, the code-review and security-review commands run locally, and a REVIEW.md for repeated comments. On every pull request: required CI, Testing, Risk and How to revert in the template, an AI reviewer on one repo first, every thread resolved, and a cap on rounds with diff-only re-review after it. Approval: risk tiers set by the change, strict first and loosened with evidence, CODEOWNERS for auth, billing, CI, release and installers, and bot approvals that never count and are dismissed on new commits. After the merge: a merge queue and feature flags, internal users first and then staged releases, one-step rollback, and incidents and reverts as the eval. Underneath: measure human time per change, with reverts and incidents as the guardrail.">
<ol class="cr-adopt">
<li><p class="cr-adopt-h"><b>1</b>Before the PR</p><ul><li>Runnable checks in a short CLAUDE.md</li><li>Hooks for formatters and linters</li><li>/code-review and /security-review locally</li><li>A REVIEW.md for repeated comments</li></ul></li>
<li><p class="cr-adopt-h"><b>2</b>On every PR</p><ul><li>Required CI</li><li>Testing, Risk and How to revert in the template</li><li>An AI reviewer on one repo first</li><li>Every thread resolved, rounds capped</li></ul></li>
<li><p class="cr-adopt-h"><b>3</b>Approval</p><ul><li>Tiers set by the change, strict first</li><li>CODEOWNERS for auth, billing, CI, release, installers</li><li>Bot approvals never count</li><li>Dismissed on new commits</li></ul></li>
<li><p class="cr-adopt-h"><b>4</b>After the merge</p><ul><li>Merge queue and feature flags</li><li>Internal users first, then staged</li><li>One-step rollback</li><li>Incidents and reverts as the eval</li></ul></li>
</ol>
<p class="mp-strip"><b>Measure</b><span>Human time per change, with reverts and incidents as the guardrail.</span></p>
<figcaption id="cr11-cap">The checklist from the end of the post, shortened. The order is theirs.</figcaption>
</figure>

If you want more:

- [How the Claude Code team does code review](https://claude.dev/blog/solving-code-review-at-anthropic/), the post, 14 minutes
- [Addy Osmani's X article](https://x.com/addyosmani/status/2114604912520921136), the same post, rewritten
- [Code Review for Claude Code](https://code.claude.com/docs/en/code-review), the docs
- [Claude Code best practices](https://code.claude.com/docs/en/best-practices), where the verify-your-work habit comes from
- [Review the plan, not the pull request](/notes/review-the-plan-not-the-pull-request.html), Overmind's answer to the same problem

Pick one piece this week, a `REVIEW.md` or a round cap, and tell me what it does to your review queue.
