Why small pull requests let AI auto-merge safely (and cut human review)
Most teams treat pull request size as a matter of taste. Some people like big PRs because they tell a complete story; others like small ones because they are less annoying to review. It gets filed under style, argued about once, and then everyone goes back to shipping whatever shape of change happened to fall out of their branch.
I have come to think that is the wrong frame. The size and shape of a PR is not a style preference. It is one of the cheapest levers you have on the risk of a deployment — and the more of your pipeline you automate, the more that lever matters.
The link nobody draws out loud
Here is the chain of reasoning, made explicit.
A large, multi-concern PR is one a reviewer cannot hold in their head all at once. So they don’t. They skim. They approve the parts they understand and wave through the parts they don’t, because the alternative is spending an afternoon on a single review and blocking a colleague. The bugs that slip through code review slip through there, in the diff that was too big to read honestly.
Now layer continuous deployment on top. Once merges flow to production automatically — or through a merge queue with no human gate at the end — the review is the safety net. A big PR is therefore not just harder to review; it is a larger, less-understood change being pushed toward production with a weaker check in front of it. That is the risk, stated plainly.
Splitting the same work into small, independently reviewable PRs attacks the risk from both sides. Each PR is small enough to actually read, so the human check gets stronger. And each PR is a smaller unit of change, so if one does go wrong, the blast radius and the thing you have to reason about are both smaller. Lower change size, higher review quality. You rarely get to improve two variables with one habit.
So when do you split?
“Keep PRs small” is useless as advice because it has no edge. Everyone agrees and nobody changes behavior. What you need is a threshold you can apply to a diff before you have written it.
Before writing code, estimate the change along four axes:
- Reviewable changed lines — additions plus deletions.
- Reviewable files touched.
- Independent conceptual changes — a refactor and a feature is two, not one.
- Review effort — could one person hold the whole thing in their head at once?
The word doing the work there is reviewable. Not every line in a diff is something a human must judge. Generated code, lockfiles, generated snapshots, vendored files, whole-file deletions, a rename applied uniformly across two hundred files — none of that needs careful reading. A 900-line diff that is 850 lines of regenerated client code is a small review. Say so out loud rather than splitting it for a number’s sake.
With that filter applied, I use these as targets — not tripwires, but crossing the hard limit means you split unless you can justify why not:
| Signal | Target | Split when |
|---|---|---|
| Reviewable lines | ≤ 250 | > 400 |
| Reviewable files | — | > 10 |
| Conceptual changes | 1 | > 1 |
| Fits one review session | yes | no |
The one people underuse is the third row. More than one conceptual change is enough reason to split even when the diff is small. A tiny refactor bundled with a tiny feature is still two things, and it still forces the reviewer to context-switch mid-review. That context-switch is where attention leaks.
If it must be split, plan the series before you code
The mistake is to start coding a giant change and then try to carve it into PRs afterward. By then the pieces are entangled and every “split” is really a big PR with a smaller commit inside it — and a reviewer approves a PR, not a commit.
Instead, propose the decomposition first and get sign-off. For each PR in the series, write down its purpose and acceptance criteria, its rough size, what it depends on, its own test scope, and which PR finally closes the issue.
Order by dependency. When it applies, this natural sequence reviews cleanly layer by layer:
- Preparatory refactor — no behavior change.
- Schema / API foundation — the migration, the API contract, the generated types.
- Business logic — services, use cases.
- UI — the part that consumes the logic.
Each of those is a clean unit a reviewer can reason about without the others in front of them.
The rule that makes it safe: every PR stays green
Splitting is only a gain if each intermediate PR is genuinely shippable. A broken PR in the middle of a stack poisons everything after it — it can’t be merged cleanly and it can’t be reverted cleanly.
So the non-negotiables:
- Each PR builds, tests, and is safe to merge on its own.
- No PR knowingly breaks the build, leaves a migration half-applied, or exposes half-finished behavior to users. Keep incomplete work dark with backward-compatible changes or a feature flag.
- The tests that prove a PR’s behavior live in that same PR. Never defer them to “a later one in the series.”
- When a later PR genuinely depends on an earlier one, use stacked branches — branch the next off the previous branch, not off
main.
And a small linking convention that keeps the stack navigable: only the final PR says Closes #<issue>; every earlier one says Part of #<issue> and links to its neighbours so a reviewer can walk the series.
When a big PR is actually fine
This is a discipline, not a religion. A single large PR is allowed when the change is genuinely indivisible — splitting it would create a broken intermediate state no flag can hide — or when it is overwhelmingly excluded content, the regenerated-client case from earlier. In those cases the move is not to split anyway; it is to state plainly why it can’t be split and get explicit approval before you write it. What you don’t get to do is let “it’s all one thing” quietly become the default excuse for every oversized diff.
Make it something that gets applied every time
A rule you have to remember is a rule you will skip under pressure. So I did the obvious thing and turned all of the above into a small, explicit skill: a checklist an AI coding agent runs before it opens a PR.
It is nothing exotic. The skill encodes the same four axes, the same thresholds, the same “estimate the reviewable size first, then decide” order, the layered sequence of refactor, schema, logic and UI, and the every-PR-stays-green rule. Given a change to make, the agent estimates the reviewable size, tells me plainly when a diff is mostly generated and does not need splitting, and — when a split is warranted — proposes the ordered series of PRs for sign-off before writing any code.
This matters more, not less, in an AI-assisted workflow. An agent can produce a large, multi-concern diff in minutes, and the volume of pull requests is climbing fast as agents write more of the code. Gergely Orosz has documented how teams are rethinking code review to keep up, and “smaller PRs” shows up again and again as one of the levers they reach for. If the discipline lives only in someone’s head, it loses to that speed every time. Encoded as a step the agent has to run, “keep PRs small” stops depending on anyone remembering it on a busy afternoon.
Here is the skill in full:
---
name: pr-decomposition
description: Use when deciding how to package upcoming or in-progress code changes into pull requests — the shape and scope of PRs, not their content. This covers: judging whether a change or diff is too large to review well and whether it needs splitting; planning an ordered set of small, stacked, independently reviewable PRs before or during coding; deciding whether distinct pieces (a migration and its business logic, a refactor and a new feature, backend and UI) belong in one PR or should be separated; and keeping incomplete work safely shippable in intermediate PRs, including whether to hide it behind a feature flag. Trigger whenever someone asks how to break a change up, whether a PR is too big, how to sequence stacked PRs, or what should go together versus apart — and proactively when a plan is growing large or blending concerns. Do NOT use for writing PR descriptions, opening or merging PRs, reviewing code for bugs, or running tests.
---
# PR Decomposition
Keep every PR to **one cohesive, independently understandable change** that a human can review in a single focused sitting. Oversized, multi-concern PRs are where review quality collapses and bugs slip through — the reviewer skims instead of reading. This skill decides *whether* to split and, if so, *how*.
The goal is not bureaucracy. It is to make each PR a small, honest unit that builds, tests, and merges on its own.
## 1. Estimate the reviewable size
Before writing code, estimate the change along four axes:
- **Reviewable changed lines** (`additions + deletions`).
- **Reviewable files** touched.
- **Independent conceptual changes** (a refactor *and* a feature = two).
- **Review effort** — could one person hold the whole thing in their head at once?
**"Reviewable"** = anything a human must actually judge: handwritten production code, tests, config, migrations, docs. **Exclude** (and say so explicitly when you do): generated code (`*/generated/*`, OpenAPI/ORM output), lockfiles, generated snapshots, vendored files, whole-file deletions, and purely mechanical transforms (a rename applied uniformly). A 900-line diff that is 850 lines of regenerated client code is a *small* review — call that out rather than splitting needlessly.
## 2. Apply the thresholds
Targets, not tripwires — but crossing the hard limits means split unless you can justify why not:
| Signal | Target | Split when |
|---|---|---|
| Reviewable lines | ≤ 250 | > 400 |
| Reviewable files | — | > 10 |
| Conceptual changes | 1 | > 1 |
| Fits one review session | yes | no |
More than one conceptual change is on its own enough reason to split, even under the line limit — a small refactor bundled with a small feature still forces the reviewer to context-switch.
## 3. When it must be split, propose the series first
Do **not** start coding. Share an ordered decomposition and get sign-off. For each PR give:
- purpose + acceptance criteria;
- estimated reviewable lines and files;
- dependencies on earlier PRs;
- its own test and QA scope;
- which PR closes the issue.
Order by dependency, and prefer this natural sequence when it applies — each layer reviews cleanly on its own:
1. **Preparatory refactor** (no behavior change).
2. **Schema / API foundation** (migration, OpenAPI, generated types).
3. **Business logic** (services, use cases).
4. **UI** consuming the logic.
Split these apart whenever they can be reviewed independently. Don't substitute "multiple commits in one big PR" for real splitting — a reviewer approves a PR, not a commit.
## 4. Every PR in the series stays green
Non-negotiable, because a broken intermediate PR poisons the stack and can't be merged or reverted cleanly:
- Each PR **builds, tests, and is safe to merge on its own**.
- No PR knowingly breaks the build, leaves a migration half-applied, or exposes half-finished user-facing behavior. Use backward-compatible changes or a feature flag to keep incomplete work dark.
- **Tests proving a PR's behavior live in that same PR** — never defer them to a later PR in the series.
- Use **stacked branches** when a later PR genuinely depends on an earlier one (branch the next off the previous branch, not off `main`).
## 5. Linking convention
- Only the **final** PR uses `Closes #<issue>`.
- Every earlier PR uses `Part of #<issue>` and links to the preceding and following PRs so the reviewer can navigate the stack.
## When a large PR is actually fine
A single large PR is allowed only when the change is **genuinely indivisible** (splitting would create a broken intermediate state no flag can hide) or is **overwhelmingly excluded content** (generated/mechanical). In that case, state plainly *why* it can't be split and get explicit approval before coding — don't let "it's all one thing" become the default excuse.
The underlying idea is simple enough to hold in one sentence: a pull request is the unit your reviewer approves and your pipeline ships, so make it small enough to be understood and safe enough to stand alone. Do that, and “keep PRs small” stops being etiquette and becomes one of the quieter, more reliable ways to keep bad changes out of production.