- Introduction — Review happens between people, not just code
- Review the code, not the person
- Nits and blockers — conventional comments
- Ask questions, don't issue demands
- Small PRs — why large ones only get rubber-stamped
- Praise good code
- Receiving feedback gracefully — the author's stance
- The reviewer's responsibilities and the author's — a two-sided contract
- Wrapping Up
- References
Introduction — Review happens between people, not just code
On the surface, code review is a technical activity. You find bugs, validate design, enforce style. But anyone who has spent time on a team knows the truth: review breaks down not because of the technology but because of the people. A perfectly correct critique can damage a relationship. A trivial comment can spiral into a three-day standoff. A review request can sit ignored for days and quietly drain the author's motivation.
So the premise of this post is simple: code review is fundamentally communication. The code is just the medium; what actually passes between two engineers is judgment, intent, and trust. Once you accept this framing, a lot of review problems look different. "Is this critique correct?" matters, but so does "how is this critique delivered?"
This post walks through concrete ways to give and receive feedback without friction: framing critique at the code and not the person, labeling comments with conventional comments, asking questions instead of demanding, the power of small PRs, the role of praise, how to receive feedback as an author, and the responsibilities each side carries.
Review the code, not the person
The most fundamental principle in review is that the target of critique is the code, not the person. This is not merely a matter of politeness; it is a matter of accuracy. The purpose of review is to improve the codebase, not to evaluate the author.
The same observation lands completely differently depending on framing.
- "You forgot error handling here." → An arrow aimed at the author. It makes them defensive.
- "It looks like error handling is missing on this path, could we check it?" → Aimed at the code. An invitation to look together.
The key tool is to change the subject of the sentence. Replace "you" with "we" or "this code."
- Instead of "Why did you write it this way?" → "I'm curious about the reason for this approach."
- Instead of "Your function is too long." → "This function might be easier to read if we split it up."
- Instead of "You introduced a bug." → "This code looks like it would throw on an empty list."
We are standing on the same side, looking together at the problem that is the code, not facing off and aiming at each other.
This "we" framing is not just a euphemism. It reflects something that is actually true. Once code is merged, it belongs to the whole team. That bug will soon be everyone's on-call page. So "we" is an honest word. Author and reviewer are collaborators working toward the shared goal of code quality.
Nits and blockers — conventional comments
A common misconception in review is that every comment carries the same weight. From the author's side, a trivial suggestion about a variable name and a serious defect that causes data loss appear as identical comment boxes stacked side by side. There is no way to tell which one absolutely must be fixed and which one can be ignored. That ambiguity is a major source of friction.
Conventional Comments solve this with labels. It is a simple convention of prefixing every review comment with a label that signals the comment's intent and severity. The common labels are:
- nit: A very minor point. Nice to fix but not blocking a merge. (e.g., minor style.)
- suggestion: A proposed improvement. The author decides whether to take it.
- question: A genuine question. When you don't understand something or want to confirm intent.
- blocker: (or issue) A problem that must be resolved before merging.
- praise: Praise. Explicitly calling out what was done well.
- nitpick / thought / typo and other variants, depending on the team.
A labeled comment looks like this:
nit: const would be clearer than let here.
question: what happens when this list is empty?
blocker: this leaks a file handle on this path — it never gets closed.
suggestion: pulling this condition into an early return would reduce the nesting.
praise: nice use of an early return here — really clean.
The difference a single label makes is large.
- The author knows the priority. With blockers and nits clearly separated, the author immediately knows what to handle first and what is theirs to decide by taste.
- The reviewer's tone softens. Those five letters, "nit: ", automatically carry the signal "this is minor, no pressure."
- Arguments shrink. If the author skips a suggestion marked as a nit, nobody is offended, because it was agreed up front that "this is not required."
Conventional comments also have an optional device called decorators beyond the label: extra context in parentheses, such as (non-blocking). So you might write suggestion (non-blocking): .... You don't even need the full formality. Just a shared understanding that "nits can be ignored" visibly changes a team's review culture.
Ask questions, don't issue demands
Of all the ways to deliver a critique, the most powerful shift is turning a demand into a question. There are two reasons for this: one human, one technical.
The human reason first. A demand makes the author defensive. "This is wrong" sounds like a verdict. The author reacts by either pushing back or shrinking. A question like "What happens if x is null?" makes the author think instead. Inquiry replaces defense.
Then the technical reason: you might be wrong. The reviewer often has less context than the author. If you declare "this is a bug" and it turns out there was already a guard somewhere upstream, you look foolish. The question form naturally avoids this risk, because a question carries the humility of "I might be missing something."
A few examples of turning demands into questions:
- "You're missing a null check." → "Is there a case where this value comes in as null?"
- "You need a lock here." → "What happens if several threads touch this at the same time?"
- "This query is slow." → "When this table grows to millions of rows, which index will this query use?"
- "This logic is off." → "Could you walk me through exactly which case this branch handles?"
That said, you need balance. Not everything needs to be dressed up as a question. For obvious facts (a typo, a syntax error) or a certain blocker, phrasing it as a question can feel passive-aggressive or waste time. For something you are sure of, like "this loop skips the last element (off-by-one)," it is better to say it plainly. Questions are a tool for when you are uncertain, and when you want to guide the author's thinking.
One more thing: distinguish a real question from a fake question. If "Why did you do it this way?" is actually a disguised "This is wrong," the author will see through it quickly. Such veiled criticism is worse than an honest critique. If you're going to ask, genuinely want the answer. If you are certain something is a real problem, don't disguise it as a question — mark it clearly as a blocker.
Small PRs — why large ones only get rubber-stamped
The single most powerful factor governing review quality is, surprisingly, not a communication technique but the size of the PR. This ties directly to human psychology.
Imagine you receive an 800-line PR. The reviewer is subconsciously overwhelmed. Honestly reviewing that big chunk line by line would take over an hour, and other work is piling up. The outcome is predictable: the reviewer skims and clicks "LGTM." This is rubber-stamping — review happens in form only, with no substantive scrutiny.
Research and experience consistently say:
- Large PRs have lower defect-detection rates. A reviewer's attention is finite, and a large PR spreads that attention thin. It is commonly observed that defect detection drops sharply past a couple hundred lines.
- Small PRs get real review. A 50-line change fits entirely in the reviewer's head, and they can actually think about each line.
- Small PRs merge faster. Because review isn't a burden, they don't get abandoned, and feedback round-trips are quick.
So good authors split changes into small, cohesive units. Ideally one PR contains one logical change. Don't mix a refactor and a feature in the same PR. The moment a reviewer has to keep asking "is this line a refactor or new behavior?", review gets dramatically harder.
When a large change is unavoidable, there are strategies:
- Split into logical commits. Organize the history so the reviewer can follow commit by commit. The fastest way to internalize this kind of commit splitting and branching strategy is to practice it against a real repository. Getting comfortable with splitting branches and reshaping commits in the Git Playground makes large changes far easier to handle.
- Stacked PRs. Build a large feature as several small, dependent PRs. Each PR is reviewable on its own.
- Keep pure moves separate. Changes that merely move code around (moves, renames) should be split from logic changes into their own PR, so the diff stays clean.
Praise good code
Most reviews point out only problems. The reviewer subconsciously enters hunter mode, searching for defects, and the good parts pass by in silence. But this is a missed opportunity and a habit that erodes review culture.
Praise matters for several reasons:
- It keeps review from being a purely negative experience. When one sincere bit of praise sits among ten critiques, the author feels the review as a conversation rather than an attack. This is about psychological safety.
- It reinforces good patterns. When you explicitly call out which approach was excellent, the author and the team repeat that pattern. Review is not just a defect filter; it is a place to learn.
- It builds trust. When the author knows a reviewer recognizes what's good, they take that reviewer's criticism more seriously too.
Praise has force when it is specific. "Good job" is empty. "I love that you handled this error case up front — you saved whoever debugs this later" tells the author what is good and why.
praise: nice turning this recursion into a loop — no more stack-overflow worries.
praise: thank you for the thorough boundary cases in the tests; made this easy to review.
praise: this one comment perfectly explains why it's written this way. Thank you.
A caution: hollow praise backfires. Mechanically inserting praise into every PR strips it of weight. When you see something genuinely well done, that is when to call it out sincerely. That authenticity is what keeps praise alive.
Receiving feedback gracefully — the author's stance
So far we have mostly covered the reviewer's posture. But review is two-sided. The stance of the person receiving feedback shapes review culture just as much. And honestly, this side is much harder, because not reacting defensively when your code is criticized runs against instinct.
A few core principles:
1. Separate your ego from your code. This is the most important one. Your code is not you. Criticism of the code is not criticism of you as a person. Without this separation, every review feels like a personal attack. With it, a critique is simply information that makes the code better. The more seasoned the engineer, the more they treat criticism of their code as a free improvement opportunity rather than a threat.
2. Assume good intent. When a reviewer's comment is terse or the tone is ambiguous, don't assume the worst intent. Most critiques come from a genuine desire to improve the code. Text easily loses tone, and the reviewer may simply have been busy and written briefly. Read it as "what is this person worried about?" rather than "is this person dismissing me?"
3. Thank the reviewer. Review takes time and energy. Someone looking carefully at your code is a gift. Especially when they catch a good bug, thank them explicitly. A single line like "great catch, I almost missed that" means a lot to a reviewer.
4. Disagree by discussing, but gracefully. You don't have to accept every critique. The reviewer might be wrong, or might have missed context. When that happens, respond with explanation, not defense. Not "you're wrong," but "I can see it that way, but I did it like this for these reasons — what do you think?" And if you truly can't reach agreement, don't argue endlessly in the comment thread — move it to a call or a face-to-face chat. Text amplifies conflict; a voice softens it.
5. Respond to every comment. Whether you applied the critique or decided not to, leave at least a short reaction on each comment. Silence makes the reviewer anxious and feels like being ignored. A single line, "fixed" or "leaving this as is for this reason," cleanly closes the thread.
Defensiveness protects your code, but openness protects your growth.
The reviewer's responsibilities and the author's — a two-sided contract
A good review culture is not built by one side's effort alone. It is a two-sided contract in which reviewer and author each faithfully carry their responsibilities. When one side breaks the contract, the other side's effort collapses too.
Placing the two roles' responsibilities side by side:
| Reviewer's responsibilities | Author's responsibilities |
|---|---|
| Respond quickly (don't let reviews sit) | Make PRs small enough to review |
| Critique the code, separated from the person | Separate ego from code when receiving feedback |
| Clearly distinguish blockers from nits | Write a good description (what and why) |
| Explain the reason behind each critique | Respond to every comment |
| Call out the good parts too | Assume good intent and say thanks |
| Separate personal taste from real problems | Respect the reviewer's time (self-review first) |
A few items from this table deserve emphasis.
Review latency is a silent poison. When reviews sit for days, the author loses context, the branch goes stale, merge conflicts pile up, and above all, motivation drains. Good teams treat review as high-priority work. "A perfect review tomorrow" is usually worse than "a good-enough review today." Remember that a review request is work that is blocking someone else's progress.
The author has a responsibility to lighten the reviewer's load. A good PR description is half the review. Write what the change does, why it is needed, how you tested it, and where the reviewer should look especially carefully. And before requesting review, read your own diff first. Catching leftover debug output, commented-out code, and typos yourself saves the reviewer's time. A self-review is the minimum courtesy.
The reviewer has a responsibility to separate taste from problems. "I wouldn't have written it this way" is usually taste, not a problem. If the code is correct and clear, you should not demand changes merely because it differs from your personal style. Spend energy on what truly matters (correctness, readability, maintainability), and leave differences of taste as a light nit or let them go entirely.
When this contract holds, review becomes collaboration rather than a gate — two people working toward the same goal, each trusting the other's role.
Wrapping Up
When you view code review as communication, it becomes clear that most sources of friction lie not in the technology but in the delivery. Aiming at the code and not the person, using "we" instead of "you," separating nits from blockers with labels, asking instead of demanding, splitting changes small, and praising what's done well — what all these techniques share is that they treat the other person as a collaborator.
And review is two-sided. The author is responsible for separating ego from code, assuming good intent, thanking the reviewer, and producing PRs that are easy to review. The reviewer is responsible for responding quickly, explaining reasons, and separating taste from problems. When both sides hold to this contract, review stops being a place to face off and becomes a place to make the code better together.
A technically perfect review can damage a relationship, and a kind but sloppy review can let a bug through. A good review catches both — accurate yet kind, rigorous yet respectful. That is the essence of feedback without friction.
References
- Conventional Comments (a convention for labeling comments): https://conventionalcomments.org/
- Google, Code Review Developer Guide (engineering practices): https://google.github.io/eng-practices/review/
- Michael Lynch, "How to Do Code Reviews Like a Human": https://mtlynch.io/human-code-reviews-1/
- Thoughtbot, Code Review Guide: https://github.com/thoughtbot/guides/tree/main/code-review
- Chromium, Respectful Code Reviews: https://chromium.googlesource.com/chromium/src/+/HEAD/docs/cr_respect.md
- Google, "The Standard of Code Review": https://google.github.io/eng-practices/review/reviewer/standard.html
현재 단락 (1/93)
On the surface, code review is a technical activity. You find bugs, validate design, enforce style. ...