Skip to content

✍️ 필사 모드: Authoring Reviewable Pull Requests: Small PRs, Good Descriptions, and Stacked Diffs

English
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

"A good PR is a gift to the reviewer. A bad PR is homework dumped on them."

Prologue — The author controls review latency

"Why does my PR always take so long to merge?" Many engineers look for the answer in the reviewer: they're busy, it's low priority, the team is slow. Some of that is true. But the biggest variable lives somewhere else. A PR's review latency is mostly decided by its author.

When a reviewer opens a PR and finds an 800-line diff with a one-line description ("add feature"), that PR gets pushed to "later." Reviewers are people too, and work that demands a heavy cognitive load naturally slides to the back of the queue. Conversely, a 120-line diff that clearly states "what, why, and how to verify" gets finished in the five minutes a reviewer waits for coffee.

This is not a post about how to do code review. That's the reviewer's craft — review etiquette, merge queues, review prioritization belong to the receiving side. This is a post about how to author a PR: everything the author can do to make the reviewer's job easy and fast.

A good PR is consideration, not negotiation

People who see a PR as "the gate my code has to pass through" and people who see it as "a place to verify code together with a reviewer" produce completely different PRs. The former makes the diff big, writes a short description, and answers questions defensively. The latter cuts the diff small, supplies enough context, and guides the reviewer on where to start.

Consideration is not an abstract virtue. It is a set of measurable behaviors — cutting the PR small, writing the verification steps in the description, separating formatting changes, doing a self-review first. This post is a list of those behaviors.

What this post covers:

ChapterTopic
1Why small PRs win — the cognitive cliff
2One PR = one idea
3The PR description — your most important artifact
4Making the diff readable — commit hygiene
5Stacked PRs / stacked diffs
6Self-review before requesting review
7Responding well to review feedback
8Draft PRs and signaling readiness
9Authoring PRs in the AI era
EpilogueChecklist + anti-patterns + next post teaser

Chapter 1 · Why small PRs win

If you had to pick the single most effective piece of PR-authoring advice, it would be: make it small. Every other technique stacks on top of this one.

1.1 Review quality is inversely proportional to PR size

A reviewer's focus is not infinite. As the diff grows, the attention spent per line shrinks. So big PRs produce a paradox — the more you change, the less each change is examined.

PR sizeWhat the reviewer actually doesResult
~50 linesReads and thinks about every lineCatches bugs and design issues
~200 linesReads the core, skims the restCatches only surface-level issues
~500 lines"Looks fine, no big problems" → approveEffectively unreviewed
1000+ linesApproves with guilt, or defers indefinitelyReview becomes a formality

An "LGTM" on a 500-line PR is not a review — it's a surrender. If what the author truly wants is scrutiny, they have to hand over a scrutinizable size.

1.2 The cognitive cliff

The relationship between PR size and review quality is not linear. Past a certain point there is a cliff where it drops off sharply. The exact number varies by team and codebase, but empirically that cliff sits somewhere around 200 to 400 lines.

Below the cliff, the reviewer can hold the whole PR in their head at once. Above it, that becomes impossible, and the reviewer starts reading the PR "in pieces." Reading in pieces means missing the interactions between the pieces — which is exactly where bugs hide most.

The goal is simple: keep every PR below the cliff. If the work is large, you cut the work — you do not grow the PR.

1.3 The compounding effect of small PRs

Small PRs don't just raise review quality.

  • Fast feedback — Small PRs get reviewed fast, merged fast, deployed fast. If the direction is wrong, you find out early.
  • Easy rollback — When something breaks, the unit you revert is small. Instead of hunting through 800 lines for the culprit, you revert one 80-line PR.
  • Fewer conflicts — Small PRs don't live long, so they have no time to drift from main. Rebase hell shrinks.
  • Clear historygit log becomes a record of meaningful units of change, not a giant "add feature" commit.

Going small is not a cost; it's an investment. The ten minutes spent cutting prevents the multi-day delay a big PR creates.


Chapter 2 · One PR = one idea

Just as important as going small is carrying only one thing. Even a small PR becomes hard to review when two unrelated changes are mixed into it.

2.1 The "no mixed PRs" rule

The most common and most harmful anti-pattern is mixing a refactor with a behavior change in one PR.

The reviewer looks at a changed function. The name changed, the location moved, the indentation is different, and somewhere inside it one if condition changed. The reviewer now can't ask the question — "Is this behavior change intentional, or a mistake made while refactoring?" The noise has buried the signal.

The rule is simple.

One PR carries one idea. Never put a refactor and a behavior change in the same PR.

The order is fixed too: refactor first, behavior change second. A refactor PR can declare "behavior identical, structure only changed," and the reviewer can skim it fast. Then the behavior-change PR arrives as a small diff on a clean foundation.

2.2 What counts as "one idea"

Wrong bundleCorrectly split PRs
Feature A + a refactor for Feature A + an unrelated typo fixPR 1: refactor / PR 2: Feature A / PR 3: typo
Bug fix + a dependency upgrade done "while we're at it"PR 1: dependency upgrade / PR 2: bug fix
New API endpoint + whole-file reformattingPR 1: formatting / PR 2: endpoint
Feature + the tests for that featureBoth in one PR — the tests are part of the feature

The last row is the key. "One idea" is not "one file" or "one commit." A feature and its tests are the same idea, so they go together. An unrelated typo, however small, is a different idea, so it gets separated.

2.3 "While I'm at it" is a warning siren

While building a PR, temptation arrives — "while I'm in this file, let me delete that dead code," "while I'm here, let me rename that variable." That "while I'm at it" is the exact moment a mixed PR is born.

The principle: when "while I'm at it" pops up, stop, and turn it into a separate PR or ticket. This is not about throwing away a good discovery. It's about moving that discovery into its own reviewable unit.


Chapter 3 · The PR description — your most important artifact

The most-used and least-cared-for part of a PR is the description. Many authors spend two hours on the diff and twenty seconds on the description. That's backwards.

3.1 The description answers what the diff cannot

The diff shows what changed. What the diff can never show is why. And the central question of review is almost always "why" — "why this way," "why not another way," "why now."

Without a description the reviewer has to guess the "why," the guess is wrong, and review comments built on a wrong guess burn the time of both author and reviewer.

3.2 The six elements of a good PR description

ElementQuestion it answersWhat happens without it
WhatWhat does this PR change?The reviewer has to read the whole diff and summarize it
WhyWhy is this change needed?The reviewer guesses the intent
HowWhat approach did you take, and why that one?The design intent doesn't get communicated
VerificationHow did you confirm it?A "did you test it?" round trip happens
Out of scopeWhat is not being done this time?"Shouldn't this be fixed too?" comments pile up
ScreenshotsHow does the UI change look?The reviewer pulls the branch and runs it themselves

Of these six, "out of scope" is the most often missing and the most effective. A single line — "this PR does not do X — that's a follow-up PR" — preempts five comments the reviewer was about to write.

3.3 A reading guide for the reviewer

When a large change is unavoidable (for example, when generated files are included), write the reading order in the description.

## Review guide
- The core logic is in `src/auth/session.ts`. Start here.
- `src/generated/*` is codegen output. Skim and move on.
- The `package-lock.json` change is a side effect of the dependency addition.

This one block changes the felt weight of "the diff is 600 lines" into "you actually need to look at 80 lines."

3.4 A copyable PR description template

Below is a template you can copy directly into .github/pull_request_template.md or pin in your team wiki. The outer fence uses four backticks because it contains code blocks and ## headers inside.

## What
{What this PR changes, in one or two sentences}

## Why
{Why this change is needed. Linked issue: #123}

## How
{The approach taken and why. One line on any alternative considered and dropped}

## How to verify
- [ ] {A check step the reviewer can follow}
- [ ] {Tests added / modified}
- [ ] {Manual verification steps — if any}

```bash
# Reproduction / verification command — if any
npm test -- src/auth
```

## Out of scope
- {What is not being done this time — and where it will be handled}

## Screenshots (for UI changes)
| Before | After |
| --- | --- |
| {img} | {img} |

## Review guide (when the diff is large)
- {Where to start}
- {Files you can skim and move past}

You don't have to fill every field. If there's no UI change, delete the screenshots field. But keep the three fields — What, Why, Verification — in every PR. Those three cut 90% of review latency.

3.5 The title is a description too

The PR title lives in git log forever. Titles like "fix," "update," "wip" give zero information six months later.

  • Bad: update
  • Bad: bug fix
  • Good: auth: add automatic token refresh on session expiry
  • Good: fix: disable the checkout button on an empty cart

The rule: the title states, in one line, what changes once this PR is merged. Signaling the area and the kind with a prefix (auth:, fix:) is even better.


Chapter 4 · Making the diff readable — commit hygiene

Even if you made the PR small and single-idea, review is still hard if the commits inside it are a mess. The diff has to be structured to be read.

4.1 Logical commits

A reviewer reads a PR in two ways — the whole diff at once, or commit by commit. For the latter to be possible, the commits have to be logical units.

Bad commit historyGood commit history
wipauth: add the SessionToken type
fixauth: implement the token expiry check
fix againauth: wire up auto-refresh near expiry
oopstest: add tests for session refresh scenarios
address review(review responses: see 4.3)

In a good history the reviewer can walk commit by commit and understand "why this change happened in this order." Ideally each commit, on its own, passes the build and tests.

4.2 Formatting changes go separate

Running an auto-formatter (Prettier, gofmt, black, etc.) fills the diff with meaningless lines. Those lines bury the real change.

The principle: mechanical changes like formatting and renaming go in a separate commit, and ideally a separate PR. A single "whole-file formatting" commit lets the reviewer skim it in one second with git diff -w (ignore whitespace). But mix it into the same commit as a behavior change, and the reviewer has to inspect line by line.

4.3 Cleaning up history before review

Before opening the PR, clean up the wip / fix / oops commits that piled up during work.

# On the work branch: restructure the last 5 commits into logical units
git rebase -i HEAD~5

# Or squash everything and re-split it logically
git reset --soft main
git add -p   # pick changes into meaningful units

Caution: do not casually rewrite history on a PR where review has already started (see the rest of 4.3 and Chapter 7). As a rule, finish the cleanup before the first review request.

4.4 Don't mix in one-line unrelated changes

When the diff contains a line where only import order changed, or a line where unused whitespace was removed, the reviewer has to stop every time and judge "is this intentional?" Filter out editor-generated changes with git add -p before committing.


Chapter 5 · Stacked PRs / stacked diffs

Sometimes you want to cut work small but the changes depend on each other — PR 2 only works on top of PR 1's code. This is where stacked PRs are the answer.

5.1 What stacked PRs are

Stacked PRs are PRs stacked in a line. Each PR is based not on main but on the branch of the PR directly below it.

main
 └─ PR 1: add auth types          (base: main)
     └─ PR 2: token validation     (base: PR 1)
         └─ PR 3: wire auto-refresh UI  (base: PR 2)

The reviewer goes through PR 1 onward in order. Each PR is small, carries one idea, and the diff shows only the difference from the PR directly below. A single 800-line PR becomes four 200-line PRs.

5.2 When to use a stack

SituationStack?
A big feature can be split into dependent stepsYes — the classic use of a stack
Refactor → behavior change on top of itYes — PR 1 refactor, PR 2 behavior
The changes are independent of each otherNo — just open separate PRs in parallel
The steps go beyond fiveCaution — a too-deep stack has high management cost

Use a stack only when there's a dependency. If you stack independent changes for no reason, then when a lower PR is blocked, the upper PR is blocked with it.

5.3 The reality of operating a stack

The biggest cost of a stack is that when a base PR changes, you have to rebase every PR above it.

# PR 1 changed from review feedback → rebase PR 2 back onto PR 1
git checkout pr-2-branch
git rebase pr-1-branch

# Updating the whole stack is better done with tool help
# (Graphite, Sapling, spr, and other stack-specific tools)

Manage a stack by hand and the rebases quickly become hell. For teams that use stacks often, adopting a stack-specific tool (Graphite, Sapling, spr, ghstack, etc.) is nearly essential. The tool handles rebasing and re-submitting the whole stack in one shot.

5.4 The fallback when you can't use a stack

If you have no stack tooling or the team isn't comfortable with stacks, the fallback is splitting the steps into logical commits within a single PR (see 4.1). Tell the reviewer in the description, "please review commit by commit." It isn't as clean as a stack, but it's far better than one giant monolithic diff.


Chapter 6 · Self-review before requesting review

There is one thing to do before pressing the request-review button: look at your own PR through the reviewer's eyes.

6.1 Why self-review works

While working, you're trapped in the viewpoint of the person "writing" the code. Push the PR and look again on the diff screen, and the viewpoint shifts. Things invisible in the editor become visible in the diff view — a left-behind console.log, commented-out code, a debug hardcode, an undeleted TODO, a missing error handler.

You should not expect the reviewer to catch these. That makes the reviewer spend their attention on trivia and strips away the bandwidth to look at the design.

6.2 Self-review checklist

Before requesting review, open the "Files changed" tab on GitHub/GitLab and look line by line.

  • No debug traces (console.log, print, commented-out code, hardcoded values)
  • No unrelated changes mixed in (formatting, import order, accidental whitespace)
  • Every new branch has a test
  • Errors and edge cases are handled (empty values, null, failure paths)
  • The "Verification" field in the PR description is something I can actually follow
  • The diff is below the cliff — if not, can I cut it?
  • The commit history is logical
  • The first line the reviewer hits is the most important line

The last item is subtle. You can adjust file order or commit order so that the most important change is the most visible within the diff.

6.3 Self-review saves time

Spend five minutes on self-review and you cut one review round trip. One review round trip is usually half a day to a full day (including the wait until the reviewer frees up again). Five minutes versus half a day. There are few investments with a higher return.


Chapter 7 · Responding well to review feedback

Once you open a PR, comments arrive. Here the author's attitude decides even the review speed of their next PR.

7.1 Respond to every comment

A review comment should end in one of two ways — you applied it, or you explained why you didn't. A comment left with no response signals to the reviewer that "my opinion was ignored."

Comment typeA good response
A clear bug pointed outFix it and say "fixed (commit abc123)"
A better approach suggestedApply it if you agree; if not, "I chose this way because X. What do you think?"
A matter of tasteIf trivial, just apply it (cost of arguing > cost of applying), or cite the team convention
A questionAnswer with a comment, not just code, and add a code comment if needed
An out-of-scope suggestion"Good catch. It's out of scope for this PR, so I filed it as #456"

7.2 Don't defend — understand

Take a review comment as an attack and your response turns defensive. A defensive response wears the reviewer down and makes them not want to look closely at that author's PR next time.

Even when a comment seems wrong, the first reaction is "why did the reviewer see it this way?" If the reviewer misunderstood, the misunderstanding itself is information — it means the code is unclear enough to be misread that way. Fix the code to be clear, or add a comment.

7.3 How to stack review-response commits

On a PR where review has already started, applying changes as new commits is the default. Adding a commit like address review feedback lets the reviewer look separately at "what changed since the first review." They don't have to re-read parts they've already seen.

Right before merge, you can squash these commits per the team convention. The key is to preserve history while review is in progress and clean up at merge time. Rewrite history with a force-push mid-review and the reviewer loses the context of their own comments.


Chapter 8 · Draft PRs and signaling readiness

A PR has two states — "don't look yet" and "please look now." Fail to signal these clearly and the reviewer's time is wasted.

8.1 What draft PRs are for

A Draft PR is an explicit signal that "the code is up, but this is not a review request yet." Use it when:

  • You want to run CI early — just check the build and test results first
  • You want early feedback on direction — explicitly say "don't review the whole thing, just whether this approach is right"
  • You want to show the team work is in progress — to prevent duplicate work

The core of a draft is managing the reviewer's expectations. Write "just review the direction" on a draft, and the reviewer won't point out variable names or missing tests — that's not the stage to look at yet.

8.2 Signal "ready" clearly

Converting a draft to "Ready for review" is itself a signal. But sometimes that alone isn't enough.

  • If you applied early feedback from the draft, leave a comment on what you changed.
  • When requesting review, be clear about who you're requesting from — "anyone" usually becomes "no one."
  • If the PR is blocking another PR or a deployment, write down that urgency.

8.3 Don't leave drafts to rot

An old draft PR is noise. A draft stuck for days makes the team unsure "is this work alive or dead?" Keep a draft open only while it's active work; if it's stopped, close it or leave a status comment.


Chapter 9 · Authoring PRs in the AI era

In 2026 the landscape of PR authoring has changed. A large share of the code is generated by AI agents, and AI can draft the PR description too. But the core principles haven't changed — if anything, they matter more.

9.1 What AI is good at: drafting descriptions and self-review

AI is excellent at specific parts of PR authoring.

  • Drafting the PR description — give it the diff as input and it summarizes "what and how." But "why" is known only to a human — the author must rewrite the "why" field of an AI draft.
  • Assisting self-review — "find the left-behind debug code, missing error handling, and untested branches in this diff" is a task AI does well. You can run the Chapter 6 checklist through AI as a first pass.
  • Cleaning up commit messages — it can draft messages when restructuring into logical commits.

9.2 What AI can't do: accountability and context

There are things AI can never substitute for.

  • The real context of "why" — what business or technical decision this change came out of lives only in a human's head.
  • Accountability for unreviewed output — code put up in a PR, whether AI wrote it or a human did, is code the author vouches for.

9.3 Never do this: dumping unreviewed agent output

The most harmful anti-pattern of the AI era is this — putting up agent-generated code as a PR without the author even reading it.

This is equivalent to telling the reviewer, "please look at code I haven't looked at." The social contract of review breaks. The reviewer assumes they're reviewing "code the author has examined once," and when that assumption becomes false, the reviewer ends up shouldering the first-pass review the author should have done.

The rule: AI-generated code has to pass the exact same PR bar as human-written code. Cut it small, carry one idea, run a self-review as the author, and have the author understand and vouch for every line. AI only makes the diff faster — the responsibility to make it reviewable still belongs to the author.

9.4 Why small PRs matter more in the AI era

AI can produce code fast and in large volume. This makes the small-PR principle matter more — the faster generation gets, the faster the reviewer drowns without the discipline to cut it into reviewable units.

AI raised authoring speed; it did not raise review bandwidth. Closing that gap is the author's PR-authoring craft.


Epilogue — A good PR is a gift to the reviewer

A PR's review latency is controlled by the author. The reviewer isn't slow — the unreviewable PR is slow. Cut it small, carry one idea, write the "why" and "verification" in the description, structure the diff to be read, and run a self-review before pushing — and the same reviewer approves the same code several times faster.

This is a favor for the reviewer and, in the end, a thing you do for yourself. Fast review is fast merge, fast merge is fast feedback, and fast feedback catches the wrong direction early. The craft of writing a good PR is a collaboration skill and, at the same time, a skill for keeping your own work cycle short.

The reviewable-PR checklist

  1. Size — the diff is below the cognitive cliff (roughly under 200–400 lines)
  2. One idea — you did not mix a refactor with a behavior change
  3. What — the PR description summarizes the change in one or two sentences
  4. Why — the reason this change is needed is written (if an AI draft, a human rewrote it)
  5. Verification — there are check steps the reviewer can follow
  6. Out of scope — what is not being done this time is stated
  7. Visuals — if it's a UI change, screenshots are attached
  8. Commit hygiene — commits are logical units, and formatting is separated
  9. Self-review — you looked at "Files changed" line by line and there are no debug traces
  10. Signal — the draft/ready state and the review target are clear

Anti-patterns to avoid

  • The giant PR — 800 lines with a one-line "add feature." An LGTM is a surrender, not a review
  • The mixed PR — refactor + behavior change + typo in one diff. The signal drowns in noise
  • "While I'm at it" — don't slip in unrelated changes. Make them a separate PR/ticket
  • The empty description — don't toss the diff with no "why" and no "verification"
  • Formatting contamination — don't mix auto-formatter output into the same commit as a behavior change
  • Defensive responses — don't take a review comment as an attack. A misunderstanding is information that the code is unclear
  • Force-push mid-review — don't rewrite history while review is in progress
  • Agent output dump — don't put up AI code you haven't read as a PR

Next post teaser

The next post is "The Reviewer's Craft — How to Do Fast, Kind, and Strict Code Review." If this post was the craft of the side that writes a PR, the next is the craft of the side that reads one. What to look at first, which comments help and which are noise, where to set the baseline for approval versus change request — and how to divide the roles of an AI review bot and a human reviewer.

The ten minutes spent writing a good PR prevents the half-day of a single review round trip. Those ten minutes are a gift to the reviewer and an investment sent to your future self.

현재 단락 (1/196)

"Why does my PR always take so long to merge?" Many engineers look for the answer in the reviewer: t...

작성 글자: 0원문 글자: 20,894작성 단락: 0/196