The Pull Request Description Nobody Writes (But Everyone Needs to Read)
Most pull request descriptions say some version of “fixes the bug.” The diff is four hundred lines across eleven files. Your reviewer opens it, has no idea what problem you were solving or which approach you rejected, and either rubber-stamps it or leaves nine comments about variable names. Both outcomes are your fault, and both are fixable with about six minutes of writing.
What the Description Is Actually For
A PR description has three audiences, and only one of them is the reviewer sitting next to you today.
- The reviewer, who needs enough context to evaluate the approach, not just the syntax
- The archaeologist, who is you in fourteen months running
git blameon a line that’s suddenly load-bearing - The incident responder, who is scanning deploy history at 2am trying to work out what changed
The reviewer can ask you questions. The other two can’t. Write for them and the reviewer gets a better document as a side effect.
The Structure That Works
Five sections. None of them long.
1. Why
The problem, in plain language, before any mention of code. What was broken, missing, or slow? Who noticed, and how?
Bulk invoice generation times out for accounts with more than ~2,000 line items. Reported by support on 12 May; three enterprise accounts affected. The controller loads every line item into memory to compute a total.
That’s the most valuable paragraph in the whole PR and it takes ninety seconds to write.
2. What
The approach, at a level above the diff. Not a file-by-file narration — the reviewer can read the diff. Tell them the shape of the change.
Moved total computation into a single SQL aggregate and switched the line item iteration to
find_each. Extracted the calculation intoInvoiceTotalizerso it’s testable without building an invoice.
3. What I considered and rejected
The section almost nobody writes, and the one that most improves review quality. Reviewers spend a lot of energy suggesting approaches you already thought about and discarded for good reasons.
Considered a
counter_cacheonline_items_total. Rejected because totals depend on discount rules that change independently of line items, so the cache would need invalidating from four places.
4. How to verify
Concrete steps or the specific test. If the reviewer has to invent a reproduction, they usually won’t bother.
bundle exec rspec spec/services/invoice_totalizer_spec.rbManually: seed an account with 5,000 line items viarake dev:seed_large_invoice, hit/invoices/:id/generate, confirm response under 2s.
5. Risk and rollout
What could go wrong, and what happens if it does. Migrations, backfills, feature flags, anything order-dependent.
Includes a backfill migration over
invoices(~400k rows), batched at 1,000. Safe to run online. No flag; behaviour change is a strict improvement. Rollback is a straight revert — no schema change.
What to Leave Out
Length is not the goal. Signal is. Cut these:
- A file-by-file walkthrough. GitHub already shows the file list. If a file genuinely needs explanation, leave an inline comment on the diff instead — it’s anchored to the code and travels with it.
- Restating the ticket. Link it. Summarise the problem in one line for people who won’t click.
- Apologies and hedging. “Sorry this is so big, I know it’s messy” tells the reviewer to lower their standards. If it’s too big, split it.
- Implementation detail that the code states clearly. “Added a method called
calculate_totalthat calculates the total” is noise.
Make the Diff Reviewable, Not Just the Description
The best description in the world can’t rescue a PR that mixes a rename, a refactor, and a behaviour change in one commit.
Separate the mechanical from the meaningful. A rename touching sixty files and a two-line logic change should be two PRs, or at minimum two commits with the rename first. Reviewers can skim a pure rename in seconds and then give the actual change their full attention.
Order your commits so they tell the story. git rebase -i before opening the PR is not cheating — it’s editing. Nobody needs to see fix typo, wip, actually fix it.
Aim for under 400 lines of real change. Review quality drops sharply past that, and the research on this has been consistent for decades. If you’re above it, ask what could ship separately.
Templates Help, But Only If They’re Short
A repo template makes the structure automatic. Keep it small enough that people fill it in rather than deleting it.
Example:
<!-- .github/pull_request_template.md -->
## Why
## What
## Considered & rejected
## How to verify
## Risk / rollout
Five headings, no checkboxes, no mandatory legal preamble. Every additional required field increases the chance the whole thing gets wiped and replaced with “see title.”
Generating the First Draft
You don’t have to write it from a blank page. The diff already contains most of the raw material.
Example:
git log --oneline main..HEAD
git diff --stat main..HEAD
git diff main..HEAD -- '*_spec.rb' --stat
The commit list gives you the “what.” The stat output tells you where the weight is, which is often not where you think. The spec diff tells you whether your verification section can just point at tests.
For teams on the GitHub CLI:
gh pr create --title "Speed up bulk invoice generation" --body-file .github/pr_draft.md
Writing the draft in your editor rather than a browser textarea produces noticeably better prose, for the same reason commit messages written in vim beat ones written with -m.
Pro-Tip: Write the “Why” section before you write the code, not after. Open the PR as a draft with just that paragraph filled in, then implement against it. Two things happen. First, you find out early whether you actually understand the problem — if you can’t state it in three sentences, you’re not ready to solve it. Second, when you finish and the code doesn’t match the stated problem, you catch the scope creep yourself instead of a reviewer catching it for you. The draft PR also gives teammates a place to say “we already have a service for that” before you’ve spent two days on it.
The Description as a Review Prompt
You can steer the review with the description. Reviewers respond to explicit asks.
Looking for feedback on: the retry strategy in
InvoiceTotalizer#with_retries. I went with exponential backoff and a 3-attempt cap; open to a circuit breaker if that’s the house pattern.
Not looking for feedback on: the
rubocopautocorrect noise inapp/services/— separate cleanup, happy to revert it if it’s distracting.
Two lines, and you’ve directed attention at the decision that actually needs a second opinion instead of hoping someone finds it in the diff.
Conclusion
A good PR description costs six minutes and buys you a faster review, better feedback, and a record that explains itself to whoever reads it next year. The five sections — why, what, rejected alternatives, verification, risk — cover nearly every case, and the rejected-alternatives section is the one that separates a description from a summary. Write the “why” before the code, keep the diff under four hundred lines, and tell your reviewer where you want them to look. The habit compounds: a repository full of well-described merges is a repository that can be understood by someone who wasn’t there.
FAQs
Q1: Does this apply to one-line changes?
Scale it down, don’t skip it. A one-line change still needs a “why” — arguably more, since the diff explains nothing on its own. One sentence is fine.
Q2: Should the PR description duplicate the commit message?
For a single-commit PR they’ll overlap heavily, and that’s fine. For a multi-commit PR the description is the summary and the commits are the detail. Squash-merging makes the description the permanent record, which is a good reason to write it well.
Q3: How do I convince a team to adopt this?
Add the template and write yours this way for a month. Descriptions are contagious — people match the standard they see in the repo far more reliably than one announced in a meeting.
Q4: Is it worth writing a description for a PR only I will review?
Yes, for the archaeologist audience. Solo projects benefit most, because there’s nobody else who remembers the context.
Q5: What about auto-generated descriptions from tooling?
They’re decent at the “what” and useless at the “why” and “rejected alternatives,” which are the sections that carry the value. Use them as a starting draft, then write the parts only you know.
Check viewARU - Brand Newsletter!
Newsletter to DEVs by DEVs - boost your Personal Brand & career! 🚀