Back to Blog
App DevelopmentPublished on July 30, 2026

Mastering Stacked Pull Requests: Architecting High-Velocity Code Reviews in Modern Enterprise Workflows

Discover how stacked pull requests eliminate code review bottlenecks by decomposing complex feature branches into atomic, dependent units. Learn manual Git techniques and automated strategies to supercharge engineering velocity.

Introduction: The Friction of Monolithic Code Reviews

In modern enterprise software engineering, code review remains one of the single largest bottlenecks to team velocity. As feature complexity grows, developers routinely submit massive pull requests (PRs) spanning thousands of lines of code across dozens of files. These monolithic PRs present severe operational challenges: reviewer fatigue leads to superficial approval, merge conflicts compound as main branches drift, and critical bugs slip past overloaded peer reviewers.

To circumvent this, traditional developer guidance suggests keeping PRs small. However, real-world feature development rarely happens in isolated, 50-line increments. A backend schema change inherently depends on an API contract, which in turn feeds a new frontend component. Forcing developers to wait for each sequential PR to be reviewed, merged, and deployed before starting the next creates immense idle time and destabilizes momentum.

This is where Stacked Pull Requests (Stacked PRs) become transformative. By decomposing large architectural changes into a linear series of small, isolated, dependent branches, stacked workflows allow engineers to continue building incrementally without blocking reviewers or stalling progress.


Deconstructing the Mechanics of Stacked PRs

At its core, a Stacked PR workflow is a chain of Git branches where each branch builds directly upon the head of its predecessor, rather than branching directly off the primary trunk (e.g., main or master).

The Monolithic Workflow vs. The Stacked Architecture

In a traditional workflow, a single developer branch accumulates all changes for a feature:

main ───► Commit A ───► Commit B (Schema) ───► Commit C (API) ───► Commit D (UI)

Result: A single PR containing 1,200 lines across 30 files.

In a Stacked PR architecture, the work is decoupled into an explicit directed acyclic graph (DAG) of small, logical PRs:

main
 └──► stack/01-db-schema  (PR #1: +120 lines)
       └──► stack/02-api-layer  (PR #2: +250 lines, targets #1)
             └──► stack/03-ui-views   (PR #3: +180 lines, targets #2)

Key Benefits of Stacking

  1. Atomic Code Reviews: Reviewers focus exclusively on isolated contexts (e.g., evaluating just the database migration before reading business logic).
  2. Unblocked Pipeline: Developers can branch off unmerged code to begin work on downstream dependencies immediately.
  3. Granular Bisecting & Rollbacks: If a regression occurs, bisecting Git history isolates the exact component, and individual commits or branches can be reverted cleanly.

Step-by-Step Technical Walkthrough: Managing Stacks in Vanilla Git

While specialized CLI tools automate stack management, understanding how to construct, update, and rebase stacks natively using raw Git commands is essential for understanding the underlying mechanics.

Step 1: Branch Creation and Initial Stack Setup

Assume you are implementing an authentication feature requiring a schema change, a controller implementation, and a middleware guard.

First, create the base branch off main:

git checkout main
git pull origin main
git checkout -b feature/auth-schema

# ... make schema changes ...
git commit -m "feat(auth): add user sessions table migration"
git push origin feature/auth-schema

Next, stack the second branch directly on top of feature/auth-schema without waiting for approval:

git checkout -b feature/auth-controller

# ... implement controller logic ...
git commit -m "feat(auth): implement session validation controller"
git push origin feature/auth-controller

Create the third branch on top of feature/auth-controller:

git checkout -b feature/auth-middleware

# ... add route protection ...
git commit -m "feat(auth): attach authentication guard middleware"
git push origin feature/auth-middleware

When opening Pull Requests on platforms like GitHub or GitLab, set the target base branch of PR #2 to feature/auth-schema, and PR #3 to feature/auth-controller.


Step 2: Propagating Upstream Changes Across the Stack

Suppose a reviewer requests changes on PR #1 (feature/auth-schema). You modify the migration and amend or append a commit to PR #1.

git checkout feature/auth-schema
# ... address feedback ...
git commit -m "fix(auth): add non-null constraint to session token"
git push origin feature/auth-schema

Because feature/auth-controller was created from the older commit of feature/auth-schema, you must now synchronize the stack using git rebase --onto.

To cascade changes up to the second branch:

git checkout feature/auth-controller
git rebase feature/auth-schema
git push origin feature/auth-controller --force-with-lease

Then propagate through to the third branch:

git checkout feature/auth-middleware
git rebase feature/auth-controller
git push origin feature/auth-middleware --force-with-lease

Using --force-with-lease ensures you do not overwrite remote commits pushed by teammates or CI bots.


Managing Cascading Rebases with Advanced Tools

Executing standard manual rebases across stacks with 5 to 10 layers becomes error-prone. Modern developer tooling solves this by maintaining a local graph of branch relationships and automating cascading rebases.

Tooling Comparison: Graphite, Sapling, and Native Platform Features

| Feature | Vanilla Git | Native GitHub (Recent) | Graphite / CLI Tools | Sapling (Meta) | | :--- | :--- | :--- | :--- | :--- | | Branch Creation | Manual (git checkout -b) | Manual | Automated (gt create) | Virtualized (sl submit) | | Cascading Rebase| Manual (git rebase --onto) | Partial Auto-Retargeting | Fully Automated (gt upstack restack) | Automatic Graph Rebase | | PR Dependencies | Manual Base Retargeting | Native Stack Navigation | Auto-generated Navigation Table | Single-Diff Stack Tracking |

Using CLI Automation (e.g., Graphite CLI)

With dedicated stacking tools, operations that took dozens of manual Git commands are reduced to single operations:

# Create a new branch in the stack
gt create feature/auth-schema
# Commit work
gt commit -m "feat: schema"

# Create next layer directly
gt create feature/auth-controller
gt commit -m "feat: controller"

# Submit all PRs in the stack concurrently to GitHub
gt stack submit

# Modify an earlier branch and automatically update downstream branches
gt log short           # View visual stack graph
gt checkout feature/auth-schema
# Make changes...
gt commit --amend
gt upstack restack     # Automatically rebases all child branches cleanly

CI/CD Pipeline Strategies for Dependent PRs

Adopting stacked PRs introduces specific challenges for Continuous Integration systems. If every branch in a stack triggers a full suite of end-to-end tests, build times can multiply exponentially.

1. Speculative Build Testing and Merge Queues

When PR #1 merges into main, GitHub or GitLab automatically changes the target of PR #2 to main. If PR #2 was tested against PR #1's outdated tip, silent integrations bugs can occur. Utilizing a Merge Queue (e.g., GitHub Merge Queue, Bors, or Mergify) ensures that branches are tested in combination prior to landing on main.

2. Selective Test Execution

To minimize compute costs, configure CI workflows to evaluate only modified code paths for lower layers of the stack, reserving full integration test suites for the terminal head branch or upon entering the merge queue.

# Example GitHub Actions filter for partial stack builds
name: Smart CI Stack Check
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  unit-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Get Changed Files
        run: |
          git diff --name-only origin/${{ github.base_ref }}...HEAD > changed_files.txt
          # Trigger granular test suites based on file paths

Cultural Alignment and Team Best Practices

Transitioning an engineering organization to stacked PRs requires shifting operational mindsets from synchronous to asynchronous code reviews:

  1. Enforce Small Atomic Boundaries: A stack layer should ideally address a single concern and rarely exceed 200 lines of code.
  2. Review Top-Down or Bottom-Up systematically: Reviewers should approve PR #1 first to establish stable foundations before evaluating PR #2.
  3. Automate Navigation Headers: Ensure your PR template or CLI tool inserts markdown links showing stack context:
### Stack Context
- 🟩 **#101: Add session schema** (This PR)
- 🟨 #102: Add session controller
- ⬜ #103: Add session middleware

Conclusion

Stacked Pull Requests bridge the gap between continuous feature delivery and rigorous code quality. By decoupling large architectural initiatives into smaller, rebaseable components, teams eliminate review bottlenecks, minimize complex merge conflicts, and radically accelerate shipping cadence. Incorporating stacking—whether via raw Git commands or automated tooling—is one of the most effective structural upgrades a modern software team can adopt.

#Git#Developer Velocity#Code Review#CI/CD#Software Architecture