How to Review AI-Generated Code Without Missing Risky Changes
AI-written code usually compiles and passes a quick test while hiding the real risks — a missing auth check, a destructive update, an untested edge case. Here's a review path that surfaces them before you merge.
Try the Code Review Prompt GeneratorWhen AI-generated code looks right but isn't
An AI writes a function, you skim it, it compiles, the one test you run passes, and you merge. A week later something breaks in a way the code looked fine for: a duplicate event charged a customer twice, a missing permission check leaked data, a migration dropped a column before anything validated the input. The code was plausible. It just wasn't safe.
AI-generated code is good at looking correct. It uses the right names, follows the shape of your codebase, and handles the case you were thinking about. The risk lives in the cases you weren't thinking about — and those are exactly the ones a quick glance skips. This guide is a review path for finding them before you merge. It gives you a process, not a guarantee: you still read the diff, run the tests, and make the call.
Why AI-generated code is hard to review
A human writing the same code leaves signals a reviewer reads instinctively — a hedged comment, an obviously unfinished branch, a TODO. AI-generated code arrives uniformly confident, so nothing flags itself. Three habits make it risky:
- It fills gaps by inventing. Asked for something underspecified, the model picks a plausible default — an auth assumption, an error behavior, a data shape — and writes it as if it were decided.
- It over-delivers. You asked for one change; it also improved three other things, so the diff is bigger than the task and the extra edits get less scrutiny.
- It optimizes for the happy path. The example that proves it works is the case it handles best; duplicates, empty inputs, failures, and concurrent calls are where it quietly cuts corners.
Step 1: Read the diff, not just the result
The fastest way to miss a risk is to judge the output instead of the change. Read the actual diff line by line: what was added, what was removed, and what moved. A deleted validation check or a quietly changed default is invisible if you only look at whether the feature works.
Reviewing an AI's diff is a specific enough job that there's a ready-made prompt for it: the Review AI-Generated Code resource is a review prompt tuned to inspect an AI's change with extra suspicion — did it do one thing, is the changed behavior covered by changed tests, did it call an API that doesn't exist. You paste it and your diff into your own assistant; it reports findings, it doesn't apply them.
Step 2: Check it against what you actually asked for
Working code that solves the wrong problem still fails review. Put the diff next to your original request and ask three things: did it do what you asked, did it do only that, and did it change anything you didn't ask for. The "only that" question is the one that catches silent scope creep — the renamed variable, the tightened timeout, the swapped library — that a passing test won't.
You can hold these criteria in your head, or structure them once: the Code Review Prompt Generator builds a review prompt with the objective, scope, criteria, severity levels, and a verdict already laid out. It makes the AI report findings — its own rule is "report findings; do not rewrite the code" — so the model judges the change without quietly editing it. It produces prompt text you run in your own assistant; it doesn't execute the code or prove it's correct.
Step 3: Walk the risk surface deliberately
This is where real bugs hide, so make it a checklist instead of a vibe. For each change, walk the surfaces where AI code most often cuts corners:
- Authorization and permissions — is every entry point checking who is allowed, or does it assume the caller is trusted?
- Destructive and migration operations — does anything delete, overwrite, or migrate data before it validates input, and is there a rollback if it fails halfway?
- Error handling — are failures surfaced, or swallowed so a broken write still looks like success?
- Concurrency and idempotency — what happens if this runs twice at once, or the same request arrives twice?
- Edge and failure cases — empty inputs, missing fields, timeouts, and the values the happy-path example never used.
- Dependencies and configuration — did it add a package, change a config default, or assume an environment variable that isn't set everywhere?
Step 4: When a change leans on one risk, use a focused review prompt
A general review spreads attention evenly, but some changes concentrate their risk in one place, and a review aimed at that place catches more. Two ready-made prompts help here.
For security-sensitive paths, the Security Code Review Prompt resource is a review prompt built around injection, per-resource authorization, secrets, and privilege-escalation checks. For a change that ships as a pull request — with migrations, deploy ordering, and cross-file impact — the Pull Request Review Prompt resource scopes the review to those PR-level risks. Both are prompts you adapt and run yourself; they surface issues for you to judge, and neither is a safety guarantee or a substitute for a real security audit.
Step 5: Make the tests cover the change, not just the happy path
AI-generated tests tend to prove the code does what it already does — they assert the happy path and stop. That passes, and it tells you almost nothing. The test that matters is the one for the case you're worried about: the duplicate event, the malformed input, the write that fails partway.
For each risk you found in Step 3, ask whether a test would catch a regression there. If not, that's the test to add before you merge — and it's often a better use of the AI than the review itself, because a failing-then-passing test is evidence, where a review is an opinion.
Step 6: Ask the AI to explain its own risky assumptions
The model made decisions it never told you about — what it assumed about the schema, the auth model, the volume, the environment. Turning it back on its own code surfaces them cheaply. Ask it plainly to list the assumptions this code makes about the system around it that, if wrong, would make it unsafe or incorrect.
The answer is a lead list, not a verdict — some assumptions will be fine, some will be exactly the gap. You decide which ones matter and check them against how your system actually behaves. It's the difference between reviewing what the code says and reviewing what it silently took for granted.
Common mistakes
A few habits let risky changes through:
- Reviewing the output, not the diff. If it runs, it looks done — but the risk is usually in a line you didn't read.
- Trusting a passing test. AI tests often cover the one path the code handles best; a green run can hide every case that matters.
- Treating the AI's verdict as approval. A review prompt ends in a verdict, but that's the model's opinion from the checks you gave it — a second opinion, not merge authority.
- Letting the model fix while you review. If it rewrites as it reviews, you lose track of what changed; review first, change second.
- Skipping security and data risks because the feature works. The feature working and the code being safe are different questions.
A worked example: an AI-written webhook handler
Say the AI wrote a billing webhook handler: it receives a payment event, marks the invoice paid, and returns 200. It compiles, and the happy-path test passes. The review is looking for what that green test hides:
- No signature check — the endpoint trusts any caller who knows the URL.
- No idempotency — webhooks retry, so a duplicate delivery marks the invoice paid twice.
- The status update runs before the event is validated, so a malformed event can still mutate data.
- The error path swallows the exception and still returns 200, so a failed write looks like success and the provider never retries.
- The test covers one valid event — nothing for duplicates, bad signatures, or a failed database write.
Review this webhook handler. Report findings only — do not rewrite it.
For each issue give: severity, the exact line, and why it matters.
Check specifically:
- Is the event authenticated (signature/secret) before anything runs?
- Is the handler idempotent if the same event arrives twice?
- Does any data change happen before the event is validated?
- Are errors surfaced, or swallowed while still returning 200?
- Do the tests cover duplicates, bad input, and a failed write — not just the happy path?
Then list the assumptions you made about the surrounding system
that, if wrong, would make this code unsafe.
Where this fits in NewPrompt
A one-off review is enough for a single change. When reviewing AI-written code becomes routine, the AI Code Review Workflow connects these checks into a repeatable pass — set up the project context, generate the review, understand the risky parts, and cover them with regression tests. And when the change is one edit inside a larger effort — modernizing a codebase you inherited, where much of the code is AI-assisted and you didn't write the original — the Modernize a Legacy Codebase with AI project has a review stage where this pass belongs, so reviewing AI-made changes is built into the path rather than bolted on. If a review turns up changes worth making, rewriting is a separate job: the Refactor Prompt Builder builds a prompt for that, so you review first and change second. Throughout, NewPrompt structures the review and the prompts; you run them in your own AI assistant, read the diff and the tests yourself, and own the merge decision.