Code reviews are one of the most effective practices for maintaining code quality, sharing knowledge across a team, and catching bugs before they reach production. But a bad review process can slow teams down and create friction. Here is how to do code reviews well, from both sides.

What to Look For

When reviewing someone's code, focus on these areas in order of importance:

1. Correctness

Does the code actually do what it is supposed to do? This is the most critical check. Read the PR description, understand the requirements, and verify the implementation matches. Look for edge cases the author may have missed: null values, empty arrays, concurrent access, error handling paths.

2. Design and Architecture

Does the change fit well into the existing codebase? Is the abstraction level right? Is the code in the right place? These are harder questions that require understanding the broader system. A function might be correct but architecturally wrong if it duplicates logic that exists elsewhere or introduces a circular dependency.

3. Readability

Will the next developer (or you in six months) understand this code? Good naming, clear control flow, and appropriate comments matter more than clever one-liners. If you have to re-read a block three times to understand it, it probably needs to be simplified.

4. Tests

Does the change include tests? Do the tests cover the important cases? Are they testing behavior (what the code does) rather than implementation (how it does it)? Tests that break every time you refactor are worse than no tests because they create resistance to improvement.

5. Style and Formatting

This should be the least of your concerns during review because it should be automated. Use Prettier, ESLint, gofmt, or whatever formatter fits your language. Never spend review time on indentation, bracket placement, or naming conventions that could be enforced by a linter.

Giving Good Feedback

The way you phrase feedback matters as much as the content. Some guidelines:

  • Ask questions instead of making demands. "What happens if this list is empty?" is better than "You forgot to handle the empty case." The question approach assumes the author may have a reason you are not seeing.
  • Explain why, not just what. "Consider extracting this into a separate function because it is also needed in the billing module" is better than "Extract this into a function."
  • Distinguish between blockers and suggestions. Prefix optional feedback with "nit:" or "suggestion:" so the author knows what must change before merging and what is just a thought.
  • Acknowledge good work. If you see something well-done, say so. A quick "nice approach here" costs nothing and builds a positive review culture.

Receiving Feedback

Being on the receiving end of code review is a skill too:

  • Do not take it personally. The reviewer is critiquing the code, not you. Even if the feedback feels blunt, assume good intent.
  • Respond to every comment. Even if you just say "done" after making the change. Unresolved comments create ambiguity about whether the feedback was addressed.
  • Push back when appropriate. If you disagree with feedback, explain your reasoning. A good review is a conversation, not a one-way directive. But pick your battles - not every hill is worth defending.
  • Keep PRs small. If your PR is 2000 lines, reviewers will either give shallow feedback or take days to review it. Aim for PRs that can be reviewed in 15-30 minutes.

PR Size Matters

Research from Google's engineering practices shows that review quality drops significantly as PR size increases. The data is clear:

  • PRs under 200 lines get thorough, thoughtful reviews
  • PRs between 200-400 lines get adequate reviews
  • PRs over 400 lines often get rubber-stamped because the reviewer's attention is exhausted

If your feature requires more than 400 lines of changes, break it into a sequence of smaller PRs. This might feel slower, but each PR gets better feedback and merges faster, so the total time is often shorter.

Automate What You Can

Every minute a human spends checking something a machine could catch is wasted. Set up your CI pipeline to handle:

  • Code formatting (Prettier, gofmt, Black)
  • Linting (ESLint, golangci-lint, Pylint)
  • Type checking (TypeScript compiler, mypy)
  • Test execution
  • Code coverage thresholds

When these checks run automatically on every PR, reviewers can focus entirely on logic, design, and correctness instead of formatting and style.

Review Turnaround Time

Slow reviews block progress and frustrate authors. Aim to respond to review requests within a few hours, not days. This does not mean you need to drop everything immediately, but checking for pending reviews twice a day keeps the team moving.

If a review will take longer (large PR, complex domain), leave a comment saying when you will get to it. Silence is worse than "I will review this tomorrow morning."

Good code reviews make everyone on the team better. They are worth investing in.