Skip to content

Commit 8f1f4a9

Browse files
feat: add blog post about splitting out unrelated changes (#334)
* feat: add blog post about splitting out unrelated changes * remove *.ts changes * Images and some tweaks * some tweaks * more on conventional titles * remove 'also' * trim example pr titles * no more PRs * moderation, not balance
1 parent b78451f commit 8f1f4a9

File tree

4 files changed

+143
-0
lines changed

4 files changed

+143
-0
lines changed
49.6 KB
Binary file not shown.
11.4 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
---
2+
description: "This is a general explainer for pull request reviews where I think unrelated changes should be split into a separate PR."
3+
image:
4+
alt: Famous portrait of Samuel Johnson squinting up close at a newspaper
5+
src: "~/assets/blog/samuel-johnson-reading.webp"
6+
pubDate: 2025-01-13
7+
tags: [
8+
"pull requests",
9+
"reviews",
10+
]
11+
title: "Split Out Unrelated Changes"
12+
---
13+
14+
import samuelJohnsonDuo from "~/assets/blog/samuel-johnson-duo.webp";
15+
import LabeledImage from "~/components/blog/mdx/LabeledImage.astro";
16+
17+
Hi!
18+
You might have been linked this blog post in a pull request that has a lot of changes in it.
19+
There might be a request linking to this blog post, asking that you split out some or all of those unrelated changes from it.
20+
21+
This blog post is an explainer for why to split out unrelated changes from reviews.
22+
23+
<LabeledImage
24+
alt="Two famous portraits of Samuel Johnson, side-by-side. The left one is him squinting up close at a newspaper. The right one is him looking at the viewer, confused."
25+
description="Famous writer Samuel Johnson reading a 100-file pull request tackling a half dozen issues at once."
26+
original={{
27+
href: "https://knowyourmeme.com/memes/samuel-johnson-reading",
28+
text: "original",
29+
}}
30+
src={samuelJohnsonDuo}
31+
/>
32+
33+
## Split Out Unrelated Changes
34+
35+
For the most part, the larger a pull request, the longer it will take to review and get merged.
36+
Larger pull requests generally come with more cost and more risk than smaller PRs.
37+
They also clutter the Git history for files, making it harder to search back over time to see why logic changed when.
38+
39+
> 👉 See [Why Open Source Pull Requests Can Take A While](https://www.joshuakgoldberg.com/blog/open-source-pull-request-timing) for why open source pull requests in particular can take a long time.
40+
41+
Anything we can do to reduce that cost is helpful.
42+
My favorite strategies in particular are:
43+
44+
- Instead of one pull request that resolves multiple issues at once, it can often be better to send one pull request for each one of those issues.
45+
- Separating changes that impact code logic out from changes that only impact layout, formatting, or style.
46+
47+
## One Pull Request Per Task
48+
49+
It's generally easier to review a pull request that resolves one task (e.g. GitHub issue) rather than a pull request that resolves multiple tasks.
50+
Each area of change you add to a pull request increases the size and complexity of the pull request, making longer and more convoluted to review.
51+
It becomes harder to keep track of which changes apply to which task as a pull request grows in size.
52+
53+
As a general ideology, I try to make each of my pull requests address a single GitHub issue.
54+
There should be one granular `fixes #<number>` in the pull request description per GitHub's docs on [linking a pull request to an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue).
55+
56+
### Working with Automation
57+
58+
Furthermore, many repositories -including most of mine- automatically generate changelogs and new releases.
59+
Granular pull requests can become individual changelog entries and releases.
60+
Catch-all pull requests don't fit well into those systems.
61+
62+
As a general rule, I try to make sure all my pull requests can be described in a single [conventional commits message](https://www.conventionalcommits.org/en/v1.0.0).
63+
If you can't describe your pull request with a title like a `feat: add abc option` or `fix: avoid crash when xyz` then it might be a sign of an overloaded set of changes.
64+
65+
## Separate Logical and Stylistic Changes
66+
67+
Stylistic changes generally don't require very much review time per line of code changed.
68+
Teams sometimes take a long time to discuss their stylistic preferences, but once those discussions are resolved, changing code to adhere to new styles is often straightforward.
69+
70+
Logical changes to code, on the other hand, generally take much longer to review.
71+
Which is good: logical changes come with a much higher risk of bugs.
72+
Every changed line of code in a pull request that changes logic should be scrutinized to make sure it doesn't break something.
73+
74+
Introducing stylistic changes in a logical pull request makes that those stylistically changed lines appear to need scrutiny.
75+
Reviewers will have to remember that those lines aren't logically changed in reviews.
76+
That's a tax that slows down review of the pull request.
77+
78+
Consider trying to understand this large set of changes to a `repeatMessage` function:
79+
80+
```diff
81+
-export function repeatMessage(
82+
- times: number,
83+
- ...messages: string[]
84+
-) {
85+
- for (let i = 0; i < times; i += 1)
86+
- for (const message of messages)
87+
+export function repeatMessage(times: number, messages: string[]) {
88+
+ for (let i = 0; i < times; i += 1) {
89+
+ for (const message of messages) {
90+
console.log(message);
91+
+ }
92+
+ }
93+
}
94+
```
95+
96+
Now consider this version of the same change but with only logical differences:
97+
98+
```diff
99+
-export function repeatMessage(times: number, ...messages: string[]) {
100+
+export function repeatMessage(times: number, messages: string[]) {
101+
for (let i = 0; i < times; i += 1) {
102+
for (const message of messages) {
103+
console.log(message);
104+
```
105+
106+
Much easier and faster to read through, right?
107+
All that was being changed was removing the `...` -- but the first diff obfuscated that straightforward intent with misleading stylistic changes.
108+
109+
### Automate Stylistic Preferences (If Possible)
110+
111+
Co-opting logical pull requests to enforce stylistic preferences is not an efficient way to roll out those preferences.
112+
Stylistic preferences lend themselves well to automation.
113+
Trying to touch them up one-by-one is sisyphean: a never-ending expenditure of effort that yields little to no real results.
114+
115+
Formatters and linters are pretty good at **automating** most stylistic conventions.
116+
If you want to enforce a stylistic preference, do so with as much automation as possible.
117+
118+
Most of the `repeatMessage` formatting from earlier could be enforced with any common formatter such as [Prettier](https://prettier.io) and linter such as [ESLint](https://eslint.org).
119+
I personally include [typescript-eslint's `stylisticTypeChecked` config](https://typescript-eslint.io/users/configs#stylistic-type-checked) along with Prettier plugins such as [prettier-plugin-curly](https://github.com/JoshuaKGoldberg/prettier-plugin-curly) to enforce a large set of consistency preferences in my projects.
120+
121+
## Everything in Moderation
122+
123+
These are't absolute rules.
124+
Sometimes the convenience of including a small unrelated change in a pull request is worth more than the benefits of splitting up changes.
125+
126+
Filing an issue and reviewing a pull request both take time.
127+
If that time is greater than the cost of adding a small unrelated change to a pull request, then it may be worth it to just stick with the one pull request.
128+
129+
If I'm already refactoring an area of code, and touching up some stylistic preferences wouldn't bloat the pull request too much, I might throw them in.
130+
Especially if those changes aren't something that need to be applied elsewhere in the repository.
131+
132+
When including an unrelated change in a pull request, I recommend explicitly adding a comment explaining why it's there.
133+
That might convince a reviewer that it is indeed worth it to keep the changes together.
134+
At the very least it will explain your thought process and make the choice seem intentional rather than mistaken.
135+
136+
## Further Reading
137+
138+
If you want to read more about why granular pull requests are often preferable, I recommend:
139+
140+
- [Google Testing Blog: In Praise of Small Pull Requests](https://testing.googleblog.com/2024/07/in-praise-of-small-pull-requests.html)
141+
- [Are small pull requests better than one large all-encompassing features, and how can I convince my team of that?](https://softwareengineering.stackexchange.com/questions/425665/are-small-pull-requests-better-than-one-large-all-encompasing-features-and-how)

src/content/blog/try-catch-as-little-as-possible/index.mdx

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ pubDate: 2024-11-27
77
tags: [
88
"errors",
99
"exceptions",
10+
"pull requests",
11+
"reviews",
1012
"try catch",
1113
]
1214
title: "Try...Catch As Little As Possible"

0 commit comments

Comments
 (0)