A bad code review feels busy. Lots of comments, lots of opinions, lots of tiny style debates. Then the bug still ships.
That is the problem with most review checklists. They ask reviewers to “check everything,” which really means everyone checks whatever annoys them first.
A useful checklist does something narrower. It helps a tired developer find the risky parts before the pull request becomes part of production.
Start with the shape of the change
Before reading line by line, ask what kind of change this is.
Is it a bug fix? A new feature? A refactor? A dependency bump? A database change?
That sounds obvious, but it changes the review. A bug fix needs proof that the bug cannot come back. A refactor needs proof that behavior stayed the same. A dependency bump needs a quick check for breaking changes and security notes.
If the pull request description does not explain that, ask for it before reviewing the code.
Check the risky path first
Read the code that can hurt users first:
- authentication and permissions
- payment, billing, and account logic
- data writes and migrations
- background jobs that retry
- edge cases around empty, missing, or duplicated input
Small teams do not have enough review time to treat every line equally. Spend the first ten minutes where a mistake would be expensive.
This is also why “looks good” can be dangerous. The risky part often looks boring.
Ask what changed for the user
Good reviews connect code to behavior.
If a button changed, what happens when the request fails? If a validation rule changed, what message does the user see? If a cache key changed, who gets stale data?
You are not reviewing a diff for its own sake. You are reviewing the user-facing effect hiding behind that diff.
This habit catches more bugs than naming debates ever will.
Look for missing tests, not test volume
A pull request with 20 tests can still miss the one case that matters.
Check whether the test covers the new risk. For a bug fix, there should be a test that fails before the fix. For a permissions change, there should be a denied case, not only a happy path.
If the code is hard to test, that is feedback too. It may mean the change is doing too much in one place.
Related reading: if the team still argues about test strategy, the TypeScript testing guide on this site is a better companion than a giant review checklist.
Separate blockers from preferences
Every review comment should quietly answer one question: does this need to block the merge?
Use simple labels in the comment itself:
| Label | Meaning |
|---|---|
blocker | This can break behavior, security, data, or deploys. |
question | The reviewer needs context before deciding. |
nit | Optional. The author can ignore it. |
This keeps reviews from turning into personality tests.
And it gives authors a way to move quickly without pretending every comment has the same weight.
Check readability after correctness
Readable code matters, but timing matters too.
Once the risky behavior is covered, scan for names, structure, and surprise. A function that says createInvoice should not also send email, update user limits, and schedule a retry job.
If the naming is the main problem, be specific. “Rename this” is lazy. “isReady sounds like UI state, but this checks payment eligibility” is useful.
The goal is not pretty code. The goal is code the next person can change without guessing.
Keep the review small enough to finish
A 900-line pull request rarely gets a real review. People skim, comment on formatting, and hope CI catches the rest.
For small teams, review size is part of quality control. If the change is too large, ask the author to split it by behavior, not by file type.
One pull request for schema. One for the service logic. One for the UI. Boring, but reviewable.
The checklist I would actually use
Before approving, answer these:
- Do I understand what behavior changed?
- Did I check the riskiest path first?
- Is there a test for the new risk?
- Could this leak data, charge money wrong, or block a user?
- Will the next developer understand the names and boundaries?
- Are my blocking comments really blockers?
If the answer is mostly yes, approve it. Perfect code is not the job.
Shipping safer code is.


