7 Treat review as an adversarial search
Review goes soft when its social purpose is approval. The author wants the merge. The reviewer wants to be helpful. Both read the same explanation, look at the same tests, and inherit the same assumptions. A polite pass over the diff finds the style problems and leaves the state model untouched.
Ask your reviewers to disprove the work instead.
7.1 Fresh context matters
You know why every line is the way it is. That knowledge serves you during implementation and betrays you during self-review: you fill each gap without noticing there was one. A reviewer arriving cold sees the gap.
What cold readers catch is rarely obscure syntax. It is failed assumptions: an approval gate that can never run, two serialization modes that quietly disagree on trailing whitespace, a golden-image signature that ignores the color information its author was sure it covered. Nothing in the diff flags any of these. The path the author walked made the code feel inevitable, and the reviewer never walked that path.
When the reviewer is an agent, you control how cold it arrives. Give it the diff and the contract, not your explanation of why the code is right. An agent that reads your reasoning first inherits your assumptions along with it.
7.2 Give each reviewer a lens
Agents let you convene a review panel at will, and the temptation is to convene five copies of the same reviewer. That buys duplicated confidence. A useful panel divides the attack surface.
For a workflow change, the lenses might be:
- control-flow reachability;
- persistence and resume;
- audit integrity;
- malformed conditions;
- unnecessary state.
For a renderer: layout geometry, backend parity, font and color behavior, validity of screenshots or goldens, platform-specific execution. For a parser or a migration: byte preservation, escaping and malformed input, version skew, downgrade behavior, agreement with downstream consumers.
The lens does not restrict the reviewer. It gives each one a reason to question a premise the author and the rest of the panel share.
7.3 Findings are claims too
Consider a wide review pass: eleven lenses over one codebase, fifty-two candidate findings. Before anything is filed, a second validator rereads the code and tries to refute every finding. Five die. One alleged a dark-mode failure; the validator found the exact template configuration that made the behavior correct. Another claimed a typesetting rule had been inverted; the algorithm’s published source showed the implementation was faithful and the reviewer’s premise was wrong. The forty-seven survivors become issues worth a maintainer’s attention.
None of this discredits review. It argues for completing it. A finding should include:
- the claimed defect;
- the code or contract involved;
- an input or state that should expose it;
- the likely consequence;
- evidence that would refute the claim.
A separate pass then accepts, rejects, or defers each finding with a reason. File every plausible concern unfiltered and you degrade the tracker and teach yourself to ignore review output.
7.4 Require repair or reasoned rejection
Review turns into ceremony when a comment can be acknowledged without changing anything. An accepted finding should produce a repair and a check. A rejected finding should preserve the technical reason. A deferred finding should state what evidence or scope is missing.
Picture one round on one change: ten comments come back. Six get fixed. Three turn out to be already resolved, and you verify that rather than waving it through. One is kept with a written caveat because no minimal failing test can be constructed. None are accepted while wrong. Zero wrong acceptances matters as much as six repairs, and both counts come from checking each comment against the code rather than against the reviewer’s confidence.
A decline can carry as much engineering as a fix: refusing to let a rendering backend mutate public scene state for its own convenience, or keeping round-to-nearest after a reviewer proposed changing the rounding, because round-to-nearest was correct.
Dispositions with reasons form a ledger rather than a pile of comments. Over time the ledger shows which parts of the system generate recurring confusion, which lenses find real defects, and which tools hallucinate the same domain rule again and again. The rejection record earns its keep twice: it stops future reviewers from reopening a settled false premise, and it proves you do not measure quality by issue count.
7.5 Make bad news useful
Adversarial review works only when the system welcomes unpleasant facts:
mainwas already red;- a screenshot diff was encoding noise;
- a reported memory bug was not a memory bug;
- an outside contributor’s tests found a gap in the internal fix;
- a performance claim measured the wrong phase;
- a shipped feature should be removed;
- a guardrail was correct and still badly implemented.
A collaborator punished for reporting facts like these will not produce fewer defects. They will produce fewer reports. Early bad news is cheap. Late reassurance is expensive.
7.6 Review the implementation and its proof separately
The code and its evidence can fail independently. A correct implementation can have a weak test. A strong test can reveal that the implementation still fails on one path. A benchmark can be accurate while its label overstates what it measured.
So ask two questions. Is the behavior correct? And would the supplied proof detect a plausible version of the behavior that is wrong? The second is easy to skip because the test suite looks like evidence. It is still authored work, and it deserves its own attack.