AI code review goes wrong when it becomes a second request for confidence.

“Check this for bugs” often returns a tidy list. The list may be right. It may also miss the one path that matters because nobody said what the system must protect.

Review the consequence, not the style.

Begin with invariants

Write five sentences that must remain true. For example:

  • A user can read only records owned by their account or team.
  • A payment event changes an order once.
  • A private token never reaches the browser or a log.
  • Deleting a parent cannot leave billable or public children behind.
  • An unauthenticated request cannot trigger work.

These statements give the review a spine. Without them, a human or model reviewer drifts toward naming, duplication, and formatting because those are easy to see.

Trace one route end to end

Pick the action with the worst plausible failure. Follow it from input to output.

Where does the value enter? Which parser handles it? Which identity is trusted? Where is ownership checked? What is written? What external service is called? What happens on a retry? What does the response reveal?

Do not stop at a helper with a reassuring name. Open it. “validateUser” may validate that a user exists, not that the user owns the object.

Make the model argue against itself

Separate generation from criticism. Give the reviewer the code, the invariants, and the threat to consider. Ask for a concrete failure path with file and line evidence. Then ask what evidence would disprove each claim.

The aim is not to produce more findings. It is to make cheap guesses expensive.

Useful prompts are narrow: “Can account A cause a write to account B’s object?” “Can this webhook be replayed?” “Which values in this response came from an untrusted source?”

Test boundaries, not happy paths

Generated tests often repeat the implementation’s optimism. Add cases at the boundary: missing identity, wrong owner, empty input, maximum size, repeated request, partial provider failure, timeout after write, and stale state.

For permissions, create two users. For idempotency, send the same event twice. For output encoding, use hostile-looking text. For deletion, inspect what remains.

A test is valuable when it could embarrass the code.

Review the dependencies and deployment

Read what packages were added and why. Remove unused ones. Check maintenance status, versions, licenses, and known advisories. Confirm that secrets come from the runtime, debug flags are off, source maps and logs expose nothing private, and development routes are absent.

Keep ownership human

AI can draft, explain, search, and challenge. It cannot accept responsibility for a charge, leak, or lockout.

Before launch, a person should be able to explain the important data path in plain language. If nobody can, the system is not fast. It is merely moving faster than its owners.