Effective Code Review Practices
Code review is where your team's quality bar actually lives—not in the README, not in the architecture doc nobody updates. I have been on teams where review meant "LGTM in thirty seconds" and teams where every PR accumulated forty nit comments. Both fail. The first ships bugs; the second ships nothing. Effective review is structured: know what to look for, know what to automate, and know when to approve with follow-up tickets.
Automate the easy ninety percent
If a human is commenting on missing semicolons, trailing whitespace, or import order, you are wasting expensive attention. CI should enforce:
- Formatter (Prettier, ktlint, rustfmt)
- Linter with error-level rules only in CI
- Type checker and unit tests
- Security scanners (Dependabot, CodeQL)
Reviewers should assume green CI means mechanical quality passed. Their job is everything CI cannot judge: business logic, API design, failure modes, and whether the change fits the system.
A tiered comment system
Ambiguous review comments cause the most friction. I use explicit prefixes:
| Prefix | Meaning | Blocks merge? |
|---|---|---|
blocking: |
Bug, security issue, or wrong approach | Yes |
question: |
Need clarification before judging | Yes, until answered |
suggestion: |
Better way, author decides | No |
nit: |
Style preference, non-standard | No |
Authors filter noise instantly. Reviewers feel permission to leave nits without guilt because they labeled them as non-blocking.
What to actually read in a PR
Read the description first. A good PR description states problem, approach, and test plan. If it does not, send it back before reading code—fixing understanding is cheaper than fixing code built on wrong assumptions.
Scan the diff top-down for structure. New files, deleted code, config changes, migrations—these deserve more attention than a renamed variable in a loop.
Trace the happy path, then break it. What happens on null input? Network timeout? Concurrent access? Permission denied?
Check observability. Are errors logged with context? Are metrics or traces added for new user-facing flows?
Verify tests prove behavior, not coverage. A test named testUserService that mocks everything and asserts true is worse than no test—it gives false confidence.
Review scope by change type
Not every PR needs the same depth:
- Hotfix on production: correctness and rollback plan only; full style review can wait
- Public API change: backward compatibility, documentation, consumer impact
- Database migration: reversibility, lock behavior, backfill strategy
- Dependency bump: changelog, breaking changes, license
Publish a lightweight decision tree in your team docs so reviewers do not apply migration-level scrutiny to a copy change.
SLAs and rotation
Review latency is a team metric, not an individual virtue problem. When PRs sit for two days, authors start new work, context rots, and merges become painful rebases.
Practices that work:
- Daily review sheriff with Slack reminder
- PR size limits (see separate article)—large PRs get slow reviews
- "Review pair" on complex changes: two reviewers, one domain expert
- Block merge if no review within SLA unless emergency bypass documented
Author responsibilities
Review quality depends on author prep. Before requesting review:
- Self-review the diff line by line
- Split unrelated changes into separate PRs
- Write a test plan a reviewer can execute in five minutes
- Highlight risky areas in the description: "Pay attention to the retry logic in
PaymentClient"
Authors who dump 800-line diffs with "PTAL" get the review they deserve.
Handling disagreement
When reviewer and author disagree on approach, escalate to a five-minute sync or a third engineer—not an async comment thread spanning three days. Document the decision in the PR so the next person understands why.
For recurring disagreements (tabs vs spaces level but architectural), capture the decision in ADRs or team conventions so the same debate does not replay monthly.
Measuring review health
Track median time-to-first-review and median time-to-merge. Drop in quality often correlates with rising merge times before it shows up in incident count.
Optional: lightweight PR retros on incidents—"would review have caught this?" without blame. Missing checks become checklist items, not postmortem shame.
Reviewer checklist
- Does PR description explain why, not just what?
- Are edge cases tested?
- Security: auth, input validation, secrets
- Observability: logs, metrics for new paths
- Rollback plan for risky changes
Review response SLA: 4 hours for small PRs, same-day for large. Block merges on style nitpicks — use linter instead.
Common production mistakes
Teams get effective practices wrong in predictable ways:
- Skipping failure-mode rehearsal — run a game day or fault injection exercise before peak traffic, not after the first outage.
- Missing correlation context — every error path should carry request, trace, or tenant identifiers so incidents are debuggable.
- Optimizing for demo, not steady state — load tests, cache warm-up, and cold-start paths matter more than local dev latency.
- Undocumented trade-offs — if you chose speed over strict correctness (or vice versa), write that down for the next engineer.
Production implementations of effective practices fail when staging mirrors production topology poorly, rollback is untested, and on-call runbooks describe the happy path only.
Debugging and triage workflow
When effective practices misbehaves in production, work top-down instead of guessing:
- Confirm scope — one tenant, region, or deployment stage? Narrow blast radius before deep diving.
- Check recent changes — deploys, flag flips, config pushes, and schema migrations in the last 24 hours.
- Compare golden signals — latency, error rate, saturation, and traffic for the affected surface vs. baseline.
- Reproduce minimally — smallest input or scenario that triggers the failure; capture traces/logs with correlation IDs.
- Fix forward or rollback — if rollback is faster than root-cause during incident, rollback first, postmortem second.
- Add a guard — alert, integration test, or circuit breaker so the same class of failure is caught earlier next time.
Document the timeline during triage. Future you (and on-call) will need timestamps, not just conclusions.
Resources
- Google Engineering Practices — How to do a code review
- Conventional Comments specification
- GitHub pull request template examples
- Smart Bear — Best Practices for Code Review
Frequently asked questions
What should code reviewers focus on first?
Start with correctness and intent: does the change solve the stated problem, handle edge cases, and match the design discussed in the ticket? Then check security, data integrity, and error paths. Style and naming come last—automate those with linters and formatters so human review time goes to logic that tools cannot catch.
How fast should code reviews turnaround?
Target first response within four business hours for normal PRs, same day for urgent fixes. Teams that miss this SLA accumulate WIP and context-switch cost for authors. Rotate a 'review sheriff' daily if review load is uneven—one person responsible for clearing the queue, not doing all reviews solo.
How do you give review comments that do not demoralize?
Separate blocking issues from suggestions using explicit labels: 'nit:', 'question:', 'blocking:'. Ask questions instead of issuing commands when intent is unclear. Praise good patterns when you see them—reviews that are only criticism train authors to dread opening PRs.
Hiring a senior Android / Flutter engineer?
I architect and ship production mobile software — Kotlin, Jetpack Compose, Flutter — for robotics, EV infrastructure, fintech, and real-time systems. Open to remote roles in Europe and the US.
Get in touch →