Here’s a problem most developers hit eventually: you’re building a substantial feature, you’ve been working on it for a week, and now you’ve got 40 changed files and 1,500 lines of diff sitting in a single branch. Your reviewer opens the pull request, sees the size of it, and schedules the review for “sometime later in the week.” The review, when it happens, is cursory because nobody can hold 1,500 lines of context in their head. The code merges, problems that could have been caught aren’t, and everyone quietly agrees to add “keep PRs small” to the team playbook without changing how they actually work.

Stacked pull requests are the practical answer to this, and they’re genuinely useful once you’ve built the muscle memory for the workflow.

What Stacked PRs Actually Are

A stacked PR workflow breaks a large feature into a sequence of small, logically distinct PRs where each builds on the previous one. Instead of one PR with everything, you have something like:

  • PR1: Add the database schema migration
  • PR2: Add the API endpoint (depends on PR1)
  • PR3: Add the frontend component (depends on PR2)
  • PR4: Wire up end-to-end with tests (depends on PR3)

Each PR is reviewable on its own terms. The reviewer for PR1 just needs to understand the schema change. The reviewer for PR2 can see a clean API implementation without navigating past test files and UI code. Reviews happen in parallel while you’re still building later parts of the stack.

The traditional alternative — waiting for each PR to merge before starting the next branch — creates the blocking problem. Stacked PRs solve this by letting you keep building on top of in-review work while your reviewer catches up.

The Problem Stacking Solves

To be honest, part of why stacked PRs aren’t universal is that the tooling for managing them has historically been painful. The core issue is rebasing. When PR1 merges and the commits get squash-merged or rebased onto main, your PR2 branch — which was based on PR1’s commits — now has the same changes appearing as “history” commits from main plus your changes. Managing that manually with git rebase is doable but fiddly, and it compounds with every level in the stack.

This is what tools like Graphite exist to solve. Graphite wraps your git workflow with a CLI that understands stacks and handles the rebasing automatically when upstream PRs merge.

Using Graphite in Practice

Graphite’s CLI is installed as an npm package (npm install -g @withgraphite/graphite-cli) and integrates with your existing GitHub repositories. The basic workflow:

# Start a new branch on top of your current branch
gt branch create pr1-database-schema

# ... make your changes ...
git add -A && git commit -m "Add users migration"

# Create the next branch in the stack
gt branch create pr2-api-endpoint

# ... make your changes ...
git add -A && git commit -m "Add users API endpoint"

# Submit all PRs in the stack to GitHub
gt submit

The gt submit command creates GitHub PRs for each branch in the stack, automatically sets the base branch correctly (PR2’s base is PR1’s branch, not main), and adds visual stack context to each PR description so reviewers can see where this PR sits in the overall change.

When your reviewer requests changes on PR1, you make the fix there, and gt sync propagates the rebase through the rest of the stack. That’s the part that would otherwise be a manual git operation for each dependent branch.

Graphite also provides a web dashboard (at graphite.dev) that shows all your stacks, their review status, and merge dependencies. It replaces the mental model of “which of my PRs are waiting for what” with a visual graph.

Alternatives to Graphite

ghstack (from PyPI: pip install ghstack) takes a different approach. It maintains a single main branch but with multiple commits, each representing a “PR” in the stack, and creates GitHub PRs automatically. It’s less visual than Graphite but works well for individuals who want the workflow without a web dashboard.

git-spr (Stacked Pull Requests) is another open-source CLI tool that works with GitHub and manages rebase chains automatically. It’s more minimal than Graphite but entirely open source with no account required.

Phabricator-style workflows (used historically at Meta and still used in some places) use a code review system that natively understands stacks. If you’re on GitHub, you’re unlikely to be using this, but it’s where the concept matured before tools like Graphite brought it to mainstream GitHub workflows.

When Stacking Makes Sense

Not every change benefits from stacking. A genuine one-line bug fix or a self-contained feature change should just be a normal PR. Stacking adds overhead — more branches, more PR management — and that overhead only pays off when the underlying change is large enough to genuinely benefit from being broken down.

The heuristic that works well in practice: if your PR would be more than 400 lines of meaningful changes, or if it touches more than two distinct areas of the codebase, think about whether it could be split into a stack. If the answer is no — if it genuinely can’t be logically separated — then a large PR is fine. Size isn’t the problem; reviewability is.

AI coding assistants have actually made this more relevant recently. When you’re using Claude Code, Cursor, or Copilot to write substantial features, the code generation speed outpaces human review speed. A developer who used to produce 200 lines a day now produces 800. Without a corresponding change in how that code is reviewed, review quality drops. Stacked PRs are one of the structural responses to this dynamic — keeping the review unit human-sized even when the generation unit is much larger.

The tooling is good enough now that there’s no longer a strong reason not to try it. Set up Graphite, run a few stacks through the workflow, and see whether it changes how your team’s code review actually goes.