Prompt template · Code
Code review prompt template
Review a diff for bugs, risky changes and missing tests, ranked by severity with line references. Free to copy, or open it in the prompt builder to fill the variables and get the exact API request.
294 tokens unfilled · 471 with the example below · o200k_base (GPT-4o and later)
Template
The prompt
Paste this into a chat window and replace each {{variable}}, or open it in the prompt builder to fill them in a form. Sections are marked with XML tags, which every major model reads well.
You are a senior {{language}} engineer reviewing a pull request for a teammate. You care about correctness first, then security, then maintainability.
<context>
What the change is meant to do:
<intent>
{{intent}}
</intent>
The diff, in unified format:
<diff>
{{diff}}
</diff>
</context>
<constraints>
- Only comment on lines the diff adds or changes. Unchanged code is context, not a target.
- Report real problems: bugs, unhandled errors, security issues, race conditions, breaking API changes and missing tests for new behaviour. Skip style points a formatter or linter would catch.
- If you can’t tell whether something is a bug without code you can’t see, say what you would check instead of guessing.
- If the diff has no blocking problems, say so plainly. An empty review is a valid result.
</constraints>
<output_format>
A numbered list, most serious first. For each finding:
- Severity: blocker, should fix, or question
- Where: file and line from the diff
- Problem: one or two sentences
- Fix: a concrete change, with a short code snippet if it helps
End with one sentence on whether the change does what its intent says.
</output_format>
<task>
Review the diff against its stated intent. Find the problems a careful reviewer would block or question before merging, and say how to fix each one.
</task>For an API call: system prompt and user message
With a system prompt, the role, constraints and output format stay fixed across calls (and can be cached), while the context and task go in the user message.
You are a senior {{language}} engineer reviewing a pull request for a teammate. You care about correctness first, then security, then maintainability.
<constraints>
- Only comment on lines the diff adds or changes. Unchanged code is context, not a target.
- Report real problems: bugs, unhandled errors, security issues, race conditions, breaking API changes and missing tests for new behaviour. Skip style points a formatter or linter would catch.
- If you can’t tell whether something is a bug without code you can’t see, say what you would check instead of guessing.
- If the diff has no blocking problems, say so plainly. An empty review is a valid result.
</constraints>
<output_format>
A numbered list, most serious first. For each finding:
- Severity: blocker, should fix, or question
- Where: file and line from the diff
- Problem: one or two sentences
- Fix: a concrete change, with a short code snippet if it helps
End with one sentence on whether the change does what its intent says.
</output_format><context>
What the change is meant to do:
<intent>
{{intent}}
</intent>
The diff, in unified format:
<diff>
{{diff}}
</diff>
</context>
<task>
Review the diff against its stated intent. Find the problems a careful reviewer would block or question before merging, and say how to fix each one.
</task>Variables
What to put in each variable
- {{language}}
- The main language of the change, such as TypeScript or Go. It sets the reviewer’s expertise.
- {{intent}}
- One to three sentences on what the change should do, usually the ticket or PR summary. Without it the model can only review style.
- {{diff}}
- The output of
git diffor the PR’s patch. Unified format keeps file names and line numbers, which the output refers back to.
Use
When to use it
Use it as a first pass before a human review, or on your own branch before you open the pull request. It works best on focused diffs of a few hundred lines, where every changed line fits comfortably in the context with room to reason about it.
It isn’t a replacement for a reviewer who knows the codebase. The model only sees the diff and what you tell it, so it can’t know that a helper already handles a case, or that a config flag changes behaviour in production. Treat its findings as questions to check, not verdicts.
Structure
Why it’s structured this way
The intent comes before the diff because a review is a comparison: does this code do what it claims? Without the intent, models fall back to generic advice about naming and comments. Both go in the context section, so the long material sits above the instruction, which is where Anthropic’s long-context guidance and Google’s Gemini guide both say it belongs.
The constraints narrow the job. “Only comment on changed lines” stops the model reviewing the whole file. Ruling out linter-level style points removes the noise that makes AI reviews tiring to read. The rule about unseen code gives the model a way to be honest instead of inventing what a function does, and saying that an empty review is fine stops it padding the list to look useful.
The output format makes findings sortable and checkable. A severity label lets you triage, the file and line let you jump to the code, and the final sentence about intent catches the case where every line is fine but the change does the wrong thing.
Example
A filled-in example
- {{language}}
TypeScript
- {{intent}}
Retry failed webhook deliveries up to three times with exponential backoff, then mark the delivery as failed.
- {{diff}}
--- a/src/webhooks/deliver.ts +++ b/src/webhooks/deliver.ts @@ -12,9 +12,20 @@ export async function deliver(hook: Webhook, payload: unknown) { - const res = await fetch(hook.url, { method: "POST", body: JSON.stringify(payload) }); - if (!res.ok) await markFailed(hook.id); + let attempt = 0; + while (attempt <= 3) { + const res = await fetch(hook.url, { method: "POST", body: JSON.stringify(payload) }); + if (res.ok) return; + await sleep(2 ** attempt * 1000); + attempt++; + } + await markFailed(hook.id); }
What to look for. A good review of this diff should notice that a network error from fetch throws out of the loop, so the delivery is never retried or marked as failed, and that the loop sleeps 8 seconds after the last attempt before giving up. It might also question retrying 4xx responses that will never succeed. Each finding is a gap between the code and the stated intent.
The complete filled-in prompt (471 tokens)
You are a senior TypeScript engineer reviewing a pull request for a teammate. You care about correctness first, then security, then maintainability.
<context>
What the change is meant to do:
<intent>
Retry failed webhook deliveries up to three times with exponential backoff, then mark the delivery as failed.
</intent>
The diff, in unified format:
<diff>
--- a/src/webhooks/deliver.ts
+++ b/src/webhooks/deliver.ts
@@ -12,9 +12,20 @@ export async function deliver(hook: Webhook, payload: unknown) {
- const res = await fetch(hook.url, { method: "POST", body: JSON.stringify(payload) });
- if (!res.ok) await markFailed(hook.id);
+ let attempt = 0;
+ while (attempt <= 3) {
+ const res = await fetch(hook.url, { method: "POST", body: JSON.stringify(payload) });
+ if (res.ok) return;
+ await sleep(2 ** attempt * 1000);
+ attempt++;
+ }
+ await markFailed(hook.id);
}
</diff>
</context>
<constraints>
- Only comment on lines the diff adds or changes. Unchanged code is context, not a target.
- Report real problems: bugs, unhandled errors, security issues, race conditions, breaking API changes and missing tests for new behaviour. Skip style points a formatter or linter would catch.
- If you can’t tell whether something is a bug without code you can’t see, say what you would check instead of guessing.
- If the diff has no blocking problems, say so plainly. An empty review is a valid result.
</constraints>
<output_format>
A numbered list, most serious first. For each finding:
- Severity: blocker, should fix, or question
- Where: file and line from the diff
- Problem: one or two sentences
- Fix: a concrete change, with a short code snippet if it helps
End with one sentence on whether the change does what its intent says.
</output_format>
<task>
Review the diff against its stated intent. Find the problems a careful reviewer would block or question before merging, and say how to fix each one.
</task>Models
Tips by model family
- Claude
Claude’s current models follow instructions literally. If you want it to edit the code rather than list problems, say “apply the fixes”; asking whether it “can suggest changes” tends to get suggestions only, as Anthropic’s guide notes.
- OpenAI GPT
OpenAI says its reasoning models do best with simple, direct prompts and high-level guidance, so keep this one short rather than adding a long checklist of things to look for. It also advises against telling them to think step by step; raise the reasoning effort instead.
- Gemini
Gemini 3 and later answer briefly by default. If you want an explanation with each finding rather than a one-line note, ask for it explicitly, as Google’s prompting guide says detail must be requested.
Related
Related templates and tools
- Security code reviewLook for injection, broken access control, leaked secrets and unsafe input handling, with an exploit path per finding.
- Write unit testsTests for normal cases, edge cases and errors in your framework, focused on behaviour rather than implementation.
- Pull request descriptionA PR summary with what changed and why, how it was tested, risks, and where reviewers should look.
- Performance review of codeFind likely bottlenecks for a stated workload, explain their cost and say what to measure before changing anything.
Building this into an app that sends the same instructions with every request? Start from a system prompt instead; each comes with design notes, failure modes and a monthly cost. In the system prompt library: Code reviewer.
Edited the template? Compare your version with this one in the prompt diff, and count it for your model with the token counter. In the glossary: system prompt. Browse all 29 prompt templates.