Posted on 8 mins read

A lot of teams procrastinate code review, phone it in, or get stuck in endless back-and-forth on every PR. Let’s talk about why.

Myth #1

Myth: Code review is difficult because it’s boring and pull requests are too long.

Fact: Code review is difficult because it duplicates the cognitive work of programming without all the intermediate steps.

Engineers tend to dislike reviewing code. Instead of just accepting it as an unpleasant part of the job, we should ask why.

Let’s start with the most common misconception: it’s not about PR length. Some teams have discovered that establishing a norm of smaller PRs leads to faster review times and less mental fatigue, and I wholeheartedly support this, but it’s not the PR length alone that brings these benefits. It’s the fact that when engineers are encouraged to submit shorter PRs, they compensate by doing more work to organize and document each PR.

Suppose you need to make a code change that, in its ideal form, comes in around 1000 lines. If your team has established a norm of PRs being 200 SLOC or less, you’ll need at least five PRs, and if you want those PRs to make sense as individual units, you’ll have to do a lot more storytelling:

  • This PR updates the database with new columns; here’s what they’re for.
  • This PR updates DTO classes so the data in those columns can be sent over the wire.
  • This PR refactors a service to incorporate the new data into its calculations.
  • Etc.

Small PRs are easier to review because they make code authors explain themselves better—step by step, at a finer grain. Without that explanation, the code reviewer has to try to to reconstruct all the reasoning and context that went into the PR. This abstract guessing game can easily take more mental energy than it took to actually write the code. We think of code review as a quick checkbox task, but the way many teams approach it, it’s the most mentally taxing thing they do.

When you code, you spend most of your time researching. You clarify requirements, explore the relevant parts of the codebase, read documentation, try out different approaches, and finally land on a solution you like. When you’re finished, the code you submit is a two-dimensional snapshot of N-dimensional context. It’s lossy compression. The snapshot can be projected back into a model, but unless information is provided by other means, there will be significant gaps.

There are several ways to narrow those gaps:

  • Split up large PRs into smaller ones (perhaps targeting a feature branch).
  • Document and share ahead of time what problem you’re solving, how you’re solving it, and why you chose that approach.
  • Leave comments throughout your own PRs, explaining your thought process around less obvious changes.
  • Use pair programming or mob programming to share context as you build.
  • Use interactive rebasing (or the equivalent in your source control tool of choice) to rearrange commits and make PRs reviewable in small chunks.

We can make code review easier for each other, and we should. Shorter PRs are just one way to accomplish that.

Myth #2

Myth: The primary purpose of code review is to block bad code from getting into the application.

Fact: The primary purpose of code review is to align the team.

Don’t get me wrong, it’s important to look out for each other’s mistakes. Security vulnerabilities and critical bugs are often caught in code review, and they should be, though it’s better to catch them earlier—more on that in a moment.

Code review’s primary purpose, though, the purpose that consistently brings the most value, is alignment. Code review is a mechanism by which a team affirms:

  • We understand what changes are being made.
  • We understand why these changes are being made.
  • We can commit to maintaining this code.

Note that there’s nothing here about agreement. Agreement and alignment are different things. Team members may disagree about how to solve a problem, but if they share an understanding of the problem, along with the pros and cons of different approaches, they can commit to an imperfect solution and move forward anyway. That’s alignment.

A secondary purpose of code review is mentorship. Team members can use it to teach techniques, share ideas, and deepen each other’s knowledge of both the immediate context of the application and broader technologies (e.g. programming languages). One of the best kinds of code review comments is, “I think we could shave off several lines of code by using this technique/feature/class: [link].” Code review shouldn’t be the only place this happens, or even the main place, but it shouldn’t be discounted either—the things you learn from PR comments tend to stick with you, since by default they’re relevant, hands-on, and immediately actionable.

This mindset simplifies code review. The reviewer’s most important job is to make sure they understand all the whats and whys, which can be done at a high level, without necessarily reading every line of code. Their second job is to lift and teach each other, which often happens at a lower level of abstraction, but still above the level of semicolons and off-by-one errors. Their final job is to watch for mistakes: this is their most difficult but least important job, and they share it with several other people (quality engineers, product owners, UX designers) and tools (linters, compilers, unit and integration tests, vulnerability scanners, etc.).

Myth #3

Myth: Code review is the ultimate gatekeeper of software quality.

Fact: Code review is at its best when it’s a formality. On its own, it will always be insufficient to the task of protecting a codebase.

Pull requests should rarely be a surprise. Not every quick win or behind-the-scenes improvement needs to be announced, but most of the time, the team should know well in advance what’s coming. By the time someone’s submitting code, the only remaining unknowns should be implementation details, the general approach having already been aligned on.

But even when the team knows what’s coming, and even when the submitter provides context and documents their work, there’s still an essential knowledge gap between the code author and the reviewer. One of them wrote the code; the other is just looking at it. That gap may be larger or smaller, but I would argue it can’t be erased completely.

The force bridging that gap is trust. You can’t collaborate on software with people you don’t trust. It’s not blind trust—we watch out for each other’s mistakes, we challenge each other’s misconceptions, we never rely on trust alone—but depending on the day, the PR, or the application, it can still be substantial. A malicious teammate will have myriad opportunities to sneak in problematic code over the course of a project, and no code review process can completely prevent that, nor should it try to. Approaching every code review as if the author might be a saboteur is incredibly inefficient, not to mention hostile.

It’s up to the code reviewer how much trust they’re comfortable with, but as I mentioned earlier, their review should be one of several (human and automated) safeguards. And even in ideal circumstances, the “trust gap” can’t be optimized out; it is the optimization. At the end of the day, the code author has to be acting in good faith. They have to be committed to writing solid, high-quality code, or the codebase’s standards will inevitably drift over time. The world’s best code reviewer is no match for the world’s worst programmer.

So if code review isn’t the ultimate gatekeeper, what is? That depends on the problem you’re trying to solve.

If too many bugs are making it to production, the right response is to invest more in QA. If features repeatedly don’t satisfy specifications, the product owner needs to tighten their collaboration with engineering. If security vulnerabilities are frequently overlooked, the team needs to do more threat modeling or hire a pentester.

Code review is valuable. No one’s saying it isn’t. But it’s a small cog in a big machine.

Myth #4

Myth: Code review solves a lot of problems when you do it right.

Fact: Code review is supplemental to every meaningful outcome. It’s not a powerful enough tool to solve problems on its own.

This myth covers a lot of the same ground as the previous one, but it’s worth emphasizing.

Try this mental exercise: ask yourself (or your team) how you would work differently if code review were outlawed. Maybe you would:

  • Pair program more frequently.
  • Plan your changes more carefully and collaboratively.
  • Do more to address technical debt, like regularly setting aside time to refactor.
  • Collaborate more closely with QA and Product during the development process.
  • Document the team’s technical standards more clearly.
  • Be more proactive about teaching and training junior team members.
  • Write more automated tests and integrate better static analysis tools.

Whatever you came up with, you should do it. Full stop. You’ll get better results than you get from code review.

The promise of code review is its versatility. A good code reviewer can catch mistakes in a huge number of categories. But it’s also inefficient: for most of those categories, there’s a more effective, less exhausting way to keep watch. Finding that way is the work of technical leadership, and the work is never done.

Code review is a safeguard, yes, but it’s also the failure mode of a hundred other safeguards. If we pay attention, it can tell us where our processes fall short.

Conclusion

Code review is often exhausting, but it doesn’t have to be if everyone does their part: if code authors provide ample context and documentation, reviewers know their priorities, and the team invests in effective guardrails. As the team’s processes improve, code review gets faster and easier.