You just left a PR comment that made a junior developer mass-rewrite code that was already fine. You meant it as a suggestion. They read it as a command. Nobody learned anything, and the PR is now three days old.
I’ve been on every side of a bad code review. The one who left a terse “why?” with no context. The one who rewrote someone’s entire approach in the comments instead of having a conversation. The one who approved everything to avoid conflict. Twelve years of code review best practices distilled into what I wish someone had told me early: the hard part was never the code.
Here’s what I actually do differently now.
The Point of a Code Review Isn’t What You Think
Most guides will tell you code reviews catch bugs. They do. But that’s maybe 15% of the value.
The real returns: shared understanding of the codebase, mentorship that happens in context, and a forcing function for readable code. If someone else has to understand your diff, you write it differently. That pressure is the feature.
Here’s the thing. A review that only checks correctness misses the point. A review that only checks style is a waste of everyone’s time. The sweet spot is reviewing for comprehension — can a future developer who has never seen this code understand what it does and why?
That framing changes everything about how you leave comments.
But framing only matters if your comments land. And most review comments don’t land, because they fail at the human layer before anyone evaluates the technical content.
How to Give Feedback That Doesn’t Start a War
The fastest way to ruin a code review: attack the person instead of the code. You know this intellectually. You still do it.
“Why would you do it this way?” reads like “what’s wrong with you?” even if you’re genuinely curious. The fix is mechanical — talk about the code, not the author.
Instead of this:
Why did you use a mutex here? There's no concurrent access.
Try this:
I don't think this path is called concurrently — if that's right,
the mutex adds complexity without a benefit. Am I missing a case?
Same technical point. Different emotional result. The second version does three things the first doesn’t: it states the reviewer’s understanding, it names the tradeoff, and it leaves room to be wrong.
That last part matters more than people admit. I’ve been confidently wrong in reviews more times than I want to count. Phrasing comments as questions isn’t weakness. It’s accuracy — you’re working from a partial view of someone else’s context.
Label Your Intent
Not every comment carries the same weight. Some things need to change before merge. Some are stylistic preferences. Some are “I found this interesting, here’s a thought.” When you don’t label intent, every comment feels like a blocker.
A system I’ve used across three teams:
blocking:This needs to change. I’ll explain why.nit:Minor style or naming thing. Take it or leave it.question:I don’t understand this. Not a criticism, genuinely asking.suggestion:Here’s an alternative approach. No pressure.praise:This is good. Calling it out because people need to hear it.
That praise: prefix is not a joke. If the only time someone hears from you in a review is when something’s wrong, they’ll start dreading your name in the reviewer slot. Positive reinforcement in context — “this error handling is thorough, nice” — costs you five seconds and changes the dynamic.
The real shift happens when the whole team adopts labels. Suddenly a PR with eight comments isn’t scary, because five of them are nits and one is praise. The author knows exactly where to focus.
But giving feedback is only half the equation. The harder skill, the one nobody teaches, is receiving it.
How to Receive a Code Review Without Losing Your Mind
Gerald Weinberg wrote about “egoless programming” in 1971. Over fifty years later, most developers still treat code criticism as personal criticism. It’s not a character flaw — it’s biology. You spent hours solving a problem. Someone points out a flaw in two minutes. Your brain registers that as a threat.
Here’s what I tell developers I mentor: the review isn’t about you. It’s about the code. And the code is going to change a hundred more times after this PR.
Practical tactics that help:
Wait before responding. If a comment stings, close the tab. Come back in twenty minutes. The comment usually reads differently once the initial reaction fades. I’ve drafted defensive replies that I’m very glad I never sent.
Assume good intent by default. Most reviewers aren’t trying to make you feel bad. They’re trying to ship good software under time pressure, and their comment phrasing suffers for it. If you aren’t sure whether someone is being dismissive or just terse, ask: “Can you say more about this? I want to make sure I understand.”
Separate your identity from your code. This sounds obvious. In practice, it requires deliberate effort — especially early in your career. Your code is a snapshot of your understanding at one moment.
It is not a measurement of your ability. The developers I respect most are the ones who treat their own code with the least reverence — they’d rather find the problem now than discover it in production at 2 AM. If you’re preparing for your first developer job, internalizing this early gives you a real advantage.
Ask questions back. A comment says “this could be simpler.” Instead of getting defensive, ask: “What would simpler look like here?” Now it’s a collaboration, not a judgment. And sometimes the reviewer realizes they didn’t have a concrete alternative — which is also useful information.
The ability to receive critical feedback without shutting down is one of the clearest signals that separate mid-level from senior. Technical skills plateau. Communication skills compound.
What to Actually Look For in a Review
Here’s where most code review guides start — and where I think the ordering matters. If you don’t have the human dynamics right, nothing in this section matters. People won’t hear your feedback if it’s delivered poorly.
But assuming you’ve got the communication part down, here’s what deserves your attention. In priority order:
1. Does it do what it claims to do?
Read the PR description first. Then read the diff. Do they match? You’d be surprised how often the code does something slightly different from what the description says. That gap is where bugs live.
2. Can you understand it without the author explaining it?
If you have to re-read a function three times, that’s a signal. Not that the author is bad — that the code needs better naming, or a comment, or to be split into smaller pieces. Code that requires its author to be present to understand is code that will rot.
3. Are the error paths handled?
Happy paths are easy to review because they’re what the author was thinking about. But production code fails. What happens when that API call returns a 500? When the database connection drops? When the input is null where you expected a string? This is where your experience as a reviewer adds the most value.
4. Does the test coverage match the risk?
I don’t fixate on coverage percentages. A function that calculates billing needs thorough tests. A function that formats a log message probably doesn’t. Match the investment to the stakes.
5. Does it fit the existing codebase?
Not in a “enforce my style preferences” way. In a “will someone maintaining this in six months know where to find things” way. If the rest of the codebase uses repository pattern and this PR introduces a raw SQL query in a handler, that’s worth a conversation.
Notice what isn’t on the list: variable naming conventions, bracket placement, import ordering. Automate those. Run a linter. Run a formatter. If you’re spending review time on whitespace, you’ve already lost. A solid git workflow for small teams should include linting in the CI pipeline so these discussions never reach a human.
The PR Author’s Side: Make Your Code Easier to Review
Code reviews are a two-player game. If you’re the one opening the PR, you control more than you think.
Write a real PR description. Not “fixed the thing.” What did you change? Why did you change it? What did you consider and reject? A good PR description is a one-paragraph cover letter for your diff. Reviewers who understand the context leave better comments.
Here’s a template I’ve used:
## What
Added rate limiting to the /api/upload endpoint.
## Why
We're seeing abuse from automated clients. 3 incidents this month.
## How
Token bucket algorithm, 10 requests/minute per API key.
Used golang.org/x/time/rate — it's already in our dependency tree.
## What I considered
- Per-IP limiting (rejected: too easy to bypass with proxies)
- Global rate limit (rejected: punishes legitimate users)
That took ninety seconds to write. It saves the reviewer ten minutes of “wait, why are we doing this?”
Keep PRs small. Under 400 lines of actual changes (not counting generated code or lock files). Past that, review quality drops off a cliff. I’ve seen studies and I’ve seen it in practice: reviewers start skimming around line 300. If your change is bigger, break it into stacked PRs.
Review your own PR first. Open the diff and read it fresh. You’ll catch at least two things — a debug log you left in, a TODO you forgot to resolve, a function that’s doing too much. Self-review is the cheapest quality gate you have.
Respond to every comment. Even if it’s “done” or “good point, fixed.” The reviewer put time into reading your code. Acknowledging their comments closes the loop and shows respect.
The Comment That Changed How I Review Code
Early in my career, I left a review comment on a junior developer’s PR that said: “This entire approach is wrong. See how the auth service handles this.”
Technically accurate. The junior’s approach had a subtle bug in session handling that would have caused issues at scale. My comment was correct.
It was also terrible.
The junior rewrote the entire thing over a weekend, burned out, and stopped submitting PRs without asking permission first. I didn’t learn about the impact until months later, during a retrospective where someone else brought it up.
I could have written: “There’s a session edge case here that’ll bite us with concurrent users. The auth service solved a similar problem — want to pair on adapting that pattern?” Same result. No damage.
The technical bar of code review is low. Read the code. Spot the issues. Suggest the fix. Any experienced developer can do this.
The human bar is high. Frame feedback so that the author walks away more capable, not more cautious. That’s the difference between a code review culture that makes people better and one that makes people afraid.
The Bottom Line
Code reviews are a skill, not a ceremony. They’re where mentorship, quality, and shared ownership actually happen — if you get the human part right.
The pragmatic answer to code review best practices has never been about checklists. It’s about two things: make your feedback about the code, not the person, and make it easy for others to review your work. Everything else follows from there.
Start with labels on your next review. Write one real PR description. Ask one question instead of making one declaration. Small changes. Compounding returns. Your team will write better code because they stopped dreading the process.