What is a Stacked PR?

A stacked pull request, also known as a stacked PR, stacked diff, or dependent PR, is a series of pull requests that are built sequentially on top of each other. Each pull request in the stack represents a small, logically isolated change. Imagine a chain where each link depends on the one before it. The first PR (PR-A) is based directly on the main branch. The second PR (PR-B) is based on the head of PR-A. The third PR (PR-C) is based on the head of PR-B, and so on. This creates a clear dependency chain: main ➔ A ➔ B ➔ C.

When you eventually merge this stack in the correct order (A → B → C), each individual change lands cleanly on the main branch. This approach offers significant advantages for code review and integration, allowing reviewers to focus on one cohesive piece of functionality at a time without being overwhelmed by unrelated changes or the complexity of a massive, monolithic PR.

Diagram illustrating the sequential dependency of stacked pull requests (PR-A, PR-B, PR-C) on a main branch.

Why Use Stacked PRs?

The primary motivation behind using stacked PRs is to improve the development workflow, particularly for large or complex features that naturally break down into smaller, dependent steps. This method tackles several common pain points in software development:

Streamlined Code Reviews

Large pull requests are often difficult and time-consuming to review thoroughly. Reviewers can miss critical bugs or logic errors when faced with hundreds or thousands of lines of changes. Stacked PRs break down a large change into smaller, digestible chunks. Each PR can be reviewed independently, focusing on a specific, self-contained modification. This makes the review process faster, more efficient, and less prone to errors. Reviewers can approve PR-A, then move on to PR-B, understanding that PR-B builds upon the already-approved changes in PR-A.

Easier Rollbacks and Debugging

When a bug is discovered in production, identifying and rolling back the specific commit responsible can be challenging, especially if the fix was part of a large, complex PR. With stacked PRs, each PR represents a distinct, isolated change. If a problem arises, you can pinpoint the exact PR that introduced it and roll it back with greater ease and confidence. This granular control significantly reduces the risk and impact of regressions.

Atomic Feature Development

Some features inherently require multiple steps. For example, refactoring a data model might need to happen before new API endpoints can be added, which in turn might require UI updates. Stacked PRs allow you to develop these features in a logical, step-by-step manner. PR-A could handle the data model changes, PR-B could introduce the new API endpoints that rely on the updated model, and PR-C could implement the UI components that consume the new API. This ensures that the codebase remains in a stable, deployable state at each step of the feature development, rather than having a large, incomplete feature block the main branch.

Continuous Integration and Deployment Benefits

Stacked PRs can integrate more smoothly with CI/CD pipelines. Because each PR is a small, self-contained change, it can be tested and verified independently. This means that even if PR-B is waiting for PR-A to be merged, PR-A can still be merged and deployed if it passes all checks. This allows for more frequent, smaller integrations and deployments, reducing the risk associated with large, infrequent merges.

Managing Dependencies

In complex projects, it's common for one piece of work to depend on another. Stacked PRs explicitly model these dependencies. PR-B cannot be meaningfully reviewed or merged until PR-A is merged and its changes are present. This forces developers to think about and manage these interdependencies proactively, leading to better architectural decisions and fewer integration headaches down the line.

How to Implement Stacked PRs

Implementing stacked PRs requires a clear understanding of Git branching and merging strategies. While GitHub doesn't have a built-in feature to enforce stacking, the workflow can be managed effectively using standard Git commands and team conventions.

Branching Strategy

The core idea is to create branches that are children of other feature branches, not directly of main. When you start working on PR-B, you don't branch from main; you branch from the latest commit of PR-A's branch. Similarly, for PR-C, you branch from PR-B's branch.

  1. Create feature-a branch from main. Make changes and push. This is PR-A.
  2. Create feature-b branch from feature-a. Make changes and push. This is PR-B.
  3. Create feature-c branch from feature-b. Make changes and push. This is PR-C.

You then open these as separate pull requests. PR-A targets main. PR-B targets feature-a. PR-C targets feature-b.

Merging Order

The critical step is merging the PRs in the correct sequence. You must merge PR-A first. Once PR-A is merged into main, you then update the base of PR-B to point to main (effectively rebasing it). After resolving any conflicts and passing checks, you merge PR-B. Then, you update the base of PR-C to point to the new head of main (which now includes PR-B) and merge it. This process ensures that each PR is always tested against the most up-to-date version of the codebase it logically depends on.

Tools and integrations can help automate parts of this process, such as ensuring PRs are based on the correct branches or automatically updating PR bases. However, the fundamental discipline lies with the development team.

Potential Challenges

While powerful, stacked PRs are not without their challenges. Developers need to be disciplined and understand Git well. The process of rebasing and updating PR bases can be complex, especially for less experienced team members. Conflicts can arise if multiple developers are working on overlapping parts of the stack, requiring careful coordination. Furthermore, the review process, while more focused, requires adherence to the merging order. Merging PR-C before PR-B, for instance, would break the chain and likely introduce integration issues.

The success of stacked PRs relies heavily on team communication and a shared understanding of the workflow. Clear documentation and training on how to manage these dependencies are essential for widespread adoption and effectiveness.

Conclusion

Stacked PRs offer a robust solution for managing complex code changes, improving review efficiency, and maintaining a stable codebase. By breaking down large features into smaller, dependent units, development teams can reduce the cognitive load on reviewers, simplify debugging and rollbacks, and enable more frequent, reliable integrations. While they require discipline and a good understanding of Git, the benefits in terms of code quality and development velocity make them a valuable workflow to adopt for many projects.