Small PRs. Small commits. Merged often.
A 2,000-line PR isn't a productivity flex — it's a bottleneck. Here's the case for small PRs, plus the tactical playbook I use to split work that feels 'too coupled to break up.'

The engineering culture I want is the one where a PR opens on Monday and merges by Tuesday, not one where a PR opens on Monday and closes as stale three weeks later. Small PRs make that possible. Large PRs make it impossible.
I've never once met a team that shipped faster after adopting large PRs. But I've watched several teams unlock a step-change in velocity by committing to small ones.
Why big PRs actually cost more
The intuition is that batching work is efficient — one review, one merge, less overhead. It isn't. Big PRs incur costs the author never sees:
Review time is superlinear. A 200-line PR reviews in 20 minutes. A 2,000-line PR doesn't review in 200 minutes — it reviews in a week, because no one has three uninterrupted hours to give it.
Rebase pain compounds. The bigger the diff, the more likely main has moved under you. The bigger the rebase, the higher the chance you introduce a subtle bug you can't attribute to any single commit.
Rollback becomes political. Reverting a 20-file PR removes 20 changes, some of which were fine. Now you're choosing between "keep the bug" and "lose the good changes." Neither is a good option.
Review turns theatrical. Nobody reads a 2,000-line PR end-to-end. They spot-check, LGTM, and hope. The purpose of review — catching bugs — quietly gets dropped.

What small actually means
My rule of thumb: a PR should be reviewable in under 30 minutes by someone unfamiliar with the change. Roughly, that's 200–400 lines of real code, tests included.
Everything larger is a red flag that needs a reason. Sometimes the reason is legitimate — a schema migration touches a lot of files but is atomic. Fine. But most of the time, "it's coupled" is a lack of imagination.

How I split work that "can't be split"
Four patterns cover almost every case:
1. Ship the plumbing first
Before the feature, land the boring stuff: new types, empty modules, unused helper. Each PR is small, adds no user-visible behaviour, and passes tests trivially. When the feature PR arrives, it only contains the feature.
2. Feature-flag the entrypoint
Merge the code behind a flag that defaults to off. Now it can land in ten small PRs over a week without any user seeing a thing. When you're ready, one final PR flips the flag.
3. Refactor separately from behaviour change
The classic mistake is "clean up this function while I add a feature to it." Now the reviewer can't tell which lines are the refactor and which are the new behaviour. Split into two PRs. The refactor is boring and merges fast; the feature is small and easy to review.
4. Convert a rewrite into a strangler
Instead of one PR that swaps the old system for the new one, ship one PR that adds the new system alongside, then per-consumer PRs that switch each caller, then one final PR that deletes the old code. Each step is safe. The rewrite ships as ten trivial PRs instead of one terrifying one.
Commit hygiene matters too
Inside the PR, I want commits I can read as a story. One commit per idea. Meaningful messages. Not "wip", not "fix", not "address review." If you can't describe what a commit does in a sentence, it's the wrong shape.
The reward: bisect works, revert works, blame is meaningful, and the reviewer can walk through the change one thought at a time instead of parsing 2,000 lines in one go.
The short version
Small PRs merge fast. Fast merges mean fewer conflicts. Fewer conflicts mean smaller PRs. It's a virtuous loop — and the only way in is to hold the line on the first small PR.
More Articles
Continue reading with these related posts



