Code Review Checklist

Per-PR review workflow a reviewer runs before approving a pull request. Covers context gathering, style, tests, design, security, and merge sign-off — with branches for oversized PRs, database migrations, and dependency changes.

6 sections 23 steps Collects data
1

PR Intake and Context

  1. Read the PR description and linked ticket
    • Open the linked Jira/Linear ticket and confirm the PR scope matches what was agreed during refinement. PRs that drift from the ticket — added refactors, opportunistic cleanups, scope creep — are the most common source of review friction. Flag scope mismatches in a top-level review comment before going line-by-line.

  2. Check out the branch locally
    • Don't review only in the GitHub/GitLab diff view for non-trivial PRs. Pull the branch, run it, exercise the changed code path. The diff hides cross-file context; running it locally surfaces issues like broken local dev setup, missing env vars, or unrunnable tests.

  3. Confirm CI is green on the latest commit
    • Don't review a red build. If a check is failing, ask the author to fix or explicitly flag it as a known flake before continuing. "Just rerun it" becomes a habit that masks real regressions.

  4. Categorize the PR size
    • Review quality drops sharply on PRs over ~400 lines of net diff. Categorize before you start reading; if it's oversized, request a split rather than skim-approving with "LGTM".

    Collects list
  5. Ask the author to split the PR
    • Suggest a split along natural seams: refactor before behavior change, infra/config before app code, schema migration before consuming code. Pause review until the split lands; reviewing a 1,500-line PR reliably means rubber-stamping it.

2

Code Style and Readability

  1. Run the linter and formatter on the diff
    • If your toolchain has ESLint, Prettier, RuboCop, gofmt, ruff, or equivalent — these should already run in CI. Spot-check that the configured rules actually ran on the changed files (no skipped files via .eslintignore or //eslint-disable). Style nits are for the linter, not the reviewer.

  2. Review naming and dead code
    • Flag ambiguous variable names (data, tmp, handler2), unused imports, commented-out code blocks, and stray console.log / puts / print debug statements. These are cheap to fix now and expensive to clean up later.

  3. Confirm complex logic has inline comments
    • Comments should explain why, not what — the code shows the what. If a regex, recursive call, or non-obvious algorithm has no comment, ask for one. Bonus: a TODO/FIXME with a ticket link is acceptable; a TODO with no owner is not.

3

Tests and Coverage

  1. Verify unit tests cover the new logic
    • For each new branch in the code, look for a corresponding test case. Coverage percentage alone is misleading — a 90% line-coverage PR can still skip the actual conditional logic. Read the test names and assertions, not just the coverage delta.

  2. Check integration coverage for API changes
    • If the PR changes an HTTP endpoint, gRPC method, or message contract, an integration test should exercise the request/response shape end-to-end. Unit tests with mocked clients miss serialization bugs and middleware ordering issues.

  3. Review edge cases and error paths
    • Common gaps: empty input, null/None, max-length input, unicode/emoji, concurrent calls, network timeout, partial failure, retry behavior. Ask: what happens if the downstream call returns 500? Is the error logged with enough context for an on-call to triage at 3am?

4

Design and Architecture

  1. Assess fit with the service architecture
    • Does this change belong in this service? PRs that introduce cross-service coupling (a billing concept leaking into the auth service, a UI module reaching into a backend repo) are architecture smells. If unsure, loop in the service owner via CODEOWNERS or a tech lead.

  2. Check for duplicated functionality
    • Grep the codebase for the new utility's name, signature, or core logic before approving. Reinvented date parsers, retry helpers, and HTTP client wrappers proliferate when reviewers don't ask. If a similar helper exists, push back unless the author has a specific reason not to use it.

  3. Evaluate scalability and SOLID adherence
    • Watch for N+1 queries on list endpoints, unbounded loops over user input, in-memory aggregations that will OOM at 10x scale, and god-classes that violate single responsibility. "It works for our current data size" is not a passing answer.

  4. Flag whether the PR includes a database migration
    • Look for files under db/migrate, migrations/, Alembic, Flyway, golang-migrate, or schema.rb/schema.prisma changes. A schema change deserves its own review pass — these are the highest-blast-radius changes in the diff.

    Collects list
  5. Review migration for locks and reversibility
    • On Postgres, ADD COLUMN with a non-null default rewrites the table under exclusive lock — fine on 10K rows, catastrophic on 50M. Require CREATE INDEX CONCURRENTLY, batched backfills, and a documented rollback. Confirm the migration is reversible or that an explicit forward-only decision is captured in the PR description.

5

Security and Dependencies

  1. Scan the diff for hardcoded secrets
    • API keys, database URLs, JWT secrets, AWS access keys, internal hostnames in test fixtures. GitHub secret scanning + gitleaks/trufflehog in pre-commit catch most cases, but a human pass on the diff is the last line of defense. If a secret lands on main, rotation alone doesn't fix it — the value lives in git history forever without filter-repo or BFG.

  2. Verify authz on new endpoints
    • Every new endpoint needs an explicit authorization check, not just authentication. Confirm the user can only access resources they own (IDOR protection), and that input is validated before hitting the database (SQL injection, command injection, OWASP Top 10). "The frontend prevents this" is not an authorization model.

  3. Flag newly added third-party dependencies
    • Check package.json / Gemfile / go.mod / pyproject.toml diff for new entries. Each new dep adds maintenance surface, license obligations, and supply-chain risk. Worth scrutiny even if it's a one-line install.

    Collects list
  4. Check new dependencies for CVEs and license fit
    • Run Snyk, npm audit, bundler-audit, or your SCA tool against the new dep tree. Verify the license is compatible with the product (no AGPL surprises in a closed-source SaaS, no SSPL gotchas). A package with 200 weekly downloads, no recent commits, and a single maintainer is a supply-chain liability — push back.

6

Approval and Merge

  1. Record the final review decision
    • Use the platform's review action — Approve, Request changes, or Comment. Don't approve with unresolved blocking comments; either resolve them or use "Request changes" so the PR can't be merged. Reviewer notes capture context (what you focused on, what you skipped) for the audit trail and for the next reviewer if approval is delegated.

    Collects list Collects paragraph
  2. Verify the merge strategy matches repo policy
    • Confirm squash vs. rebase vs. merge-commit matches the repo's branch protection rules. Confirm CODEOWNERS approvals are satisfied. If the commit message will become the squash message, make sure it's not "fix typo (#1234)" — clean it up before merge so the main-branch history stays readable.

  3. File follow-up tickets for deferred feedback
    • When the decision is "Request changes", capture the blocking items as PR comments and any non-blocking items as tickets in Jira/Linear linked from the PR. Avoid the pattern where deferred feedback lives only in PR threads — those become unsearchable once the PR is closed.

Use this template

Copy it to your account, customize the steps, and run it with your team in minutes.


Sections 6
Steps 23
Category Software Development
Price Free to start
Need a different process

Browse hundreds of free templates across every team and industry.

Back to template library

Run Code Review Checklist with your team

Customize the steps, assign roles, set a schedule, and keep a complete record for every run.