Every development team hits the same wall: a pull request that sprawls across thirty files, mixes a refactor with a new feature, and stalls in review for days. Even experienced reviewers struggle to keep the entire diff in their head, and the author eventually gets frustrated waiting for an approval that never comes. The usual advice—”open smaller PRs”—doesn’t always work, because some changes are genuinely large in scope. A better solution is to slice big pull requests into reviewable commits using a deliberate commit-splitting workflow. When every commit is a clean, testable unit, the PR becomes a story your reviewers can follow, and the merge becomes far less risky.
Why Big Pull Requests Are a Productivity Trap
Large PRs are not just annoying; they are measurably expensive. Research on code review has repeatedly shown that reviewers lose significant context after the first few hundred lines of diff. A thirty-file PR forces reviewers to context-switch between unrelated concerns—a database migration here, a CSS tweak there—which leads to shallow feedback, delayed approvals, and subtle bugs slipping through the cracks.
The cost compounds when the branch stays open for a long time. The longer it lives, the more it diverges from the mainline, and the more painful the eventual integration becomes. Merge conflicts pile up, trust in the team’s CI diminishes, and the entire pull request becomes a bottleneck. The productivity loss is rarely because the engineering work is too hard; it is because the work is packaged in a way that is difficult for other humans to process.
The Commit-Splitting Workflow That Keeps PRs Reviewable
The core idea behind this workflow is atomicity. A commit is atomic when it does exactly one logical thing, leaves the project in a working state, and could theoretically be merged on its own. When every commit in a PR is atomic, the PR itself becomes a sequence of reviewable steps instead of a blur of interrelated changes. The workflow below turns a messy, lumpy branch into a tidy series of commits that any reviewer can walk through without losing the plot.
Step 1: Map the Change into Logical Layers
Before you touch git, stop and analyze the diff as it currently exists. Write down the distinct concerns hiding inside your working tree. For example, a dark mode PR might actually contain four separate things: design token updates, a theme context switch, a new set of CSS variables, and changes to three components. Those are different concerns, even if they touch adjacent files. List them and decide the sequence a reviewer should see them in: foundations first, application logic second, cosmetic changes last. This map is your blueprint for the rest of the process.
Step 2: Rebuild the Branch with Interactive Rebase
Once you have your map, restructure the branch with git rebase -i main. The interactive rebase editor lets you reword confusing commit messages, reorder commits so the narrative flows, edit commits that need splitting, and drop commits that should not exist at all. If your branch is a tangle of WIP commits with messages like “fix” and “stuff,” squash them down to a single base commit and start fresh from there. For extra convenience, enable autosquash in your git config so fixups are automatically assigned to the commit they belong to.
Step 3: Split a Commit into Multiple Focused Commits
When you mark a commit as edit in the rebase, git pauses at that exact point in history. To break it apart, run git reset --soft HEAD~1. This undoes the commit but keeps all of its changes in the staging area. Now use git add -p to interactively select hunks, and stage the first logical slice. Commit it with a descriptive message that follows your team’s conventions, like git commit -m "feat(theme): add dark mode design tokens". Repeat the process for the next slice, and the next.
Not every split fits neatly into hunks. If a logical slice spans multiple files, use git add <paths> to stage the whole file set together. If a single file must be split between commits, git add -p remains your best tool because it lets you pick individual diff hunks inside that file. And if some work-in-progress code still needs attention, place it in a later commit rather than the first one so the earliest commits stay clean.
Step 4: Verify Every Commit in Isolation
A commit that cannot build on its own is not reviewable. After splitting, run a verification pass over the entire branch. The fastest way is to pair the interactive rebase with an exec command: git rebase -i main --exec "npm run lint && npm test". This replays your checks against every single commit in the new series. If one fails, you have found a boundary problem: a piece of code was split away from its dependency. Fix the split, not just the tests, because a commit that fails on its own will break git bisect for everyone later.
Order Commits So the Pull Request Is Easier to Review
The ordering of commits is an underappreciated part of the commit-splitting workflow. Start with infrastructure: the schema change, the new utility function, the shared type definitions. Then follow with the feature logic that consumes that infrastructure. Put test updates in the same commit as the code they validate, rather than in a separate “make tests pass” commit at the end.
A useful pattern is to lead with a small, non-functional refactor so the noise is out of the way before the feature logic appears. Keep commit messages in the imperative mood, use standard prefixes such as feat:, fix:, and refactor:, and add a short body explaining why the change exists. Reviewers should be able to read the commit list like a table of contents for the PR.
Tools That Make Commit Splitting Less Painful
The classic commands still carry the load. git add -p remains the workhorse for split-by-hunk staging, while git stash helps you temporarily set aside unrelated changes. git worktree lets you open a second working directory on the same branch, which is handy for quick verification without disrupting your current workspace. For more ambitious teams, git absorb can automatically fold changes into the correct existing commit by matching their context, and modern alternatives like Jujutsu (jj) offer a commit-first model that makes rewrites, splitting, and reordering feel more natural than vanilla git.
A Worked Example: Refactor Plus Feature in One PR
Suppose you need to upgrade an authentication middleware and add a login audit log. Your branch currently has one giant commit named “stuff.” The conversion sequence looks like this:
git reset --soft HEAD~1— undo the giant commit while keeping all changes staged.git add auth/middleware.go auth/session.go— stage the refactor only.git commit -m "refactor(auth): extract session validation middleware"— first logical slice.git add audit/ audit_test.go— stage the audit feature plus its tests.git commit -m "feat(audit): log successful logins to the audit trail"— second logical slice.git rebase main --exec "go test ./..."— verify both commits build and pass.
Now the PR contains two meaningful commits, each reviewable in a single sitting. Your reviewers can approve the refactor confidently and give the feature the focused attention it deserves. The conversation around the PR also sharpens: instead of vague comments about “that big auth diff,” teammates can reference a specific commit by its hash.
Beyond the Review: Merging Becomes Predictable
When every commit is atomic and verified, you unlock benefits beyond the review session. git bisect becomes reliable because each commit represents a known state. The final merge is no longer a conflict-resolution battleground; it can be a clean fast-forward or a trivial rebase. Mainline history becomes a readable record of how the project evolved, which is invaluable for onboarding and for debugging production issues months later.
Slicing big pull requests into reviewable commits is not about bureaucratic overhead; it is one of the highest-leverage habits a developer can adopt. By committing to a deliberate commit-splitting workflow, you respect your reviewers’ attention, keep the mainline healthy, and reduce the risk of every merge. The extra thirty minutes spent rewriting history before opening the PR pays for itself many times over—in faster approvals, fewer conflicts, and a project history that makes sense.
