PR Size: How Big Should a Pull Request Be? Data From 12 Repos

repowise team9 min read
On this page

A pull request should be one self-contained change, and for most teams that means under about 400 changed lines. Google's engineering practices say the right size is "one self-contained change" and call about 100 lines reasonable and 1,000 usually too large, and the SmartBear study of a Cisco team put the limit of effective review at 200 to 400 lines. We also measured it: in 3,202 merged PRs from 12 active public repositories, the median PR changed 51 lines across 3 files.

Below is the data, how we pulled it, what the published limits say, and how to split a PR that has already grown.

What is the typical pull request size?

As of 29 October 2026, in the merged PRs we sampled, the median pull request changed 51 lines (additions plus deletions, lockfiles and generated files excluded) in 3 files. Three quarters of PRs were under 177 lines, 87% were under 400, and the 90th percentile was 590 lines and 17 files.

Lines changedPRsShareMedian files
under 501,57549%1
50 to 19988828%4
200 to 39931210%7
400 to 9992387%13
1,000 and over1896%42

Scroll the table sideways to see every column.

Repositories differ a lot. Median lines per PR, from the same sample:

RepositoryPRsMedian lines
godotengine/godot34410
home-assistant/core28218
django/django16526
dotnet/runtime27132
grafana/grafana24061.5
apache/airflow24162
kubernetes/kubernetes28366
vercel/next.js33569
rust-lang/rust33472
facebook/react16490.5
hashicorp/terraform220102
microsoft/vscode323104

Scroll the table sideways to see every column.

Home Assistant and Godot merge many small integration and bug-fix PRs. VS Code and Terraform sit above 100 lines at the median. None of them has a median anywhere near 400.

Does PR size change time to merge and review?

In the repositories that record approvals as GitHub reviews, bigger PRs took longer to merge and drew more review, with the largest bucket the slowest. Nearly every PR in those repositories got at least one human review whatever its size, so unreviewed merges do not separate big PRs from small ones. This table uses 10 of the 12 repositories: rust-lang/rust and kubernetes/kubernetes approve by comment (bors and prow /lgtm), so GitHub shows no review on many of their PRs and they are left out here.

Lines changedPRsMedian hours to mergeMedian reviewsPRs with changes requestedPRs with no human review
under 501,3144.111.8%1.1%
50 to 1997185.913.3%2.1%
200 to 3992565.313.5%2.7%
400 to 9991846.726.0%4.3%
1,000 and over1138.420.9%1.8%

Scroll the table sideways to see every column.

Bar chart of median hours from pull request opened to merged, rising from 4.1 hours for PRs under 50 lines to 8.4 hours for PRs over 1,000 linesBar chart of median hours from pull request opened to merged, rising from 4.1 hours for PRs under 50 lines to 8.4 hours for PRs over 1,000 lines

Correlation is not causation: the biggest PRs are often moves, generated output or release work that gets merged on trust, so the table describes what happens to big PRs, not what big PRs cause.

The last row shows it. Changes requested peak at 6.0% for 400 to 999 lines and fall to 0.9% above 1,000, which fits large PRs being mostly mechanical changes that reviewers confirm instead of debate. A review counts here whether it is an approval, a change request or comments only, so the reviews column is a floor for effort.

How we measured

  • Repositories: kubernetes/kubernetes, vercel/next.js, microsoft/vscode, rust-lang/rust, django/django, facebook/react, home-assistant/core, grafana/grafana, dotnet/runtime, apache/airflow, godotengine/godot, hashicorp/terraform. We first included pytorch/pytorch and dropped it. PyTorch lands PRs through its merge bot, so GitHub marks most human-authored PRs closed instead of merged, and the merged-PR query returned only 53 PRs for the quarter, 33 of them bot-authored. That sample would not represent its real flow, so dotnet/runtime replaced it.
  • Window and sample: PRs merged 2026-07-01 to 2026-09-30, pulled on 2026-10-29 with the gh CLI. This is a sample, not every PR. The window was cut into 14 slices (13 weeks plus a final day) and up to 25 merged PRs were taken from each slice, newest first, so a repository contributes at most 350 PRs and busy repositories are sampled from the end of each week. The sample was 3,567 PRs before filtering.
  • Bots: PRs whose author is a bot account were dropped, 365 in all: GitHub apps such as github-actions, dependabot, renovate, copilot-swe-agent and cursor, plus logins ending in "bot" such as rustbot and vercel-release-bot. The filter is on the author, never on who merged. Bot reviews are not counted as human reviews.
  • facebook/react: GitHub's search API does not index this repository (every search scoped to it returns a validation error), so the merged: queries used for the others return nothing. It is sampled with gh pr list --state merged and cut into the same slices locally. It contributes 164 PRs because React merges less often than the others.
  • Size: additions plus deletions per PR from the pull request files API, excluding lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml, Cargo.lock, go.sum, poetry.lock, uv.lock, Gemfile.lock, composer.lock), snapshots (*.snap, __snapshots__/), vendored and node_modules directories, minified files, and generated protobuf and Kubernetes zz_generated files. Other generated code stays in. Without the exclusions the median is 54 lines instead of 51 and the 90th percentile is 655 instead of 590.
  • Time to merge: mergedAt minus createdAt, so draft time counts.
  • Reviews: a review submission from an account that is not the PR author and not a bot (names ending in "bot", [bot] or app/, plus Copilot, CodeRabbit, github-actions, Dependabot and Renovate). Approvals, change requests, comment-only and dismissed reviews all count. Approvals given in comments, such as /lgtm, are invisible to this method, which is why two repositories are excluded from the review and time columns.

The sampling pull, per repository and weekly slice (file lists come from gh api repos/OWNER/REPO/pulls/NUMBER/files --paginate):

bash
gh pr list -R vercel/next.js --state merged \
  --search "merged:2026-07-01..2026-07-07" --limit 25 \
  --json number,author,additions,deletions,changedFiles,createdAt,mergedAt,reviews

Samples of 164 to 344 PRs per repository from large projects are a snapshot of how those projects work, not a population estimate for all teams. Your own repository's numbers will differ, and gh makes it a short script to check.

What PR size limit do teams use?

The two sources most teams cite are Google's engineering practices and the SmartBear study of a Cisco team. Google's numbers are 100 to 1,000 lines and SmartBear's are 200 to 400.

Google says in its small CLs guidance that the right size for a change is "one self-contained change": a minimal change that addresses one thing, usually one part of a feature, with its related tests. It also gives rough numbers: "100 lines is usually a reasonable size for a CL, and 1000 lines is usually too large", with the caveat that the number of files counts too (a 200-line change in one file might be fine, spread across 50 files it usually is not). Whole-file deletions count as about one line, and changes from a trusted automatic refactoring tool can be larger. It lists eight reasons small changes are better, including faster and more thorough review, fewer bugs and simpler rollbacks, and it says reviewers may reject a change for being too large.

SmartBear reports, in its peer code review best practices (checked 29 October 2026), that a study of a Cisco Systems programming team found developers should review no more than 200 to 400 lines at a time, and that beyond 400 the ability to find defects diminishes. It adds that 200 to 400 lines over 60 to 90 minutes should yield 70 to 90% defect discovery, and that defect density drops when inspection runs faster than 500 lines an hour.

SourceLimitWhat it says
Google eng-practicesAbout 100 lines reasonable, 1,000 usually too largeOne self-contained change, with tests; err small
SmartBear and Cisco200 to 400 lines per reviewDefect finding falls beyond 400; stay under 500 lines an hour
This sampleMedian 51 lines77% of PRs under 200 lines, 87% under 400

Scroll the table sideways to see every column.

A workable team rule is a soft cap near 400 changed lines excluding generated files, with the reviewer free to ask for a split above it. Count lines the reviewer must read, not lines in the diff. Our sample drops lockfiles, snapshots and common generated files but keeps other generated code, so it can still overstate what a person reads.

How do you split a large pull request?

Split along changes that can ship on their own: behaviour-preserving refactors first, then the feature in slices that each leave the system working. Three techniques cover most cases.

  1. Refactor first. Move code, rename and extract in a separate PR that changes no behaviour. Reviewers can skim a pure move, and the follow-up PR shows only the real change. Land it before the feature branch, then rebase.
  2. Feature flags. Merge unfinished work behind a flag that is off. Each slice is small, reviewed and in trunk, and the flag turns on when the last slice lands. This also removes the long-lived branch that conflicts with everyone.
  3. Stacked PRs. Open PR 2 against the branch of PR 1, and PR 3 against PR 2. Each is reviewed alone. The cost is rebasing the stack when an early PR changes, which is why tooling for stacks exists. Plain git works:
bash
git switch -c step-1-schema main
# commit, push, open PR against main
git switch -c step-2-api step-1-schema
# commit, push, open PR against step-1-schema
# after step 1 merges (squash), move step 2 onto main
git rebase --onto main step-1-schema step-2-api

A fourth option is splitting by independent concern. If a PR touches an auth change and an unrelated logging cleanup, those are two PRs. The question is whether any file in group A depends on group B, and that is something you can check instead of guess.

How does repowise keep pull requests reviewable?

repowise puts the facts a reviewer needs on each PR, so a 600-line diff is read with its blast radius in view and an unrelated change is easy to spot. This is what the repowise change-risk layer does.

repowise risk scores a change (a commit, a base..head range or uncommitted work) by where its diff size and spread rank among the repository's recent commits, and lists prior bug fixes on the files it touches. It needs no LLM key and no index. When an index exists it also reports independent_changes: groups of changed files that nothing links through imports, calls, co-change history or shared commits. Two or more groups is a sign the PR should be split.

bash
repowise risk main..HEAD
repowise risk main..HEAD -x 'tests/' -x '*.spec.ts'   # leave tests out of the size

The repowise bot brings the same signals into the pull request. It has two modes: Signals, which are free, deterministic, use no model and stay quiet unless a threshold is crossed, and an optional AI review on Pro, with review depth you can set to light, auto or deep. A "repowise review" check run is on by default and never fails the build unless you turn the health gate on. The tests signal is an estimate from the import graph unless you connect a coverage report. AI review costs about 9 cents a pull request on average. Comment /repowise review deep to ask for a deep review or /repowise recheck to run again.

You can see a real one on repowise-dev/repowise#4506. For the author-side routine, see the pull request checklist.

Pitfalls when enforcing a PR size limit

  • Counting generated files. Lockfiles, snapshots and codegen output inflate the number. Exclude them, for example repowise risk main..HEAD -x package-lock.json -x '*.snap'.
  • Splitting by file instead of by change. Ten PRs that each need the other nine to work are one large PR with more overhead.
  • Making size a performance metric. People split commits artificially. Use it as a review aid.
  • Ignoring spread. A 30-line change across 25 files in a core package carries more risk than 300 lines in one new module. Files changed and where they sit matter as much as line count.

FAQ

How many lines should a pull request be?
Aim for under about 400 changed lines and one self-contained change. In our sample of 3,202 merged PRs from 12 public repositories, the median was 51 lines and 87% were under 400. Google calls about 100 lines reasonable and 1,000 usually too large, and the SmartBear study of a Cisco team found review effectiveness fell past 200 to 400 lines.
What is the ideal PR size according to research?
The most cited figure is 200 to 400 lines per review, from the SmartBear study of a Cisco Systems team, reviewed over 60 to 90 minutes for 70 to 90% defect discovery. That is a review-session limit, so a PR can be larger if it is reviewed in several sittings, though each sitting loses accuracy past about 60 minutes.
Are small pull requests really better?
Google lists faster review, more thorough review, fewer bugs and simpler rollbacks as reasons. In our data, the median PR over 1,000 lines took 8.4 hours to merge against 4.1 for PRs under 50 lines. We cannot show that size causes this, because large PRs in these repositories are often generated or mechanical changes.
How do you break up a large pull request?
Land behaviour-preserving refactors first, put unfinished work behind a feature flag, and stack the remaining slices so each is reviewed on its own. Split independent concerns, such as an auth change and a logging cleanup, into separate PRs.
Do lockfiles and generated files count toward PR size?
They count in raw GitHub numbers, but reviewers do not read them. We excluded common lockfiles and snapshots from ours. Set your limit on lines a person must read and exclude generated files when you measure.
How can I check the PR size in my own repository?
Run the gh pr list --state merged --json additions,deletions,changedFiles,createdAt,mergedAt command shown above for your repository and take the median. To score a single change, run repowise risk main..HEAD.