Code Review Is Not a Gate — It's a Teaching Moment
The Code Review That Broke a Team
A senior engineer at a client's company left 47 comments on a junior developer's pull request. Comments like "This isn't how we do things here," "Wrong pattern," and "Did you even read the style guide?" The junior developer spent three days reworking the PR, submitted again, got 23 more comments. By the fourth round, they'd been working on the same feature for two weeks. They quit three months later.
The senior engineer thought they were maintaining quality. They were actually destroying their team.
What's Wrong With Most Code Reviews
The Gatekeeping Model (common, broken):
Developer submits PR → Senior reviews → Rejects with comments
→ Developer fixes → Senior reviews again → More comments
→ Developer fixes → Finally approved (3-5 days later)
Result: Slow delivery, frustrated developers, knowledge hoarding
The Teaching Model (effective):
Developer submits PR → Reviewer provides context and alternatives
→ Quick conversation (sync if needed) → Collaborative resolution
→ Approved with shared understanding (hours, not days)
Result: Fast delivery, growing developers, knowledge sharing
The difference isn't lower standards. It's the approach. Gatekeeping reviews ask "Is this code perfect?" Teaching reviews ask "Does this developer understand why we'd do it differently?"
The Three Types of Review Comments
Every code review comment falls into one of three categories. Label them explicitly:
## Comment Categories:
🔴 **Blocker**: Must fix before merge. Security issues, bugs, data loss risks.
"This SQL query is vulnerable to injection — must use parameterized queries."
🟡 **Suggestion**: Would improve the code but not blocking.
"Consider extracting this into a utility function — we have similar logic in utils/format.ts"
🟢 **Nit**: Style preference, minor improvement. Not blocking.
"nit: Could rename this variable to be more descriptive. Not blocking."When a reviewer doesn't categorize their comments, every comment feels like a blocker. Developers waste time fixing nits when they should be addressing real issues. Explicit labeling eliminates this ambiguity.
The "Why" Rule
Every non-trivial review comment should explain why, not just what:
❌ Bad: "Don't use any here."
✅ Good: "Using `any` here bypasses TypeScript's type checking, which means
bugs in this function won't be caught at compile time. Since this handles
payment data, we want the compiler to verify the shape. Consider using the
PaymentIntent interface from @/types/payments."
❌ Bad: "This should be a separate function."
✅ Good: "This block handles two responsibilities — validation and transformation.
Separating them makes each easier to test independently. We follow the single
responsibility pattern in this codebase — see utils/order.ts for an example."The "why" turns a correction into a lesson. After seeing the same "why" twice, most developers internalize the principle and stop making the same mistake. Without the "why," they memorize the rule without understanding it.
The Review Speed Problem
DORA research shows that code review speed is one of the strongest predictors of team performance. Long review times kill velocity:
Review response time benchmarks:
< 2 hours: Excellent — team is highly collaborative
2-4 hours: Good — normal for focused work cycles
4-24 hours: Concerning — reviews are being deprioritized
> 24 hours: Broken — reviews are a bottleneck
Impact of slow reviews:
Context switching: Developer moves to another task, then has to context-switch
back when review comments arrive (+30 min per switch)
Merge conflicts: Longer a PR is open, more likely it conflicts with other work
Morale: Waiting for review feels like waiting for permission
WIP limits: Developers start multiple PRs, increasing cognitive load
How to Speed Up Reviews Without Cutting Corners
1. Automate the Mechanical Stuff
Humans shouldn't review things machines can check:
# .github/workflows/automated-checks.yml
# These run BEFORE human review — reviewer assumes they passed
checks:
- lint (ESLint, Prettier)
- type-check (TypeScript strict)
- tests (unit + integration)
- security (Snyk, CodeQL)
- bundle-size (check for regressions)
- coverage (minimum threshold)If automated checks pass, the human reviewer focuses only on logic, architecture, and knowledge sharing. This cuts review time by 40-60%.
2. Small PRs, Always
PR size and review effectiveness:
< 200 lines: Reviewers find 80% of issues
200-400 lines: Reviewers find 60% of issues
400-800 lines: Reviewers find 40% of issues
> 800 lines: Reviewers skim and approve (finding < 20% of issues)
Rule: If a PR is over 400 lines, it should be split.
Exception: Generated code, migrations, large refactors (tag these clearly)
The goal isn't small PRs for their own sake — it's effective reviews. A 100-line PR gets a thorough review in 15 minutes. An 800-line PR gets a cursory scan in 45 minutes. The small PR actually gets better review quality in less time.
3. The Review Rotation
Don't let the same senior engineer review everything. Rotate reviewers:
// Review assignment strategy
const reviewAssignment = {
// Round-robin within the team
primary: getNextReviewer(team, { exclude: author }),
// For junior developers: pair with a mentor reviewer
mentor: author.level === "junior"
? getMentorReviewer(author)
: null,
// For critical paths: require domain expert
domainExpert: touchesCriticalPath(pr)
? getDomainExpert(pr.filesChanged)
: null,
};Rotation distributes knowledge, prevents bottlenecks, and gives junior developers exposure to different coding styles and perspectives.
The Review Checklist That Actually Works
Instead of reviewing everything at once, use a structured pass:
Pass 1 — Intent (2 minutes):
□ Read the PR description — do I understand what this does and why?
□ Does the approach make sense at a high level?
Pass 2 — Correctness (10 minutes):
□ Are there logic errors or edge cases?
□ Are error cases handled?
□ Do the tests cover the important paths?
Pass 3 — Design (5 minutes):
□ Is this in the right place in the codebase?
□ Does it follow existing patterns?
□ Will this be easy to change later?
Pass 4 — Teaching (3 minutes):
□ Is there something the author could learn from this review?
□ Are there codebase conventions they might not know about?
□ Can I point them to a good example of the pattern we want?
Total time: 20 minutes for a well-scoped PR. That's fast enough to review 3-4 PRs per day without it consuming your schedule.
Building a Review Culture
The best code review cultures share these traits:
- Everyone reviews — Seniors review juniors, and juniors review seniors. Fresh eyes catch things experience misses.
- Reviews are thanked — "Great catch" and "I learned something from this review" should be common phrases.
- Disagreements are conversations — If a review comment leads to back-and-forth, hop on a 5-minute call instead of writing essays in GitHub comments.
- Speed is valued — First review within 2 hours is a team norm, not a stretch goal.
Code review is the single best mechanism for growing engineers, sharing knowledge, and maintaining quality — but only if you treat it as teaching, not gatekeeping. Fix your review culture, and everything downstream improves: velocity, quality, retention, and morale.