How much human review for AI-assisted code is enough?

19 min read

Set human review for AI-assisted code by change size and sensitivity, with clear tiers for tests, security scans, dependencies, and approvers.

How much human review for AI-assisted code is enough?

AI can shorten the time needed to draft a change, but it does not shorten the list of things that can go wrong in production. Human review should scale with the change's blast radius, the sensitivity of the code, and the strength of the evidence attached to the pull request. The amount of text an AI wrote is almost irrelevant.

I use four review tiers. A tiny, reversible change in an isolated component may need one competent reviewer and focused tests. A change that touches identity, money, health data, permissions, deployment, or a shared dependency needs independent reviewers who understand that domain, wider tests, security evidence, and an explicit release decision. This is not distrust of AI. It is ordinary engineering control applied to a source that can produce plausible code faster than a team can inspect it.

The useful policy fits into a repository, runs in continuous integration, and tells authors exactly what evidence they owe. A policy that says every AI change needs extra care will collapse into checkbox approval. A policy that maps observable facts to named gates can survive a busy release day.

Review the consequence, not the author

The right review tier comes from what the change can affect, not whether a person typed it, copied it, generated it, or revised generated code. Provenance still matters because reviewers need to know what was independently verified. It does not replace risk classification.

Start with two axes: change size and code sensitivity. Size includes more than lines changed. Count the number of components crossed, public interfaces altered, data migrations introduced, generated files replaced, configuration scopes changed, and execution paths affected. A twelve line authorization change can carry more risk than a two thousand line test fixture. Diff size is a routing hint, not a safety measure.

Sensitivity asks what authority the code holds and what harm a mistake could cause. Treat authentication, authorization, cryptography, secrets, billing, personal data, clinical workflows, infrastructure permissions, build pipelines, dependency manifests, and destructive data operations as sensitive. Add product specific areas such as entitlement rules or safety limits. Keep this list in version control so an author cannot silently reinterpret it when a deadline gets tight.

Reversibility is the third fact that changes the answer. A faulty page label behind a flag can be disabled in minutes. A migration that deletes columns, an event published to outside consumers, or a credential exposed in a log cannot be cleanly recalled. Raise the tier when rollback depends on restoring data, coordinating another team, or asking customers to act.

A practical classifier records these facts in the pull request rather than asking for a vague risk rating:

  • Which services, data stores, and public interfaces change?
  • Does the code make or enforce a security or business decision?
  • Can the team reverse it without losing or reprocessing data?
  • Does it alter dependencies, build instructions, or deployment permissions?
  • What evidence shows the changed behavior works and the old behavior still works?

Do not let authors choose a low tier from intuition alone. Repository rules can promote a change automatically when protected paths, migration folders, package manifests, infrastructure files, or a large diff appear. A reviewer may also promote it after reading the design. Demotion should require a recorded reason and the approver who accepted it.

Four tiers make the policy usable

Four tiers are enough for most teams. More categories create debates over labels, while fewer categories force routine work and dangerous work through the same gate. The tier names matter less than the entry conditions and required evidence.

Tier 1 covers narrow, reversible changes. Examples include copy, isolated styling, test data, comments, and small internal refactors with no behavior change. Require the normal build, linting, relevant unit tests, and one reviewer who owns or understands the area. Automatic merge can be acceptable after approval if branch protection prevents the author from approving the change and every required check reports success.

Tier 2 covers ordinary production behavior. This includes bounded feature logic, API behavior that preserves the contract, fixes in established code, and modest configuration changes. Require one independent reviewer, focused tests for the changed behavior, the full affected test suite, static analysis, secret scanning, and a clean dependency review. The pull request should explain what AI produced, what the author changed afterward, and which claims the author verified outside the generated explanation.

Tier 3 covers sensitive or wide changes. Route identity, access control, payment flow, health information, migrations, shared libraries, deployment policy, public contracts, and changes spanning several components here. Require two reviewers, including a domain owner; add integration or end to end tests at the boundary that carries risk; run security analysis suited to the language; inspect dependency and lockfile changes; and require a deployment and rollback plan. The second reviewer should not repeat the first review. Assign one to behavior and design, the other to security, data, or operations.

Tier 4 covers changes with severe or hard to reverse consequences. Examples include cryptographic design, privilege boundaries, bulk data transformation, production identity policy, build trust, clinical decision support, and a new external data exchange. Require a design review before implementation, named owners from the affected domains, threat modeling, tests against misuse and failure, staged release evidence, and an explicit person who accepts residual risk. Some teams also require a release manager or change board. Use that only when the person has the context and authority to stop the release; ceremonial approval adds delay without control.

A machine readable policy keeps the mapping reviewable. This example is intentionally simple so a team can adapt it to its own CI system:

review_tiers:
  tier_1:
    approvals: 1
    checks: [build, lint, unit]
  tier_2:
    approvals: 1
    checks: [build, unit, affected_suite, static_analysis, secrets, dependencies]
  tier_3:
    approvals: 2
    required_roles: [code_owner, domain_owner]
    checks: [tier_2, integration, security_scan, rollback_test]
  tier_4:
    approvals: 3
    required_roles: [code_owner, security_owner, release_owner]
    checks: [tier_3, threat_model, misuse_tests, staged_release]
protected_paths:
  tier_3: [auth/, billing/, migrations/, infra/, package-lock.json]
  tier_4: [crypto/, production/identity/, clinical/decision-support/]

The failure this prevents is common: an author labels a permissions change as small because the diff has eight lines, gets a quick approval, and ships a branch that grants access when a lookup fails. A protected path raises the tier before anyone argues about line count. Tests then have to cover denial, missing data, and service failure, not just the successful request.

Tests must prove the risky claim

Test count says little about review readiness. The useful question is whether the tests would fail for the plausible defects introduced by this specific change. Generated code often arrives with generated tests that confirm the same mistaken assumption. A green suite can show internal consistency between two wrong artifacts.

Require the author to state the risky claim in plain language, then connect it to evidence. For an access check, the claim might be that only clinicians assigned to a case can view a record. Evidence should cover an assigned clinician, an unassigned clinician, a user with no clinical role, a missing assignment service response, and a stale session. A test that proves the assigned clinician succeeds covers only the happy path.

Reviewers should perform a mutation in their head or in the code: change && to ||, remove the tenant filter, return success on an exception, or make an empty collection pass. If the proposed tests stay green, the tests do not guard the decision. Mutation testing tools can automate part of this, but a five minute manual mutation is often enough to expose a generated test that merely repeats the implementation.

Run tests at the narrowest level that can disprove the claim, then add boundary tests where components disagree. Unit tests are good for branches and invariants. Contract tests catch mismatched request and response assumptions. Integration tests expose database constraints, transaction boundaries, queues, caches, and identity middleware. End to end tests belong on a small set of journeys whose failure would block release. Making every tier run every end to end test wastes time and teaches teams to ignore slow, flaky results.

Review the tests as carefully as production code. Watch for mocks that bypass the permission layer, fixtures that omit realistic nulls, assertions that only check status codes, snapshots that accept broad unrelated changes, and retries that hide races. AI is particularly good at producing impressive test volume around an interface it misunderstood.

The author should attach the exact commands used when a local check is not already enforced by CI. A reviewer can reproduce a focused run and see an output shape like this:

$ npm test access-policy.test.ts
PASS access-policy.test.ts
  assigned clinician can read (18 ms)
  unassigned clinician is denied (7 ms)
  missing assignment response fails closed (9 ms)
Tests: 3 passed, 3 total

The output is evidence only if the commit in the pull request produced it. Prefer CI artifacts tied to the commit over pasted screenshots. For Tier 3 and Tier 4, preserve test reports, scan results, and approval records with the release so an incident responder can reconstruct what the team knew.

Sensitive code needs an independent security view

A general code review and a security review answer different questions. The first asks whether the implementation is correct and maintainable. The second asks how an attacker, a compromised dependency, a malicious input, or an overprivileged internal actor can make the system violate its security requirements. Combining both into a single checkbox hides the distinction.

OWASP Application Security Verification Standard gives teams a useful vocabulary for this work. ASVS defines verification levels with increasing rigor and groups requirements around areas such as authentication, access control, validation, cryptography, logging, data protection, APIs, and configuration. I would not copy the whole standard into every pull request. Map the product's sensitive paths to the applicable requirements, then make those requirements visible when a change touches them.

NIST SP 800-218 makes a related point in its Secure Software Development Framework. Its design review practice calls for a qualified person who was not involved in the design, automated processes in the toolchain, or both, and it asks teams to record findings as artifacts. The qualification matters. A random second approval does not satisfy the intent when neither reviewer understands the trust boundary being changed.

Static application security testing, secret scanning, and dependency scanning are useful gates, but they cannot decide whether a nurse should see a particular patient's record or whether a refund rule permits abuse. Tools find patterns. People must inspect business authorization, tenant isolation, failure behavior, audit semantics, and the relationship between data collected and data actually needed.

For a Tier 3 security change, require a short abuse case alongside the normal acceptance case. A good abuse case names an actor, the capability they should not have, the path they might try, and the control that blocks it. For example: a support user changes the account identifier in a request; server side authorization rejects the request before the record lookup; the audit entry records the denied attempt without storing the sensitive payload.

Tier 4 needs a threat model before the implementation becomes expensive to change. Keep it concrete: assets, trust boundaries, entry points, attacker capabilities, controls, and unresolved decisions. The security owner should review the model and the code, while the service owner verifies operating assumptions such as identity availability, clock behavior, data retention, and rollback.

Do not approve sensitive code because the AI explains it convincingly. Generated explanations can rationalize the implementation they were given, including a flawed one. Trace every security claim to a test, a configuration, a reviewed design decision, or observed platform behavior.

Dependency changes carry code you cannot see in the diff

A one line manifest edit can add thousands of lines of executable code through direct and indirect dependencies. That makes dependency changes a separate review dimension, not a small variation of source review. Promote any new runtime dependency and any meaningful lockfile rewrite to at least Tier 2. Raise it further when the package runs during builds, handles untrusted data, receives secrets, or executes in a sensitive service.

A dependency check should answer which packages were added, removed, and updated; whether indirect packages changed; whether known vulnerabilities apply; what licenses enter the product; and whether the expected registry and package identity match. GitHub's dependency review, for example, compares dependency changes between the base and head commits and can enforce a failure threshold in a pull request. Other hosting systems have equivalents. The policy outcome matters more than the vendor.

Do not accept a giant lockfile diff with the explanation that the package manager made it. Regenerate it from the declared manifest using the repository's pinned package manager, then compare the result. Unexpected registry URLs, lifecycle scripts, package name lookalikes, integrity changes, and unrelated upgrades deserve investigation.

New dependencies also need a human necessity check. Teams often add a package because generated code imports it, then review only whether the package has a published vulnerability. Ask whether the standard library or an existing dependency already does the job, whether the package is actively maintained, what code runs at install time, and how difficult removal would be. Vulnerability databases report known defects; they cannot tell you that a package is an unnecessary expansion of trust.

For Tier 3 and Tier 4 releases, retain a software bill of materials when the build system can produce one, and keep provenance for the built artifact. SLSA treats provenance as verifiable information about where, when, and how an artifact was produced. That does not prove the code is safe, but it lets a reviewer connect the deployed artifact to the reviewed source and build process.

Explicit approvers prevent rubber stamps

Two approvals do not help if both reviewers assume the other checked security, data behavior, and deployment. Every required approval should have a named concern. Code owners can route a change to people, but the pull request template must tell each person what decision they own.

Use roles that match the risk: implementation owner, domain owner, security owner, data owner, and release owner. A Tier 2 change may need only an implementation owner. A migration that changes patient data could need a domain owner to verify meaning, a data owner to verify migration and recovery, and a security or privacy owner to check exposure. One person may fill two roles when qualified, but the record should say so.

The author cannot provide the independent approval. Nor should the person who prompted the AI be treated as independent merely because the model produced the code. The author owns the change: understanding it, editing it, testing it, and explaining its limits. If the author cannot explain a branch or dependency, the review should stop until they can.

Approval comments should record a decision, not social reassurance. A useful Tier 3 approval might say: reviewed tenant isolation in the query and service layers; reproduced denial for a user in another tenant; checked migration rollback against a copy of representative data; accepting the remaining risk that rollback requires a brief write pause. That note gives the release owner and a future incident responder something concrete.

Dismiss approvals when the author pushes a material change after review. Define material mechanically where possible: changes to protected paths, dependency files, migrations, permissions, or more than a small configured diff. A comment typo should not restart a three person review, but a new error handler in an authorization path should.

Avoid approval by exhaustion. Large generated pull requests are hard to review because the model can produce them quickly and the reviewer still reads at human speed. Set a reviewable size limit, then require the author to split independent changes or provide a sequence of commits that separates mechanical edits from behavior. Do not waive scrutiny because splitting the change feels inconvenient. If the code cannot be separated, promote the tier and reserve focused review time.

A distributed team also needs a clear handoff. Record which checks remain, who can approve them, and whether deployment is blocked. SaaS Production uses experienced engineers to control its AI assisted development process, which is the right division of labor: AI accelerates production, while people retain the decisions that require context and accountability.

AI output needs verification beyond the diff

Reviewers need to inspect the assumptions around generated code, not only its syntax. AI can call an API that does not exist, use a real API with the wrong version, invent a configuration field, copy an obsolete security pattern, or silently broaden the requested behavior. The resulting code may compile if mocks or loose types conceal the error.

Ask the author to disclose AI use at the change level, not to confess every completion. The useful record says which files or functions were substantially generated, which model supplied the draft if policy requires that fact, what external material entered the prompt, and what the author independently checked. Do not paste sensitive prompts or proprietary data into the pull request. The disclosure exists to direct review effort and support provenance, not to shame the author.

Verify code adjacent claims against authoritative sources. If generated code uses a framework security option, read the installed version's manual and inspect the default. If it calls a cloud service, compare the request and response with the official API schema. If it implements a protocol, test the relevant RFC behavior. Model memory is not evidence, and a plausible citation that nobody opened is worse than no citation because it can end the review prematurely.

Generated changes need a scope check against the request. Compare the accepted task with the diff and list behavior the change adds beyond that task. Watch for new logging, telemetry, fallback behavior, dependencies, configuration, network calls, and error recovery. These extras often look helpful but can create privacy, cost, and security consequences that nobody asked to accept.

Check data exposure in both directions. Review what the author sent to the AI system under the organization's rules, then review what the generated code sends at runtime. A harmless looking helper can serialize an entire object when the API needs two fields. Tests should assert the outgoing shape and confirm that logs, exceptions, and analytics exclude secrets and regulated data.

Finally, require ownership without demanding a theatrical manual rewrite. Requiring people to retype generated code does not make it safer. Requiring them to explain invariants, reproduce tests, verify APIs, trim unnecessary scope, and respond to review does. The standard is comprehension backed by evidence.

Exceptions need an expiry and a recovery plan

Production incidents sometimes justify merging with incomplete evidence, but urgency does not make the missing control disappear. An exception should name the failed or skipped gate, the immediate harm the change prevents, the person accepting the added risk, the limit on exposure, the rollback trigger, and the time when normal review will be completed.

Keep the emergency change as small as possible. Avoid dependency additions, opportunistic refactors, formatting sweeps, and unrelated generated fixes. Use a feature flag, traffic limit, targeted configuration, or a reversible patch when the system permits it. Pair the author with the incident commander or service owner, and capture commands and observations in the incident record.

Some gates should remain nonnegotiable even during an incident. The code must come from an authenticated contributor, the build must identify the commit, basic tests must run unless the test system itself is broken, secret scanning must not report a new credential, and a named person must authorize production. If a gate cannot run, record that fact and use the best independent check available. Silence is not a substitute.

Set an expiry measured in hours or days according to the system, not an exception with no end. The follow up review should decide whether to keep, replace, or revert the emergency change. It should also add the missing test and examine why the normal path could not respond quickly enough. Track this work as part of the incident, with an owner and due time.

Never create a permanent low tier for fixes labeled urgent. That label will spread to normal work. Preserve one exception path with stricter recording and later review, then measure how often teams use it. Frequent exceptions usually point to slow tests, unavailable owners, unclear policies, or deployments that are too hard to reverse.

Calibrate tiers with evidence from your releases

A review policy should change when production evidence shows that it routes work badly. Record the assigned tier, why it was assigned, review time, which gates found a problem, changes requested after approval, rollback or incident links, and whether an exception was used. Do not turn those records into a contest over reviewer speed.

Look for routing errors. If Tier 1 changes repeatedly trigger production fixes, the protected paths or entry conditions are too weak. If Tier 3 changes wait for days while reviewers find nothing outside the normal test suite, the tier may demand the wrong specialist or duplicate a reliable automated check. If security scans report the same unactionable pattern, tune the rule and document the reason instead of training reviewers to ignore red builds.

Sample approved changes as well as failed ones. A periodic review by a senior engineer can compare the diff, evidence, tier, and production result. The goal is to find false confidence: approvals that lacked an owned decision, tests that did not cover the risky claim, dependency reviews that missed indirect changes, or rollback plans that were never executable.

Keep the first implementation plain. Put the tier table in the repository, protect sensitive paths, require status checks, assign code owners, and add fields for risky claim, evidence, rollback, and AI verification. After several release cycles, adjust the mapping with observed failures and delays.

Human review is enough when an independent person with the right domain knowledge can connect every material claim to evidence and can stop the release. For low consequence work, that may take minutes. For code that can expose records, move money, grant authority, or corrupt data, the team owes production a slower and more explicit decision, regardless of how quickly the first draft appeared.

Frequently Asked Questions

Does all AI-generated code need human review?

Yes, production code needs accountable human review, but it does not all need the same depth. Route a narrow reversible edit differently from a change to permissions, payment, health data, deployment, or build trust.

Can automated tests replace a human code reviewer?

No. Tests can prove selected behavior, while a reviewer checks whether the selected behavior, scope, assumptions, and risk are right. Strong automation can reduce routine inspection, but a person must own the production decision.

How should a team classify a small but sensitive code change?

Sensitivity should override line count. A tiny change in authorization, cryptography, secrets, migrations, or production identity belongs in a higher tier because its consequence can be large and hard to reverse.

Should reviewers know which code was written by AI?

They should know which parts were substantially generated so they can inspect assumptions and verification evidence. The disclosure should direct attention, not excuse the author from understanding every line.

How many approvers should a high-risk AI code change have?

Use at least two independent reviewers for sensitive or wide changes, with named responsibility for the affected domains. Severe changes may need security, data, and release owners, but extra signatures without relevant expertise add little.

What security scans should run on AI-assisted code?

Run static analysis suited to the language, secret scanning, and dependency checks as a baseline for ordinary production changes. Add threat modeling, misuse tests, and manual review of authorization and data flow when the code touches a sensitive boundary.

How should dependency updates generated by AI be reviewed?

Inspect direct and indirect changes, known vulnerabilities, licenses, registry identity, integrity data, and install scripts. A person should also decide whether the new dependency is necessary because scanners cannot judge avoidable expansion of trust.

Can an urgent production fix bypass the review tiers?

It can use a documented exception path, but it still needs an authenticated author, identified commit, basic evidence, a named production approver, and a rollback trigger. Give the exception an expiry and complete the missing review after the incident.

What evidence should an AI code pull request include?

Include the risky behavior claim, focused test results tied to the commit, required scan results, dependency changes, and a rollback plan when reversal is not trivial. For generated code, add the APIs and assumptions the author independently verified.

How often should code review tiers be updated?

Review them after enough releases to see recurring routing errors, and immediately after a serious miss exposes a bad rule. Use findings from tests, incidents, exceptions, and sampled approvals rather than changing policy from opinion alone.