A code review checklist that still works when AI writes the code
Most code review checklists you'll find online were written for a world where a human typed every line and the reviewer's job was catching human mistakes: typos, laziness, tunnel vision, the kind of sloppiness that announces itself with a weird variable name or a commented-out block that never got cleaned up. That world is mostly gone. The majority of diffs now arrive with an AI co-author, and AI code fails in a completely different way: it is clean, confident, idiomatic, and wrong in ways that read as right, which means the old checklist catches the old bugs and sails past the new ones. Here's a version built for both, short enough that you can actually run it on every pull request instead of bookmarking it and never looking at it again.
Start with the requirement, not the code
The single most expensive category of review miss is not a crash, not a security hole, not even a logic error in the traditional sense. It's code that solves a subtly different problem than the one that was asked for. A discount capped at the wrong boundary, a date filter that excludes the day it should include, a retry that retries the wrong operation. This is the kind of bug where the code is beautiful and the tests are green and the PR description matches what was built, and the only thing wrong is that what was built doesn't match what was needed. Read the requirement first, read the ticket or the spec or whatever document describes what this change is supposed to do, and then read the diff, and check them against each other line by line. If you read the diff first, you'll evaluate it on its own terms and it will almost always look reasonable, because the model that wrote it is very good at making things look reasonable.
What happens when things go wrong
For every function in the diff, there are really only three questions worth asking about edge cases, and between them they catch most of what the happy path hides. What happens on empty input: zero items, empty string, null, a field that's missing from the object entirely? What happens at the boundary: the first element, the last one, exactly at the limit, one past it? And what happens when the thing this function calls fails: a timeout, an exception, a partial result where the function expected a complete one? A diff that only handles the path where everything works is not finished code, it's a draft, and the part that deserves the most suspicion is code that catches an exception and continues without surfacing it, because silently swallowed errors pass every test you can write and then rot quietly in production until someone has an untraceable incident at 2am.
Following data across boundaries
Anywhere external input crosses a trust boundary in the diff, user input, request parameters, file contents, webhook payloads, environment values, the question is always the same: is it validated and escaped, or is it interpolated raw into a query, a shell command, a template, a path? But the subtler version of this question, the one that catches the bugs the obvious version misses, is about ordering. Escaping that happens after expansion sanitizes nothing, as the Snowflake CI incident demonstrated in June when a diff that added sed escaping looked like a security improvement and actually created an injection hole, because the GitHub Actions template expanded the user input before the shell ever ran the escaping. Check the order of operations, not just whether sanitization exists somewhere in the function. And check what the code can reach from where it sits: secrets in scope that it doesn't need, permissions wider than the task requires, paths that could escape their intended directory.
Would a bug actually turn a test red
Don't check whether tests exist. Check what they assert. A test that pins the current behavior proves exactly nothing about the correct behavior, because the current behavior might be wrong, and the test will faithfully stay green while the wrong thing keeps happening. The question that actually matters is: if you introduced the specific bug you're worried about, say you flipped a comparison or changed a boundary by one, would any test in this suite go red? This matters double when the same model wrote both the code and its tests, because then the test suite is a self-portrait: it verifies that the code does what the code does, which is a tautology dressed up as coverage.
Readability, but keep it last
Naming that tells the truth, functions that do one thing, no cleverness that needs a comment to decode. Also the quieter forms of debt that accumulate in AI-written code specifically: logic that got duplicated because the model didn't know about the existing implementation, and abstractions built for requirements that nobody has yet. This is the least urgent section on the list and also the one that most reviewers spend the most time on, because style comments are easy to make and feel productive, and the mental effort of spotting a boundary-condition bug is much higher. Keep readability last on purpose. Style comments are cheap to make and cheap to fix. A missed correctness bug is neither.
The three checks that barely mattered five years ago
There's a second layer to this that's specific to AI-generated code, and it catches entire bug classes that the traditional checklist never needed to worry about.
The first is verifying that everything the diff imports actually exists. Models invent packages, methods, and APIs that sound completely plausible: a lodash helper that was never part of the library, a method with the right name and wrong signature, a package name that's one typo away from the real one. Attackers have started registering those hallucinated names on npm and PyPI, a supply chain attack called slopsquatting, and the window between "the model made up a name" and "someone hostile owns that name" is closing fast. Any dependency you don't personally recognize deserves thirty seconds of verification: does it exist, is it the canonical package with that name, and is it maintained by someone real?
The second is distrusting diffs that look like improvements. AI-written changes often read as hardening: added escaping, added validation, a tidier pattern that replaces something that looked clunky. The failure mode is that the change reorders execution or deletes the one pattern that was load-bearing, while looking like it's being responsible. When a diff touches how data flows through execution contexts, template then shell, decode then validate, check then use, review the ordering, not the apparent intention.
The third is checking fidelity to the original request, not just quality of the code, and yes, this is the same thing as the first check on the list. It's here twice on purpose, because with AI-generated code it is the single most common miss. Models produce excellent, well-tested, cleanly structured code for a slightly wrong interpretation of what was asked. The units are milliseconds where the spec said seconds, the function is exclusive where the requirement was inclusive, the field is renamed where it should have been added alongside the old one. Every quality check passes. The requirement doesn't.
The part the checklist can't do for you
A checklist tells you where to look, but it can't make you recognize the bug when you're staring straight at it. That recognition is a trained skill, and the job trains it terribly: you approve, the PR merges, and nothing ever circles back to tell you what you missed. A 2026 benchmark of AI review tools found that the best one catches about half of real production bugs, and nobody has ever run a comparable benchmark on the human who's supposed to catch the other half.
The fix is the same as it is for every other trainable skill: reps with feedback. Review diffs where the bug is known ahead of time, commit to a verdict before you see the answer, and then find out exactly what you caught and what you walked past. Run this checklist against twenty such diffs and you'll learn something genuinely useful about yourself: which of these checks you actually execute under time pressure, and which ones you only believe you do.
Try the checklist
Run these checks on a real diff and see what you catch
200+ realistic AI-written PRs. Pick one, run the checklist, get graded. Free, no signup.
Browse the library →Common questions
What should a code review checklist include?
Five core areas cover most of what matters: correctness against the actual requirement (not just "does the code look reasonable"), edge cases and error handling, security of everything that crosses a trust boundary, whether the tests would actually fail if the code were wrong, and readability for the next person who has to touch it. For AI-generated code specifically, add three more: verify that every API and package in the diff actually exists (models hallucinate plausible-sounding names constantly), question code that looks like a safety improvement but reorders execution, and check that the change does what was asked rather than something confidently adjacent to it.
Is a checklist enough to review AI-generated code?
A checklist tells you where to direct your attention, but it can't substitute for the pattern recognition that spots a plausible-but-wrong line when you're looking straight at it. That part has to be trained, and it has to be trained on real diffs with known bugs and honest feedback about what you missed, because reading about bug patterns and actually catching them in a diff are two very different skills.
Where can I practice going through a checklist on real diffs?
On DiffDojo's free library: over 200 realistic AI-written pull requests across Python, TypeScript, React, Angular and Node, each with planted bugs (or deliberately clean code where the right call is to approve), and a canonical review that grades you on what you caught. No signup needed, and you'll find out which items on this checklist you actually run versus which ones you skip under time pressure.
Read next How to review AI-generated code: a guide for the human in the loop