Logo
code reviewengineering leadershipworkflowpull requests

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.'

3 min read
Small PRs. Small commits. Merged often.

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.

ChatGPT Image Aug 27, 2026, 05_21_45 AM

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.

ChatGPT Image Aug 27, 2026, 05_21_52 AM

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