An Undefeated Pull Request Template
Published
Background
I use the word “undefeated” in jest. Everyone will have their own opinions, of course. This template may be too much or too little for some. And I haven’t worked at every company or with every team. Nonetheless! Nearly everyone I’ve worked with, either formally or informally, seems to love this template (or a close version of it).
It enables focused and efficient code reviews, which leads to code changes being accepted sooner, fewer changes containing bugs, and more frequent delivery of changes to the people using the product (i.e. decreased cycle time, change failure rate, and lead time).
When teammates are onboarded, it empowers them to start contributing earlier and more confidently. It encourages the author to complete a self-review before requesting reviews from others. It’s also self-documenting, which preserves context for weeks, months, and even years to come.
Components of a Good Pull Request Description
The first goal of a pull request description is to make code review as straightforward and frictionless as possible. As teams and products grow, everyone can’t have all the context all the time. It’s also impossible (and not necessary) to explain all the context in every pull request.
We can, however, easily provide just enough context to preemptively answer common code review questions, and include links for the rest if the reviewer seeks a deeper understanding. In this template, there are 7 sections that can help you accomplish this. I’ll explain them in their subsections.
Related Links
The first section is a short list of links that help reviewers conduct the review or get the context of the task(s) the code changes aim to address. This is frequently just two links: a deploy preview and the tracking Asana/Jira/etc task.
The deploy preview is necessary for testing the changes in a staging-like environment. The tracking task (if written well) includes a brief description, acceptance criteria (or the Definition of Done), and links for deeper context (related tasks, higher-level feature epics, design documents, proposals, etc).
You don’t want to include links to tutorials or other resources you may have used to complete the work. Those will go in the How section. This section is short and at the top because the links will be referenced several times over the course of the review.
What
The next level of information a reviewer needs is an outline of the changes made. This section is easiest to write when you’ve made small, frequent commits while working on the changes. You can refer to the commit history to jog your memory or copy-paste the history log and edit it where needed. Another tactic is to go through the diff file by file, writing this list as you go, then reordering or condensing after. This is what I almost always do, and I do a self-review at the same time.
Doesn't that take a long time?
Yes, it can. And there are times when fixes need to get through quickly, or maybe you’re just short on time. This is something to discuss as a team — “How do we handle special circumstances? When are we OK with leaving out certain sections?”
The purpose of a template isn’t to make anyone fill it in 100%, with perfect detail, and correct grammar and punctuation. The purpose of a template is to help you help your reviewers with as little effort as possible.
I created this template and have used it for a little over 5 years. Can I write it out from memory recall? Yes. Do I want to? Hell no. Thinking is exhausting. Gimme all the templates, please!
Don’t go into much detail just yet. We’ll get to that in the next section. Each item in this list should be just a few words. This will help potential reviewers determine if they’re the right person to review the code (maybe another teammate is better suited for it) and estimate how long it will take them to review.
Here are a few examples:
- Added TextInput component
- Fixed calculation of cookies sold per hour
- Removed no longer used file
If you end up with multiple lines that are almost the same, for example:
- Added TextInput component
- Added Select component
- Added Checkbox component
You can condense them a little to improve their readability, like so:
- Added a few components
- TextInput
- Select
- Checkbox
Why
Here’s where we start getting into some of the juicy details. If your team is pretty small and there are only engineers reviewing your code, there might not be much to add here. Maybe you and one other engineer paired up on a feature, already planned exactly who’s working on what, and are only reviewing each others’ pull requests. Maybe all the “why” has already been discussed and decided on in a design, architecture, or proposal doc.
The primary goal of this section is to make sure your reviewers have the context they need to review, but it’s far from the only goal. A few months from now, what sometimes feels like a lifetime after your feature shipped, you or a teammate will find a peculiar line of code. You’ll think, “Huh. I don’t remember it working like this. Has this changed recently? I think so. Wait… why?”
You ask git
to tell you who changed the line of code and when. You find the pull request associated with that change. You take a minute to read the Why section and… that’s pretty much it!
Of all the sections in this template, this is the one I still have the hardest time with. I think there are two main reasons for this. First, it’s easy to forget our reviewers may not have the same context as us when we’ve spent a lot of time with our code.
Second, it’s really hard to guess what information might be helpful weeks or months from now from a documentation standpoint. In reality, this pull request description may never need to be read by another person again.
So, here are a few generative questions to help you determine what to write.
- Why are these changes helpful or necessary?
- Was a new feature requested?
- Are you refactoring in preparation for another feature or task?
- Was there design feedback that needed to be addressed?
- Is this a fast follow-up to a previous PR?
It doesn’t need to be perfect. Try for one or two points, each a sentence or two long. If you’re still stuck after a few minutes, leave a placeholder and chat with your teammates / potential reviewers. Ask if they need any additional context beyond what you’ve already written.
Remember: this template is meant to ease the process of writing a pull request description, not add process just for the sake of adding process.
How
Alrighty! We’ve reached the section that’s typically the last one with a lot of detail. (I must say, you’re doing great!!) What’s also cool? This section isn’t needed every time.
What you write (or don’t write) depends on things like:
- the complexity of the task
- your experience level
- what tools were already at your disposal
- how many approaches you tried
If you followed a tutorial, list it. If you installed a new third-party library, list it. If you leveraged official built-in APIs, list those duders too. The goal is to preemptively answer common high-level questions that come up in code review.
I don’t care how many times I’ve asked it, I almost always feel like a jerk if I plainly say, “Why did you do it this way?” or, “Why didn’t you do it this other way?” In my experience as a neurodivergent engineer, most people read these questions as me interrogating their intellectual ability, not as genuine curiosity and wanting to learn from them. Tone can also be a general challenge of text-based communication.
It’s often just a little bit smoother for everyone involved, where possible, to anticipate these kinds of questions ahead of time and answer them in the description.
Designs
The section only applies if the proposed changes affect the part of a product that end users will directly interact with. In part, it helps document what a design looked like at the time of implementation. This reasoning has become less and less impactful over the years as branching/versioning features become increasingly common in design tools.
This section is primarily useful for letting reviewers know exactly what you were referencing as you made the code changes. It helps ensure everyone is on the same page, looking at the same pixels, and reading the same notes.
Design files are freakin’ hard to keep organized. Especially in the pre-component and pre-variable days (I’m referencing tools like Figma). Sometimes a file has three frames, other times it might have thirty. Shit happens.
By the way, If I catch you being mean to your design teammates because you think design must be sooo much easier than engineering… just don’t do it. It’s rude. All of our jobs require skills that take a long time to build. Act like an adult and build cool shit together. It’s much better that way!
Test Steps
This is my favorite section. Not because I enjoy writing it (I don’t). Because it proves useful 99% of the time. I don’t know what the other 1% of the time is but it felt wrong to confidently say 100% of the time. 🤭
This section preemptively answers the question, “How do I test this?” This widens the audience of potential reviewers, some of whom might not know the area of the codebase you’ve made changes in.
Even if everyone knows the code, you’re saving them time by doing the thinking for them. The code is still fresh in your mind whereas they might need to take a few minutes to recall how the feature works.
A pleasant side effect of this section
People will try to make things as easy as possible to test. No one wants to write a 30-point checklist or even three 10-point checklists. It’s time-consuming.
The two most interesting things (to me) that can happen:
- Smaller pull requests (i.e. less files, code, or functionality changed)
- More test/demo pages (like Ladle or Storybook) that simulate complex data
The other primary goal of this section is to make sure the right code has been changed. This ties back to the very first section: Related Links. If written well, the tracking task’s Acceptance Criteria (or Definition of Done) section can nearly be copy-pasted to fill in this section. This is especially true for bug report tasks which are most helpful for engineers when they have a Steps to Reproduce section.
Here’s my advice on how to write this section, based on my educational training and several years of practice.
It comes down to two main things:
- Each list item should either be an action or an observation
- Each list item should contain one action or observation
Examples of actions:
- Go to the home page
- Scroll to the 3rd page section
- Expand the 2nd accordion item
- Press the `TAB` key 5 times
- Wait for the page to load
Examples of observations:
- You should see a warning icon
- The screen reader should announce "Cancel, button"
- The "Save" button should become disabled
- No animation should happen
- You should be taken to the "Edit Settings" page
You don’t always need to list every single action required to complete a task. It depends on what is being tested. Let’s use logging into a system as an example.
- Is that feature implemented in this pull request? Or are you changing the existing login process? You probably need to list out each step: Enter your username, Enter your Password, Submit the form, etc.
- Are you adding a new feature that only logged-in users can access? Or updating an existing one only accessible to users with certain permissions? You don’t need to list out each action required to log in. It’s probably only one list item like: “Log in” or “Log in as Admin.”
If you think it might be helpful to include the steps of a task, there’s no harm in it! To improve readability you can indent those steps and make them a sublist.
For example:
- Log in as Admin
- Go to the /login page
- Enter the email `admin@testwebsite.com`
- Enter the password `Adm!nP@assw0rd`
- Go to ...
Sublists are also great when your reviewer needs to make multiple observations after performing an action.
For example:
- Click the "Finish Onboarding" button
- Wait for the page to finish loading
- A success Toast should appear at the top right
- The notification should read "Onboarding complete! 🎉"
- There should no longer be an onboarding checklist in the sidebar
- ...
Other Notes
The final section is a bucket for anything else you want to share but doesn’t fit neatly in one of the other sections. You might not even need this section. Here are some examples where it can be beneficial:
- Stuck on one thing but everything else is ready for review? List it here and ask if anyone knows how to proceed.
- Encounter a bug or piece of tech debt you want to address, but it’s outside the scope of changes? Your reviewer will probably encounter it too, so list the details to let them know you’re already aware and/or planning to address it.
- If you know part of your task needs a little more research but you don’t want to hold up all your code changes, just say so. It happens all the time!
The Template
## Related Links
What links will make reviewing these code changes as straightforward as possible?
- Deploy Preview
- Jira/Asana/etc. Ticket
- User Story
## What
What changes did you make at a high level?
- Added...
- Updated...
- Refactored...
- Moved...
## Why
Why are these changes helpful or necessary?
- New feature requested...
- Refactoring in preparation for...
- Addressing design feedback...
- Fast-follow to previous PR...
## How
How did you go about making these changes?
- Paired with <Teammate Name> where we did A, B, C
- Tried another approach but it didn't work because X, Y, Z
- I followed this resource by <Author Name>: <Resource Link>
- Used these code APIs/SDKs: <Name>, <Name>, <Name>
## Designs
What visual context do reviewers need?
- Component
- Mockup
- Wireframe
- Prototype
## Test Steps
What are all the steps to testing your code changes?
- [ ] Enable `feature_flag`
- [ ] Go to this page: /a-test-page
- [ ] Simulate "Browser Feature"
- [ ] Scroll to "Example Section Name"
- [ ] It should show...
- [ ] Activate the "Example Button" button
- [ ] You should see...
- [ ] Wait for the page to load...
- [ ] ...
## Other Notes
What, if anything, hasn't been addressed in these code changes but should be in future changes?
- ABC wasn't working as expected...
- XYZ needs more research...
- A fast-follow PR is already planned for addressing 1, 2, 3...