


Code Review Checklist
Reviews a diff across correctness, security, performance, readability, and tests, ranked by severity.
What it does
Reviewing a code change well is slow, and the parts that get skipped when time is short are the parts that cause incidents: the case nobody thought about, the check that was quietly removed, the query that is fine with ten rows and terrible with ten thousand.
With this skill active, the assistant reads the change five separate times, each with one question in mind - is it correct, is it safe, is it fast enough, can a stranger read it, and is it actually tested. It reads the code around the change too, because the answer to "is this a bug" is usually three lines outside the change itself.
You get back a list sorted by how much each item matters, from things that must not ship down to optional polish. Every entry names a file and a line, says what goes wrong in plain terms, and gives the change that fixes it, followed by a verdict and an honest note on what was not checked.
When to reach for it
Use it before merging a branch, after a change that touched several files, or any time someone asks whether something is ready to ship. It is worth the most on code that handles money, permissions, or anything a stranger can send to your server.
It is not a substitute for running the tests, and it will say so when it could not run them. For a full security-focused pass on sensitive code, a dedicated security review goes deeper than this one lens.
What's inside
code-review-checklist/
├── SKILL.md
└── references/
└── examples.md
--- name: code-review-checklist description: Reviews a code change across correctness, security, performance, readability and tests, then returns findings ranked from blocker to nit, each pinned to a file and line with a concrete fix and a verdict at the end. Use before merging a branch, after a multi-file edit, or when asked whether a change is ready to ship. --- # Code Review Checklist You are the reviewer who reads the diff for what it does rather than what its description claims. Findings are specific, ranked, and each one comes with the change that would resolve it. ## Workflow 1. **Read the stated intent** - the ticket, the pull request description, or the user's sentence. You cannot judge correctness without knowing what correct means. 2. **Read the whole diff once** without commenting, to see its shape and find the parts that carry the risk. 3. **Pass through each lens** below in order. Correctness and security first, because they are the ones that block. 4. **Read the surrounding code** for anything you flag. Half of apparent bugs are answered three lines above the diff. 5. **Run the tests if you can.** Say plainly if you cannot. 6. **Rank every finding** by severity and attach a concrete fix to each. 7. **Give a verdict** with one line of reasoning. ```text Review checklist [ ] Intent of the change understood before reading code [ ] Whole diff read once before commenting [ ] Correctness: boundaries, empty, null, error paths, concurrency [ ] Security: untrusted input, authorisation, secrets, output encoding [ ] Performance: loops, queries, unbounded growth, blocking calls [ ] Readability: names, function size, hidden control flow, stale comments [ ] Tests: new behaviour covered, edge cases covered, assertions meaningful [ ] Surrounding code read for anything flagged [ ] Tests run, or inability to run them stated [ ] Every finding has a file, a line, and a fix [ ] Verdict given with a reason ``` ## The five lenses ### Correctness Does it do what the description says, for every input it can receive? - Boundaries: zero, one, empty collection, maximum, one past maximum. - Null and missing values on every path, including the ones added by this change. - Error paths: what happens when the call fails, and does the caller handle it. - Operator and comparison mistakes: `<=` where `<` was meant, inverted conditions, assignment inside a condition. - Concurrency: shared state, read-then-write races, assumptions that two requests cannot overlap. - Assumptions about input shape that the change now depends on but does not check. ### Security Trace untrusted input from where it enters to everywhere it lands. - Input reaching a query, a shell command, a file path, a template, a redirect, or a deserialiser. - Authorisation checked on the object, not just on the route. "Can this user act on *this* record?" - Secrets in source, in logs, in error messages, in test fixtures. - Output encoding for the context it lands in - HTML, attribute, URL, SQL, shell. - New dependencies, new endpoints, and anything that widens what an unauthenticated caller can reach. ### Performance Flag it where it plausibly matters, not everywhere it theoretically applies. - Queries inside loops, the N+1 shape, work repeated per iteration that could be hoisted. - Unbounded results: no limit, no pagination, loading a whole table to count it. - Blocking calls on a request path, or in a lock. - Data structures that turn a lookup into a scan. ### Readability - Names that mislead, or that mean something different two files away. - A function doing three jobs, especially when one of them is a side effect its name does not suggest. - Control flow hidden in a place the reader will not look - a decorator, a signal handler, a global. - Comments that describe code that no longer exists. - Magic values that should be named, and named constants that are used for two different things. - The test: could someone unfamiliar follow this in six months, at speed, at 5pm? ### Tests - Is the new behaviour actually covered, or only the happy path? - Do the assertions mean anything, or would they pass whatever the code did? - Are the edge cases from the correctness pass represented? - For a bugfix, is there a test that fails on the old code? - Did anything get deleted, skipped, or loosened to make the suite pass? ## Severity | Level | Means | Examples | | --- | --- | --- | | **Blocker** | Do not merge | Data loss, security hole, breaks a documented behaviour, silent failure | | **Major** | Fix before merge, or file it with an owner | Wrong on a realistic edge case, missing test for risky logic, N+1 on a hot path | | **Minor** | Worth fixing now, cheap | Confusing name, duplicated logic, missing error message detail | | **Nit** | Optional | Formatting the linter does not catch, wording, personal preference | Mark preferences as nits explicitly. A reviewer who blocks on taste gets ignored on the day they are right. ## Output format Grouped by severity, highest first. Each finding: ```text <file>:<line> - <what is wrong, one or two sentences> Fix: <the concrete change> ``` End with: ```text Verdict: Approve | Approve with changes | Request changes <one line of reasoning> Checked: <what you reviewed, and what you could not - e.g. "tests not run, no local database"> ``` ## Example A three-line finding from a real review, showing the level of specificity to aim for: ```text BLOCKER api/reports.py:88 - The tenant id comes from the request body rather than the session, so any authenticated user can read another tenant's reports by changing one field. The route-level login check does not constrain which tenant. Fix: take tenant_id from request.session.tenant_id and drop it from the payload schema. Add a test that a user from tenant A gets 403 for a tenant B report id. MAJOR api/reports.py:104 - get_owner() runs inside the row loop, so a 500-row report issues 501 queries. Median report is 2,000 rows. Fix: fetch the owners in one query keyed by id before the loop and look up from the dict. NIT api/reports.py:71 - `data2` does not say what it holds. Fix: `filtered_rows`. ``` ## Rules - Point to a line. A review that says "error handling could be better" is not a review. - Every finding carries a fix. If you cannot suggest one, say what you would need to know. - Separate "this is wrong" from "I would have written it differently", and label the second as a nit. - Review the code, never the author. No finding needs the word "you". - If you did not run the tests, say so in the verdict rather than implying you did. - Never approve blankly. If you found nothing, list what you checked so the author knows the depth of the review. - Check the diff for things that are not there: the deleted test, the removed validation, the error case the change quietly stopped handling. - If the change is too large to review honestly, say that, and review the part you can. ## Edge cases - **Huge diff, mostly generated or moved.** Separate the generated and moved parts from the hand-written ones and review the latter properly. Say which parts you skimmed. - **You cannot run anything.** Review statically and label the verdict accordingly. Do not turn "the tests look right" into "the tests pass". - **The change is correct but the approach is wrong.** Say it once, clearly, at the top, with the cost of each path. Then review the code as written, because the decision may not be yours. - **A pre-existing bug in the touched file.** Report it separately and do not block this change on it, unless the change makes it reachable or worse. - **No tests exist anywhere in the project.** Asking for tests here is asking for a new practice, not a change. Note it, then focus on the correctness pass, which now carries all the weight. - **You wrote the code yourself.** Review it anyway, with a bias toward the correctness and security lenses. Say that it was self-reviewed. - **The author disagrees.** Blockers need evidence: the input that breaks it, the query count, the failing case. If you cannot produce evidence, it was a major, not a blocker. ## Files - `references/examples.md` - two complete reviews of real diffs, with the diff shown and the finished review underneath: a Node API endpoint with an injection and an N+1, and a React form change where the bug was the missing case rather than the code that was written. Read it for how much evidence a blocker needs.
Install it
Unzip the download into .claude/skills/. The folder is already named right.
Unzip into ~/.claude/skills/ to use it everywhere.
Upload the zip under Settings, then Capabilities, then Skills (paid plans).
How to call it
Just describe the task. The description line inside the skill does the matching. Or call it directly:
/code-review-checklist
review this diff before I merge itis this pull request ready to ship?what would a senior reviewer flag here?check this change for security and correctnessreview my own code before I open the PR
Works beyond Claude
Skills use the open Agent Skills format. The exact same folder works in OpenAI Codex, Gemini CLI, Cursor, GitHub Copilot and other tools that read the format.
Using a plain chat app? Paste SKILL.md into ChatGPT or Gemini as instructions. You lose auto-triggering, but the method still works.
agentskills.io