What are stacked pull requests

Stacked pull requests (or merge requests) are pull requests are based on other pull requests, rather than being based off a long-running branch like main or develop.

Why use stacked pull requests

Using a stacked pull request workflow has some advantages. It allows large changes to be separated into several smaller changes. Because each pull request is smaller it can also result in more thorough code review and more comprehensive descriptions. Depending on the team, it can also help reduce the time it takes to get a change merged, as changes can be approved and merged incrementally rather than as one large all-or-nothing change.

Separating behavioral changes from structural changes

In a 2020 Railsconf talk, Kent Beck comments that as developers become more experienced they start to realise the benefit of separating structural changes from behavioral changes.


It is not uncommon that a behavioral change, even a seemingly superficial one, can often lead to structural code changes. Put another way, when implementing a new feature a developer may want to refactor the existing code to make the new implementation simpler, cleaner, or easier to maintain.


Stacked pull requests allow the developer to continue working on the next set of changes while waiting for the previous change to be reviewed. Because the changes are more isolated they could also be reviewed by different people, say if the feature requires both backend and frontend changes.

Drawbacks

Stacked pull requests are not perfect. For one thing, if the "bottom" PR in the stack requires changes it could affect every other pull request in the stack, meaning either a chain of rebases or merges might be required. It should be said, however, that if a PR review results in large or architectural changes this could be a symptom of poor communication amongst the team during development and any feedback that would cause large changes should be sought early in the development process.

How to create stacked pull requests on Github

Creating stacked pull requests on Github is just a matter of selecting an existing PR branch as the 'base' when creating the pull request. When the "bottom" PR of the stack is merged, Github will automatically update the base when the 'bottom' pull request is merged.

Preventing accidental merges

Unfortunately, Github does not indicate when you are looking at a stacked pull request. This means it's quite possible for someone to inadvertently merge a PR higher in the stack before the bottom one is merged in. There are a few ways to prevent this - You could mark the higher PRs as 'draft', but then reviewers cannot mark it as approved. You could add a do not merge label, but the reviewer may not notice it. This is where GitArborist comes in:


Simply add @GitArborist depends on #123 in the PR description, where #123 is the number of the PR below it in the stack, and GitArborist will add a pending status showing when #123 has been merged right by your CI status. You can also use @GitArborist merge after #123 to automatically merge this PR when #123 is merged, and this can be chained across the stack so all approved PRs are merged immediately when the 'bottom' one is merged.

Conclusion

Using stacked pull requests encourages developers to separate their changes into smaller, more isolated pieces. A natural separating point also becomes behavioral/structural, which is good engineering practice. The smaller PRs also often results in more thorough code review as it is easier for reviewers to understand the changes being made and any ramifications.