Code review is one of the few practices nearly every team claims to value and nearly every team does badly. The pull request sits for two days. The reviewer rubber-stamps it with “LGTM” after skimming the diff. Or worse, review turns into a proxy war over tabs versus spaces while an actual concurrency bug sails through untouched. When review works, it catches defects early, spreads knowledge, and slowly raises the whole team’s bar. When it doesn’t, it is bureaucratic theater that everyone resents.

The gap between those two outcomes is not talent. It is process and habit. Good review is a skill with learnable techniques, and most of them are unglamorous: keep changes small, know what you are actually looking for, respond quickly, and offload the mechanical checks to machines. Here is what separates review that improves code from review that just delays it.

What is code review actually for?

Before arguing about tactics, get clear on the goal, because teams that skip this end up optimizing for the wrong thing. Review is not primarily about catching bugs—your tests and CI catch more bugs than any human reviewer ever will. Its highest-value functions are subtler.

Review spreads knowledge. When a second person reads a change, the codebase stops having single points of human failure. The reviewer learns a part of the system they didn’t write, and the author gets a sanity check on assumptions. Six months later, when the author is on vacation and the code breaks, someone else has seen it.

Review improves design. The best time to catch a bad abstraction is before it calcifies. A reviewer with fresh eyes will ask “why is this class doing three things?” in a way the author, deep in the weeds, cannot. Google’s engineering practices put it plainly: the primary thing a reviewer should assess is whether the change is well-designed and appropriate for the system. Their engineering practices documentation is a widely respected primary source and worth reading in full.

And review sets a standard. Every approved PR is an implicit statement of “this is good enough here.” Over hundreds of reviews, that standard is the culture. Understanding this reframes everything else: you are not a gatekeeper hunting for reasons to reject; you are a collaborator helping ship the best version of a change.

Why do small pull requests matter so much?

If you change one thing about how your team reviews code, make the pull requests smaller. This single lever improves nearly every review metric that matters.

Large PRs get bad reviews. A study-backed reality that anyone who has reviewed a 2,000-line diff knows in their bones: reviewer attention drops off a cliff after the first few hundred lines. A huge changeset gets a cursory pass and an approval, because thorough review would take hours the reviewer doesn’t have. The big PR feels efficient to the author and is actively worse for quality.

Small PRs get real reviews. A 150-line change can be understood completely. The reviewer can hold the whole thing in their head, reason about edge cases, and give feedback that engages with the substance. Defects that would vanish into the noise of a large diff become visible.

Small PRs also move faster. A reviewer can fit a small review into the gaps of their day; a large one requires blocking out a meeting-sized chunk, so it gets deferred. The counterintuitive result is that a feature split into five small PRs often ships faster end-to-end than the same feature as one giant one.

The objection is always “but my change is inherently large.” Usually it isn’t. Separate refactoring from behavior change. Land the new interface before the code that uses it. Use feature flags to merge incomplete work safely. The discipline of decomposition is itself a design skill—see our software engineering section for how this connects to broader architecture practice.

What should you actually look for in a review?

A reviewer staring at a diff without a mental checklist tends to comment on whatever catches the eye first, which is usually style. Here is a rough priority order, from most to least important.

Correctness and design first. Does the change do what it claims? Are there edge cases—empty input, concurrent access, failure of a downstream call—that aren’t handled? Does the design fit the existing system, or does it bolt on a parallel way of doing something that already has a pattern?

Then tests. Is the new behavior actually tested? Would the tests fail if the code were wrong? A test that passes regardless of the implementation is worse than no test because it provides false confidence.

Then readability. Will the next person understand this? Good naming, clear control flow, comments that explain why rather than what. If you have to ask the author what a block does, so will everyone after you.

Then, and only then, style. Formatting, naming conventions, import order. These matter, but they are the least valuable use of human attention—which is exactly why you should automate them away entirely (more on that below).

One practical tip: if you find yourself writing the same comment repeatedly across the team, it is a candidate for a shared snippet or a lint rule. Capturing those recurring patterns—see our guide on managing code snippets—turns one-off review feedback into reusable team knowledge.

How do you give feedback without starting a fight?

Review is where technical judgment meets human ego, and tone determines whether feedback lands or breeds resentment. The code is not the person, but it is very easy for an author to feel otherwise when their work is picked apart.

Ask, don’t command. “What happens if items is empty here?” invites the author to reason and often to discover the bug themselves. “This will crash on empty input, fix it” invites defensiveness. Questions preserve the author’s agency and frequently surface context the reviewer lacked.

Distinguish blocking from optional. Not every comment is a demand. Prefix nitpicks explicitly: “nit: could rename this for clarity, non-blocking.” This tells the author which feedback must be addressed before merge and which is a suggestion they can take or leave. Without this signal, authors either treat everything as mandatory (slow) or ignore everything (defeats the purpose).

Praise real cleverness. A review that is only criticism is exhausting to receive. When someone handles a tricky case elegantly, say so. It costs nothing and it makes the harder feedback easier to hear.

And review your own tone as if you were receiving it. Sarcasm, exasperation, and “obviously” have no place. The reviewer holds power in the interaction; using it graciously is what makes review sustainable over years rather than a source of quiet team friction.

How fast should reviews happen?

Review latency is the silent killer of team velocity, and it is under-measured because nobody logs the hours a PR spends waiting. When a change sits unreviewed, the author context-switches to something else, the branch drifts from main, and merge conflicts accumulate. By the time review arrives, the author has to reload the entire mental context to respond.

The rule of thumb that works: review requests should be answered within one business day, and ideally within a few hours. This does not mean dropping everything the instant a PR arrives—constant interruption destroys the reviewer’s own flow. It means checking for review requests at natural breakpoints, a few times a day, and treating a waiting PR as blocking a colleague, because it is.

Fast review is partly a small-PR problem in disguise. A 100-line change can be reviewed in the ten-minute gap before a meeting. A 1,000-line change cannot, so it waits for a slot that never comes. Shrink the PRs and the latency problem shrinks with it.

For genuinely urgent changes, agree on an explicit fast-track signal so authors don’t resort to pinging reviewers directly and reviewers know when to interrupt their own work. Make the normal case fast and the exception rare.

Can automation replace human reviewers?

No—but it can make human reviewers dramatically more valuable by removing everything a machine does better. This is the highest-leverage change most teams can make to their review process.

Formatting should never be a review comment. Run a formatter (Prettier, Black, gofmt) in CI or as a pre-commit hook, and the entire category of style debate disappears. Linters catch unused variables, obvious bugs, and convention violations automatically. Type checkers catch a whole class of errors before a human ever looks. Every one of these is a check a person no longer has to make.

Set up your continuous integration to run the full suite—format check, lint, type check, tests—on every PR, and make a passing build a merge prerequisite. Now when a human opens the diff, the mechanical questions are already answered, and their attention is free for the things only a human can assess: is this the right design, does it fit the system, will the next engineer understand it?

That is the correct division of labor. Machines handle the deterministic, tireless, boring checks. Humans handle judgment, design, and knowledge transfer. A team that gets this split right finds that review gets faster and better at the same time, because human effort is no longer wasted on work a script could do.

Review is also where refactoring proposals usually surface first—a reviewer flags a pattern that’s caused repeated trouble, or an author admits the surrounding code fought them the whole way through. Knowing when to actually act on that signal, versus when it’s a style preference dressed up as a structural concern, is a judgment call review alone doesn’t answer.

Frequently Asked Questions

How large should a pull request be?

Aim for changes a reviewer can fully understand in one sitting—roughly under 400 lines, and smaller is better. Beyond that, reviewer attention drops sharply and the review becomes a rubber stamp. If a change is inherently large, decompose it: separate refactors from behavior changes, land interfaces before their callers, and use feature flags to merge incomplete work safely.

What should I prioritize when reviewing code?

Review in priority order: correctness and design first, then whether tests actually exercise the new behavior, then readability for the next developer, and style last. Style should be automated away with formatters and linters so human attention is reserved for design and correctness—the things only a person can meaningfully assess.

How do I give feedback without offending the author?

Ask questions instead of issuing commands, mark nitpicks as non-blocking so authors know what is optional, and acknowledge genuinely good work. Remember the reviewer holds power in the exchange; use it graciously. The goal is to help ship the best version of a change collaboratively, not to gatekeep or score points against a colleague.

How quickly should code reviews be turned around?

Answer review requests within one business day, ideally within a few hours, checking at natural breakpoints rather than interrupting your flow constantly. Waiting PRs block colleagues, cause branch drift, and force costly context reloads. Much of the latency problem solves itself once pull requests are small enough to review in the gaps of a normal day.

Can automated tools replace human code reviewers?

No, but they should handle everything humans do worse. Run formatters, linters, type checkers, and tests in CI, and make passing them a merge requirement. This removes mechanical checks from human hands entirely, freeing reviewers to focus on design, correctness, and knowledge transfer—work that machines cannot do. Automation and human review are complementary, not competing.