{
  "slug": "code-review",
  "url": "https://checklists.org/c/code-review",
  "markdown": "https://checklists.org/c/code-review.md",
  "title": "Reviewing a Pull Request",
  "description": "What a rigorous reviewer checks in a pull request, from first read to approval.",
  "category": "software",
  "tags": [
    "software"
  ],
  "version": "1.0",
  "updated": "2026-07-07",
  "sources": [
    {
      "name": "Google Engineering Practices — Code Review Guide",
      "url": "https://google.github.io/eng-practices/review/reviewer/"
    },
    {
      "name": "SmartBear — Best Practices for Peer Code Review",
      "url": "https://smartbear.com/learn/code-review/best-practices-for-peer-code-review/"
    }
  ],
  "intro": "For reviewing someone else's pull request on a shared codebase. The goal is not a perfect PR — it's a PR that makes the codebase better than it was, reviewed fast enough that the author is still thinking about it.",
  "itemCount": 23,
  "essentialCount": 6,
  "sections": [
    {
      "title": "Before you start",
      "items": [
        {
          "id": "hpm18b",
          "text": "Start the review within one business day",
          "note": "Review latency, not review depth, is what makes teams route around the process. A stalled review costs more than most bugs."
        },
        {
          "id": "xztvfh",
          "text": "Read the PR description and linked issue before any code",
          "note": "You are judging the diff against its intent. If you can't tell what the change is for, ask for a description — that's the whole review."
        },
        {
          "id": "1kd03uy",
          "text": "Ask for a split if the diff is over ~400 lines",
          "note": "Defect detection falls off sharply past 400 lines per review session. Two small PRs get two real reviews; one big PR gets a skim."
        },
        {
          "id": "exz8qh",
          "text": "Check out the branch and run it",
          "note": "Screenshots show the happy path the author chose. Clicking around yourself finds the state they didn't.",
          "when": "user-facing-change"
        }
      ]
    },
    {
      "title": "Correctness",
      "items": [
        {
          "id": "19zo59i",
          "text": "Trace the main path through the diff line by line",
          "note": "Read the code, don't pattern-match it. Most approved-then-reverted PRs were skimmed, not read.",
          "essential": true
        },
        {
          "id": "1c0s5xc",
          "text": "Hunt the edge cases: empty, null, zero, negative, huge, duplicate, concurrent",
          "note": "Ask of every input: what's the worst value this could hold?",
          "essential": true
        },
        {
          "id": "1qe7oj3",
          "text": "Follow every error path to its end",
          "note": "What happens when the network call fails, the file is missing, the parse throws? Swallowed exceptions are bugs with a delay timer."
        },
        {
          "id": "1tpec9i",
          "text": "Verify the tests would fail if the change were wrong",
          "note": "For a bug fix, the new test must fail without the fix. A test that can't fail is worse than no test — it's false confidence."
        },
        {
          "id": "1o833dn",
          "text": "Check that tests cover the new behavior, not just the new lines",
          "note": "Coverage tools count lines executed, not outcomes asserted."
        },
        {
          "id": "161xn9a",
          "text": "Check that input crossing a trust boundary is validated",
          "note": "Anything from a user, another service, or a file: injection, path traversal, unbounded size, unexpected type.",
          "essential": true
        },
        {
          "id": "1b3w89c",
          "text": "Check authorization, not just authentication",
          "note": "Verifying *who* the user is but not *whether they may touch this resource* is the most common access-control bug in web code.",
          "essential": true
        },
        {
          "id": "ykod8q",
          "text": "Scan the diff for secrets and credentials",
          "note": "Keys, tokens, passwords, internal URLs. Once merged, they're in history forever and must be rotated, not just deleted.",
          "essential": true
        }
      ]
    },
    {
      "title": "Design",
      "items": [
        {
          "id": "1wrf3cw",
          "text": "Flag anything unrelated to the PR's stated purpose",
          "note": "Drive-by refactors hide bugs in the noise and complicate reverts. Ask for them as a separate PR — most authors happily agree."
        },
        {
          "id": "190memr",
          "text": "Check that names say what things actually are",
          "note": "A function named `getUser` that also creates one is a future bug. Names are the API most readers will ever see."
        },
        {
          "id": "e50q5n",
          "text": "Question every new dependency",
          "note": "Each one is a maintenance commitment and supply-chain surface. Could thirty lines of code do the same job?"
        },
        {
          "id": "1l9rfss",
          "text": "Verify the change follows the codebase's existing patterns",
          "note": "A second way to do the same thing costs every future reader a decision. If the new pattern is better, that's a proposal, not a PR."
        },
        {
          "id": "uz2oz",
          "text": "Verify API and schema changes are backwards compatible",
          "note": "Old clients keep working; removals get a deprecation cycle. Renaming a JSON field is a breaking change no compiler will catch.",
          "when": "public-api",
          "essential": true
        },
        {
          "id": "yc8gui",
          "text": "Check the change can be debugged in production",
          "note": "Enough logging and metrics that whoever is on call at 3 a.m. can tell what it's doing without adding print statements first."
        }
      ]
    },
    {
      "title": "Before you approve",
      "items": [
        {
          "id": "1skt1w0",
          "text": "Reread your comments and label the non-blocking ones",
          "note": "Prefix preferences with \"nit:\" so the author knows what actually gates approval. Unlabeled comments all read as blocking."
        },
        {
          "id": "n5cmco",
          "text": "Confirm blocking comments are resolved, not just replied to",
          "note": "\"Will fix in a follow-up\" is a resolution only if the follow-up ticket exists."
        },
        {
          "id": "ym59xu",
          "text": "Check the PR title and commit message describe the change",
          "note": "They become the changelog and the answer `git blame` gives in three years. \"Fix bug\" answers nothing."
        },
        {
          "id": "15u9eaj",
          "text": "Check docs and changelog are updated",
          "when": "user-facing-change"
        },
        {
          "id": "1wl4si8",
          "text": "Approve only if the change improves overall code health",
          "note": "Google's standard: not perfect, better. Don't hold approval hostage to your personal style — and don't approve code you wouldn't be willing to maintain."
        }
      ]
    }
  ]
}