# 15.4 Layers of trust: tests, types and review (/building-ai-workers/code-review/why-review-changes)

---
type: Document
title: "15.4 Layers of trust: tests, types and review"
description: "Trust in agent-written code comes in layers, and the question that replaces \"did someone read it?\" is which check should catch each kind of problem."
status: stable
order: 315.04
ksor:
  owner: team:panaversity
  audience: [ public ]
  approval:
    by: process:panaversity
    at: 2026-11-02T18:33:52Z
chapter: "15"
part: III
expert_status: builder-only
concepts: [ "15.4" ]
last_verified: 2026-10-31
sources:
  - id: anthropic-team-review
    title: "How the Claude Code team does code review (Anthropic, October 15, 2026, accessed 2026-10-31)"
    resource: https://claude.dev/blog/solving-code-review-at-anthropic/
generated:
  at: 2026-11-02T18:33:52Z
  by: esl-rewrite/1.2.0+ksor.1
trust_tier: unverified
build_id: sha256:e1fbcb7d9a8f8b9b7cb96875022325c02c9ba4be94cef13b301cbd39a9edad85
dirty: true
ksor_version: 0.0.60
---

**In everyday life.** An airport does not ask one guard to inspect every passenger, bag and aircraft. A scanner checks bags, a gate agent checks boarding passes, a mechanic checks engines, and a pilot walks around the plane. Each check catches a different kind of problem. Nobody asks "did a person look at everything?" They ask "which check catches this kind of problem?"

**What you will learn.** What earns trust in code you did not write, and which check should catch each kind of problem.

**Why it matters.** The number of changes grows faster than anyone's reading time, so you have to choose what a person reads.

### What is a review for?

A **code review**, or review, is a check on a proposed change before it joins `main`. It has two goals:

1. **Catch problems before they ship.** A wrong total, a leaked key, a broken rule.
2. **Keep a shared understanding.** After the merge, someone besides the author knows what changed and why.

When a person wrote every line, a second person reading every line met both goals. When agents write most of the code, that stops working. A team that keeps "a person reads every line" ends in one of two places. Either the queue of waiting changes grows without end, or people start approving without reading. The second is worse, because it looks like review.

This is the bottleneck from the chapter's opening, seen up close. Writing got cheap, and checking did not. Human time also counts twice. A person spends it once reviewing, and again fixing whatever the review missed. So the aim is to use as little human review time as you can, without raising the number of changes you have to undo.

### What earns trust in code I did not write?

No single check earns trust in code you did not write. Four layers do, and each catches what the others miss:

| Layer | What it gives you | Taught in |
| --- | --- | --- |
| Tests written first | A definition of "correct" that existed before the agent wrote a line, with expected results from a source the agent did not invent | [13.9](../programming-fundamentals/tests-first.md) |
| Strict types | A check, before anything runs, that each value is the kind the code says, in every line the checker can see, including lines no test reaches, unless the code turns the check off with `any` or a cast | [13.2](../programming-fundamentals/the-workbench.md), [14.2](../structuring-programs/types-you-write-down.md) |
| Review | A judgment on what tests and types cannot see: intent, policy, and what else the change touches | [15.5](evidence-before-the-pr.md) to [15.9](approval-scaled-by-risk.md) |
| Required checks | A promise that the first two run on every change, and that a failure stops the merge | [15.8](checks-that-block.md) |

### Why tests first, and what does first not fix?

Tests written after the code tend to describe what the code does, not what it should do. If the agent writes the code and then its tests, both come from one reading of the request, so both can agree and both be wrong ([15.1](the-change-is-the-unit-of-work.md)). Tests written first set the meaning before the code exists, so the code cannot change it. The history shows the order: a commit of failing tests, failing for the right reason, before the commit that makes them pass. But order alone is not enough. Anyone, person or agent, can misread a request before writing a single test. The real protection is where each expected value came from. It should be an agreed rule, a hand calculation, or the person who owns the policy ([15.1](the-change-is-the-unit-of-work.md)'s "sourced"). Order is how you show those values came before the code.

### Why strict types, and where does strict stop?

A type checker in its default mode passes code with no types written down at all. Strict mode makes the checker demand them, so far more of the code can be checked ([13.2](../programming-fundamentals/the-workbench.md)). It checks the code, not the data. An amount read from a file or an API is whatever arrives at run time. That is why Chapter 14 checks data at the edge with a schema ([14.4](../structuring-programs/data-models-and-schemas.md)). Tests and types overlap less than people expect, so each one catches problems the other misses. In Chapter 13's workbench example, every test passed, and only the type checker caught an amount that was still text.

![Four layers stacked. At the bottom, tests written first define expected behavior from approved requirements and fail for the intended reason. Above it, strict type checking catches type errors before the code runs, while outside data is validated at runtime. Above that, human review judges intent, policy, risk and effects elsewhere. On top, required checks run the tests and type checks on each new version. Beside the stack, three steps of test-first evidence: the new test fails for the right reason, the change is made, all the tests pass. A note says commit order alone is not proof.](img/layers-of-trust.png)

*Figure 15.8. Layers of trust. Each layer catches what the others miss. The history shows the tests came first, and their expected values say where they came from.*

### What replaces "did someone read it?"

A better question: **what could go wrong with this change, and which check, automated or human, should catch it?** "Did someone read it?" was only a stand-in for that. Each kind of problem has a check that is best at it:

| Problem | The check that should catch it |
| --- | --- |
| A wrong total or date | A test with an agreed expected value |
| A value of the wrong type | The type checker |
| An existing test weakened | A check, required on every change, that compares the tests with `main` |
| A key printed or committed | A security review, and a check for secrets |
| A rule nobody agreed | A person who owns that rule |
| A bug in code nobody tested | A review by AI that reads the whole change |

A person still reviews. But a person reviews only what a person can judge. That means policy, intent, and changes risky enough to need it ([15.9](approval-scaled-by-risk.md)). Everything a machine can check every time should be checked by a machine every time.

### What is a finding?

A **finding** is one problem a review reports, with where it is and why it matters. Its **severity** says how much it matters, from a bug to fix before merging down to a **nit**, which is a small point that does not block.

**At work.** Ana starts with the bottom layers. In the weekend branch's history, "Failing tests for the weekend rule" comes before the commit that adds the rule. At that first commit the tests fail because `payBy` does not exist yet, and the type checker agrees. That is the right reason to fail. The expected values came from AP's proposal, worked out by hand: 10 invoices, $33,109.53. Both type checkers run in strict mode. Then she writes the table for her own change. For a date moved to the wrong day, she has the four `payBy` tests. For a wrong total with the rule on, she has the test for 10 invoices and $33,109.53. For the rule switched on by accident, she has the test that the answer is unchanged with the flag off. For something else that uses due dates, she has nothing yet. That last empty row is why the change also gets a review by AI that reads the whole repository. It is also where the bug in [15.7](ai-review-on-every-pr.md) hides.

**In the case study.** The Claude Code team measures this goal against the share of merged changes that have to be reverted, and the incidents that follow a merge. If review time drops but reverts rise, the team has cut too far.[^anthropic-team-review]

**The operating rule:** trust a change through layers, with tests written first, strict types, review, and required checks that run them, and give a person only the questions a machine cannot answer.

![Six cards, each a check and the problem it is best at. Tests catch wrong totals or behavior. Type checking catches incompatible values. Test-change review catches weakened tests. Secret and logging checks catch exposed passwords and keys. AI reviews catch possible missed bugs. Human review judges intent, policy and risk. Each card names a limit, such as "only the cases covered are checked". A note says no single check is a guarantee.](img/which-check.png)

*Figure 15.9. Which check should catch this? Each check is best at one kind of problem, and a person keeps the questions only a person can judge.*

**Practice it.** [Practice: layers of trust](both-ai-vendors.md#practice-layers-of-trust), in your own tool, ten minutes.

[^anthropic-team-review]: How the Claude Code team does code review, Anthropic.
