Code review is the bottleneck nobody budgets for. A pull request sits in the queue, a senior engineer skims the diff between two meetings, approves it with a thumbs-up, and three days later an edge case with a null payload takes down checkout. The uncomfortable truth: most review bugs are not subtle logic errors — they are things a careful reader would have caught if they had time to look. That is exactly the gap an LLM can fill.
This is not about replacing human review. It is about using a model as a first pass — a tireless reader that checks the diff against a checklist before a human ever opens it. The catch is that "review my code" produces generic noise. The prompts below are specific: each one targets a failure class, names the stack it fits, and tells you which review stage it belongs to.
A quick note on method before we start. Paste the diff, not the whole repo. Give the model the contract (what the function promises), not just the code. And always ask it to cite the line — an unanchored finding is a hallucination waiting to happen.
The prompt table at a glance
| # | Prompt target | Best stage | Stack |
|---|---|---|---|
| 1 | Edge-case sweep | First pass | Any |
| 2 | Race-condition hunt | Concurrency-heavy | Go, Java, Python async |
| 3 | Null and undefined paths | First pass | TS, Java, Python |
| 4 | Readability and naming | Late pass | Any |
| 5 | Security diff review | Before merge | Any |
| 6 | API backward compatibility | Public APIs | REST, GraphQL |
| 7 | SQL and N+1 detection | Data layer | ORMs |
| 8 | Error-handling audit | First pass | Any |
| 9 | Test-gap finder | Before merge | Any |
| 10 | Resource-leak check | Before merge | Go, C, Rust |
| 11 | Dependency and config diff | Dependabot PRs | Any |
| 12 | The "walk the diff" re-prompt | Second pass | Any |
The 12 prompts
1. The edge-case sweep
Task: force the model to enumerate inputs the author did not consider.
Prompt:
You are reviewing a diff. For each changed function, list inputs that
are NOT covered by the code paths shown: empty string, zero, negative,
very large, unicode, null. For each, state the line and what breaks.
If nothing breaks, say "safe" and why. Do not summarize the code.
Example result: parsePrice("") returns NaN at line 14 because parseFloat on empty string yields NaN and the null check happens after. Finding: move the check before parsing.
2. The race-condition hunt
Task: catch interleavings a linear read misses.
Prompt:
Assume two goroutines run this function concurrently. Enumerate the
interleavings that produce a wrong result. Name the shared variable
and the line where the read/write order matters. If there is a lock,
check it covers every access.
Example result: a counter incremented outside the mutex at line 22 — under load, lost updates appear intermittently. This is the class of bug that ships to production and reproduces once a week.
3. Null and undefined path tracing
Task: follow every optional value to where it is used.
Prompt:
Trace every value that can be null/undefined in this diff to each
place it is used. Flag any dereference that is not guarded. Show the
path: parameter -> call -> dereference line.
Example result: user.address is optional in the type, but formatLabel reads user.address.city at line 51 without a guard.
4. Readability without taste
Task: get naming and structure critique that is not subjective.
Prompt:
Critique readability only. Flag: names that hide intent, functions
longer than one screen, nested conditionals deeper than two levels.
For each, propose a rename or a guard clause. Do not comment on style
formatting — the linter owns that.
Example result: handleData → normalizeInvoicePayload; a three-level if flattened with early returns.
5. The security diff pass
Task: scan changes for injection, secrets, and auth gaps.
Prompt:
Review this diff for security only: SQL/command injection, secrets
committed in code, missing authorization checks, unsafe deserialization.
For each finding give severity (high/medium/low) and the exact line.
Example result: a query built with string concatenation at line 33 — high severity, parameterize it.
6. API backward compatibility
Task: catch breaking changes before clients do.
Prompt:
Compare the old and new signature of each public endpoint. Flag
removed fields, renamed fields, changed types, and new required
parameters. For each, say whether existing clients break.
Example result: userId: number → string breaks typed clients silently at runtime — a classic that slips past unit tests.
7. SQL and N+1 detection
Task: spot query patterns that scale badly.
Prompt:
Inspect ORM calls in this diff. Flag any query inside a loop (N+1),
any missing index hint on a filtered column, and any SELECT * on a
wide table. Explain the cost at 10k rows vs 1M rows.
Example result: a .find() per order in a list render — 1 query becomes N. At 500 orders that is 501 round-trips.
8. Error-handling audit
Task: find swallowed exceptions and silent failures.
Prompt:
List every try/catch and error branch in the diff. Flag catches that
swallow without logging, generic catches that hide the cause, and
paths where a failure returns success. Quote the line.
Example result: catch (e) {} at line 88 — a failed write reports success to the caller.
9. Test-gap finder
Task: map changed lines to missing tests.
Prompt:
For each changed branch in this diff, state whether a test covers it.
List the uncovered branches as a checklist. Do not write the tests —
just name the missing case.
Example result: the if (retryCount > 3) branch has no test. That is where the retry storm will hide.
10. Resource-leak check
Task: catch unclosed handles and dangling locks.
Prompt:
Check every opened resource in this diff (file, connection, lock,
channel) is closed on all exit paths, including error returns.
List any path where it leaks.
Example result: a defer rows.Close() missing after an early return err at line 40.
11. Dependency and config diff
Task: review the boring PRs nobody reads.
Prompt:
This diff changes dependency versions and config. List: major version
bumps, removed packages still imported, and config keys added or
renamed. Flag anything that needs a migration note.
Example result: a major bump of a parser library — check the changelog for breaking regex behavior before merging.
12. The walk-the-diff re-prompt
Task: the second pass that catches what the first missed.
Prompt:
Here are the findings from the previous review. For each, state
whether it is a real bug or a false positive, and why. Then scan the
diff once more for anything the first pass missed.
Example result: two of five findings are false positives (the model misread a guard on line 60); one new finding surfaces — an off-by-one in a pagination slice.
What this actually buys you
The trade-off is real. You gain coverage — a checklist that never gets tired — and you pay in false positives and context limits. A model that sees only the diff cannot know that a helper three files away already guards the null. That is why prompt 12 exists: the second pass filters the first.
If you are building these workflows into a broader practice, the same discipline — narrow prompt, named failure class, anchored output — carries over to AI for Everyday Life, where vague prompts fail the same way.
The point is not to automate review. It is to make the human review that remains shorter and sharper. Run prompt 1 and 5 on every PR, prompt 2 only where concurrency lives, and prompt 12 always. Your reviewer's thumbs-up will mean something again.