Split a large AI-generated pull request by reviewable behavior, not by an arbitrary line count. Separate mechanical preparation from product behavior, keep related tests with each behavior change and make every pull request leave the system working. If a reviewer needs later changes to understand whether the current one is safe, the split is incomplete.
Coding agents make oversized pull requests easy to create. A feature that once took several days of incremental work can arrive in an afternoon with a migration, API, interface, tests, dependency upgrade and cleanup across forty files. The implementation may work. The pull request still asks one reviewer to reconstruct too many decisions at once.
This guide shows how to cut that change into a sequence people can review. If you first need to decide whether the original PR is understandable at all, start with how to review a vibe-coded pull request.
Why large AI-generated pull requests are hard to review
Review difficulty does not rise neatly with changed lines. It rises with the number of concepts, boundaries and side effects a reviewer must hold in mind.
A 1,000-line generated client can be easier to check than a 150-line authorization change spread across routes, policies, database queries and background jobs. A useful size assessment includes:
- distinct behaviors changed
- files and layers touched
- data migrations
- security boundaries
- external API contracts
- deployment or configuration changes
- generated and mechanical noise
- rollback difficulty
Assess these signals together rather than relying on line count. A large generated file and a small authorization change create very different review burdens even when their raw sizes suggest the opposite.
Google’s engineering guidance describes a small change as one self-contained change, with the related tests and enough context for a reviewer to understand it. That definition is more useful than a universal line limit.
First, remove noise from the size estimate
Before planning the split, classify the changed files:
- product behavior
- tests
- schema and migrations
- refactoring
- dependencies and lockfiles
- generated code
- documentation
- infrastructure and configuration
Lockfiles, generated clients, snapshots and formatter output can dominate the raw line count. Separate those files from the code a human must reason through, then verify how each generated or mechanical artifact was produced.
Noise is not free. A dependency still needs provenance, and generated output should match its source. It should be reviewed differently from handwritten behavior rather than mixed into the same mental total.
Write the dependency graph before making branches
List the pieces of work and draw an arrow when one must land before another. For a feature that adds workspace usage limits, the graph might look like this:
database columns and defaults
↓
usage counter service
↙ ↘
review admission billing display
↓ ↓
background retries settings UIThis graph exposes two kinds of split:
- Preparation: code that enables later behavior without changing the user experience.
- Vertical behavior: one narrow outcome that crosses the required layers and can be tested on its own.
Do not begin by making one PR for frontend files and another for backend files unless each can be understood and validated independently. File ownership can justify that split, but directory boundaries alone rarely match product behavior.
A worked example: splitting an AI-generated usage-limit feature
Suppose an agent produced one 2,400-line pull request that:
- adds monthly review limits to the workspace table
- counts model-backed reviews
- blocks new reviews after the limit
- shows usage in settings
- sends an email near the limit
- refactors the billing service
- upgrades the email package
The generated PR is internally connected, but it contains at least six review decisions. A safer sequence follows.
PR 1: Add the data model without enforcing it
Add the columns, defaults, constraints and migration. Include tests that migrate representative old records and prove the default state preserves current behavior.
Keep application reads tolerant of the new fields. Do not block reviews yet.
The reviewer can focus on:
- migration safety
- defaults for existing workspaces
- nullability and constraints
- rollback behavior
- compatibility during a rolling deployment
PR 2: Introduce the usage counter behind existing behavior
Add the service that records completed model-backed reviews, but do not enforce a limit. Expose metrics or logs that let the team compare the counter with known activity.
The reviewer can focus on:
- what counts as a billable review
- idempotency under retries
- transaction boundaries
- backfill requirements
- whether cancelled or failed reviews count
PR 3: Enforce admission with a disabled flag
Add the decision that allows or rejects a new review. Put the behavior behind a server-side flag or configuration default that leaves production unchanged until enabled.
Keep the tests with this PR:
- below limit is allowed
- at limit is rejected before model usage
- two concurrent admissions cannot both consume the last slot
- an override follows the documented permission
The reviewer now sees the main business rule without the settings interface or email work.
PR 4: Show usage in the product
Add the API response and settings display for current usage and limit. This vertical change can include server serialization, client types, loading and error states, and focused interface tests.
It does not need to know how warning emails work.
PR 5: Add threshold notifications
Add the near-limit email as a separate side effect. Review deduplication, recipient selection, retries and unsubscribe or notification preferences without mixing them into admission control.
PR 6: Refactor and upgrade separately
Move the unrelated billing cleanup and email-package upgrade out of the feature sequence unless an earlier PR truly requires them. A refactor with no intended behavior change needs tests that establish equivalence. A package upgrade needs release-note and compatibility review.
This sequence creates more pull requests, but each one asks a smaller question and can be reverted independently.
Keep the system working after every merge
A split that breaks the build between pull requests is a publishing schedule, not a safe change sequence.
Use compatibility techniques such as:
- add fields before requiring them
- accept old and new payload shapes during a transition
- deploy readers before writers
- keep behavior behind a disabled server-side flag
- make database migrations backward compatible with the running version
- introduce interfaces before switching implementations
- remove old paths only after every caller has moved
Avoid long-lived compatibility layers when a short stacked sequence will do. Mark the removal in the plan and include it in the final cleanup PR.
Keep tests with the behavior they protect
Do not put all tests in a final pull request. Reviewers need evidence while evaluating each change, and the repository should stay protected if the sequence pauses halfway through.
Preparation PRs need tests too:
- migration PR: old data migrates safely
- refactor PR: behavior remains unchanged
- interface PR: old implementation still satisfies the contract
- generated-client PR: output is reproducible from the reviewed schema
Independent characterization tests can land before a risky refactor. The guide to reviewing AI-generated tests explains how to check that generated tests protect behavior rather than mirror the implementation.
Separate refactoring from behavior
Agents often “clean up nearby code” while implementing a feature. The cleanup may be reasonable. It also makes it harder to tell which changed lines are required for the behavior.
Move a refactor into its own pull request when it:
- renames or moves code across modules
- changes an abstraction used beyond the feature
- rewrites control flow without changing intended output
- applies formatting or generated fixes broadly
- introduces a helper mainly to make later work easier
Land the refactor first when it prepares a clean seam. Land it later when it is optional cleanup. Do not hide behavior changes inside a refactor label.
Use vertical slices for product behavior
A vertical slice implements one outcome through every layer it needs. For example, “workspace owners can rename a project” may include:
- route and authorization
- validation
- database update
- client call
- interface state
- tests
That can be easier to review than separate backend and frontend PRs because the reviewer sees one complete contract. Vertical slices work best when they are narrow and can ship independently.
Horizontal splits still make sense for shared infrastructure, schemas or APIs that multiple slices require. The dependency graph should explain why the horizontal piece exists and show at least one real use soon after.
Use stacked pull requests carefully
Stacking lets the author continue while earlier preparation is under review. Each pull request targets the previous branch, so reviewers see only the relevant delta.
For each PR in the stack:
- state its parent and dependents
- explain whether it changes production behavior
- link the full implementation plan
- keep commits coherent enough to rebase
- update the description when an earlier review changes the contract
Do not ask reviewers to approve PR 4 while PR 1 still contains an unresolved design decision that changes the whole stack. Parallel review works for independent details, not for unsettled foundations.
A useful pull request description should state the parent, dependents, rollout order and behavior of each change. File paths alone cannot explain those dependencies.
When should you refuse to split a large PR?
Some large diffs are best reviewed as one unit:
- output from a trusted, reproducible code generator
- mechanical migration produced by an established tool
- deletion of a self-contained subsystem
- atomic protocol change where intermediate compatibility is impossible
Even then, separate the source change from generated output when possible, document the generation command and focus human review on the inputs and invariants.
“The agent already wrote it this way” is not a reason to keep a PR large.
A practical splitting checklist
### Large pull request split
- [ ] The intended behavior is written in one sentence.
- [ ] Generated files, lockfiles and mechanical changes are classified separately.
- [ ] Dependencies between changes are written down.
- [ ] Refactors are separated from product behavior where practical.
- [ ] Each PR leaves the build and application in a working state.
- [ ] Related tests ship with the behavior they protect.
- [ ] Data and API changes remain compatible during the sequence.
- [ ] Each PR can be explained and reverted independently.
- [ ] Stack order and rollout behavior are documented.Frequently asked questions
How large is too large for a pull request? There is no universal line limit. A PR is too large when the reviewer cannot understand its intended behavior, important boundaries and evidence at the same time. Spread, risk and conceptual count matter more than raw lines.
Should I split a pull request by frontend and backend? Only when each side has a stable contract and can be reviewed or shipped independently. For narrow product behavior, a vertical slice with both sides is often easier to verify.
Can AI review a very large pull request? AI can summarize files and surface candidate issues, but context limits and missing product intent still matter. Splitting improves both human and AI review because each change has a clearer purpose and smaller set of relevant context.
Should generated files be a separate pull request? Often yes, especially when they create most of the diff. Include the reviewed source change and generation command, or separate reproducible output so reviewers can focus on provenance rather than inspect every generated line.
Start with the smallest independently useful behavior, keep its tests beside it and make the rest of the stack earn its dependency on that first change.