Teemu Piirainen

Presentation · AI code review · October 2026

How much should you trust an AI code review?

I replayed one real diff through my review agents, changed the prompts and settings between replays and measured what the reviewers actually read. What changed, and what I now look at before I approve a pull request.

Teemu Piirainen

The full write-up

teemupiirainen.com/resources/ai-code-review-evidence

The decision

One finding.Is the rest fine?

If you trusted the review completely, this is easy. Fix the one finding, approve, move on.

If you don't, you are back to reading the whole change yourself.

aw-review

● Review finished. 1 finding.

MEDIUM · data · found by general

backend/src/services/bulletin-publishing.service.ts:305-314

Edit can change the recipient lists of an emergency message, but its receiving units stay fixed.

How it fails: units on the new list can read the message but cannot acknowledge it, and the publisher status never completes.

What the review did not say

One finding.Most of the change unread.

files changed
95
changed lines of code
2,589
lines no reviewer read
1,789

95 files · 2,589 changed lines of code

800 of 2,589 changed lines read, 1,789 unread.

Changed lines of code, read = returned by at least one reviewer's own tool call

A short list feels like good news. It might just mean the reviewer saw a third of the change.

Change 00 · Measure first

Recorded by the system.Not reported by the agent.

What I changed

Every review run writes a ledger of what each reviewer's tool calls actually returned.

Read = one of the reviewer's own tool calls returned the line. Nothing else counts.

Baseline · effort medium · share of changed code read

correctness
30%
general 1
29%
general 2
27%
security
25%
tests 1
13%
tests 2
13%
Unread by all
1,789lines
Findings · reported / kept
2 / 1
Cost per run
$1.66

What I learned

Nothing in the review changed yet. But now I could see it.

Change 01 · Prompt wording

Two sentences removed.Correctness 30% → 74%.

What I removed

Do not flag speculative concerns.

Only as far as … stop.

Written to prevent noise. The agents seemed to read them as permission to stop.

Same model, same effort (medium), same diff. Output instructions moved to the end.

Prompt cleanup · dashed line = previous step

correctness
74%▲44
general 1
62%▲33
general 2
48%▲21
security
33%▲8
tests 1
16%▲3
tests 2
0%▼13
Unread by all
1,789 to637lines
Findings · reported / kept
2 / 1 to9 / 7
Cost per run
$1.66 to$4.95

What I learned

Wording changes how far an agent goes.

Change 02 · Three things at once

Aimed at general.Correctness fell 74% → 54%.

What I changed

  • Shorter prompts per agent
  • Queue sorted by path
  • Frontend example for general

Queue and shorter prompts

correctness
54%▼20
general 1
77%▲15
general 2
88%▲40
security
32%▼1
tests 1
13%▼3
tests 2
1%▲1
Unread by all
637 to285lines
Findings · reported / kept
9 / 7 to10 / 4
Cost per run
$4.95 to$4.16

What I learned

Aim at one reviewer. Measure all of them.

Change 03 · One sentence back

Reading more.Seeing less.

What I put back

Read the patch first, open the full file only to prove a finding.

Without it, the tests reviewer read whole files with cat.

An agent can read a lot and still read the wrong thing. Make coverage the target and you get reviewers that fetch more code without thinking harder about it.

Patch-first

correctness
36%▼18
general 1
79%▲2
general 2
62%▼26
security
30%▼2
tests 1
14%▲1
tests 2
14%▲13
Unread by all
285 to509lines
Findings · reported / kept
10 / 4 to6 / 5
Cost per run
$4.16 to$3.73

What I learned

Coverage is a guardrail, not the goal.

Change 04 · Effort and workflow

Effort medium → high.

What I changed

  • Effort high: correctness, security, tests (general stays medium)
  • Tests in two phases: read, then mutate
correctness
5 calls →24 calls
tests, each
8 calls →24–27 calls

Test gap findings 0 → 11, all confirmed.

High effort

correctness
89%▲53
general 1
50%▼29
general 2
89%▲27
security
50%▲20
tests 1
90%▲76
tests 2
90%▲76
Unread by all
509 to253lines
Findings · reported / kept
6 / 5 to18 / 14
Cost per run
$3.73 to$11

What I learned

A better review was possible. It was not free.

The whole journey

From 30% to 91%.Not in a straight line.

Five replays of the same diff, then the current version on real branches. Wording lifted correctness, the next change took most of it back, and only effort and the two-phase workflow moved tests. On real branches the floor held.

Share of changed code read by correctness over five replays: 30%, 74%, 54%, 36%, 89%. Tests: 13%, 8%, 7%, 14%, 90%. Lines no reviewer read: 1789, 637, 285, 509, 253. On 38 real runs: correctness 91%, tests 92%, unread <3%.

Read ≠ found

Three reviewers read it.None reported it.

The AppLayout file was read in two runs. In Patch-first, general read it and reported a high severity bug: AppLayout returns 401. In High effort, three reviewers read it and none of them reported anything.

The AppLayout bug was high severity, and it was found once. In the High effort run, three reviewers read that file. None of them reported it.

The BulkWriter bug was found every time. Its severity moved between high and low. Same bug, same code, different opinion.

  • BulkWriter: unhandled write promises

    Every replay where the reviewers completedSeverity: High once, medium or low elsewhere

  • removeList race

    Prompt cleanup onwards, nearly every replay

  • AppLayout returns 401 (frontend)

    Only Patch-first, by generalSeverity: High

  • Name uniqueness checked outside the transaction

    First in High effort, by general on medium

What I learned

Coverage tells me what reached the reviewer. Not what it noticed.

Validation · Try to disprove it

Same finding.Now with evidence.

The finding from the start, with its evidence and what the validator checked when it tried to prove the finding wrong.

Earlier: 1–3 rejected per run.
High effort: 0 of 18.

Better findings, or an easier validator?

The validator runs on Opus, the reviewers on Sonnet. A different model, but the same family, and it can share their blind spots.

aw-review

● Validating…Validation done. 1 confirmed.

MEDIUM · data · PENDINGCONFIRMED · found by general

Edit can change the recipient lists of an emergency message, but its receiving units stay fixed.

Evidence:

  • bulletin-publishing.service.ts:305(defect)
  • bulletin-publishing.rules.ts:343(context)
  • emergency-acknowledgement.service.ts:112(call_site)

Validator: save() resolves receiving units only when mode === 'create'. On update the list is empty, so the units are never rewritten.

What I learned

CONFIRMED is evidence. Not proof.

Same reviewer, twice

Same prompt.Same diff.Same run.

General runs as two instances, started at the same moment. Nothing differs between them.

Sometimes they agree. Sometimes one reads half the change and the other almost all of it.

Which one is the review?

General · share of changed code read run 1 run 2
Replay
50% · 89%
Branch A
49% · 89%
Branch B
46% · 84%
Branch C
57% · 84%
Branch D
90% · 94%
Branch E
85% · 85%
Branch F
76% · 63%
Branch G
70% · 74%

What I learned

Run it once and I get 46% or 84%, with no way to know which.

On real branches

Not a higher peak.A higher floor.

Median share of changed code read Old · 30 runs Current · 8 runs Floor = lowest current run
correctness old
69%
correctness now
90%· floor 89
tests old
29%
tests now
92%· floor 90
security old
28%
security now
52%· floor 47
general old
73%
general now
80%· floor 46

What nobody found

2 confirmed misses

Both general's. Both in a file it never read.
Found later on the PR. Written back to the ledger.

Latest run: every changed code file read by at least one reviewer. Seven of seven live findings confirmed.

General still swings from 46% to 94%. It is the one reviewer without the two-phase workflow yet.

What I learned

Every review a PR gets is data about how good mine was.

Take this with you

Three questions for any AI review.

  1. What did it read?

    Mine0 unread

    If the reviewer saw a third of the change, a short finding list means nothing.

  2. Did the findings hold?

    Mine7 of 7 confirmed

    A finding a validator tried to disprove, and could not, is worth more than a confident explanation.

  3. How has it done before?

    Mine238 runs · 14 misses

    One run is one sample. The ledger says whether a reviewer is usually thorough or usually lucky.

When your AI review says “no problems found”,do you know what it read?

teemupiirainen.com/resources/ai-code-review-evidence

Every number here, the replays it comes from and what each metric cannot tell you are in the post.

How much should you trust an AI code review?