How to implement a merge queue for your project

Greg Foster
Greg Foster
Graphite software engineer
Try Graphite

Table of contents

Modern software teams often face the challenge of integrating many parallel code changes without breaking the main branch. A merge queue is a powerful solution to this problem. In this post, we’ll explain what merge queues are in simple terms, why they are especially important for monorepos, and how to implement one using GitHub’s native features and popular tools like Graphite, and Mergify. We’ll also compare self-hosted vs. SaaS options and discuss how to adopt a merge queue whether or not your team already has CI/CD in place.

A merge queue is essentially a mechanism that controls the order in which pull requests (PRs) are merged into your main branch. Instead of merging PRs directly as they’re approved, they enter a queue. The queue’s job is to sequentially (or in controlled batches) integrate each PR with the latest main branch code and run any required tests or checks before the actual merge. This way, only changes that pass all checks on the up-to-date codebase get merged, keeping the codebase stable. GitHub and GitLab have built-in solutions for this process (GitHub calls it Merge Queue and GitLab calls it Merge Trains), and there are also third-party services like Mergify that provide merge queue functionality. In short, a merge queue streamlines the merge process, reduces conflicts, and helps maintain a healthy, unbroken main branch in a busy repository.

Merge queues become more critical as your project and team grow – especially in a monorepo, where many projects or modules share a single repository. In a high-traffic monorepo with many developers, it’s common for multiple PRs to be ready at the same time. Without a merge queue, if two or more PRs merge in quick succession, the main branch can end up with undetected integration issues (often called semantic merge conflicts). For example, one PR might rename a function while another PR (based off an earlier state of the code) calls that function. Both PRs pass tests on their own, and GitHub will merge them without complaint – but afterwards the main branch has a broken call to a now-nonexistent function. A merge queue prevents this by catching such conflicts: it would update each PR with the latest main and run tests before finalizing the merge, so incompatible changes never land on main.

Monorepos can sometimes amplify these challenges. With many teams working in one codebase, you risk frequent overlapping changes and long CI times. A merge queue helps coordinate merges to avoid breaking one module when another module’s changes go in. It manages the merge order and dependencies between PRs so that changes in one part of the repo don’t conflict with changes in another. This reduces the “merge wars” or rebase races – developers no longer have to constantly rebase and race to merge first to avoid conflicts. The main branch stays green (buildable and passing tests), which means your trunk is always in a deployable state. In a monorepo, using a merge queue can also improve efficiency by batching related changes: for instance, some queue tools will merge dependent PRs together or run combined tests, saving time versus testing each PR serially. Overall, a merge queue gives teams confidence that even with many contributors, the main branch will remain stable and releases from it won’t be broken.

GitHub’s merge queue is a native feature that automates PR merges on busy branches to keep them from breaking. It became generally available in 2023 and can be enabled on organization repositories (including all public repos) via branch protection rules. Once enabled (by checking “Require merge queue” in your branch protection settings), authorized users can add a PR to the queue instead of merging it immediately. GitHub will then create a temporary test branch that combines the PR with the latest main (and any earlier queued PRs) to run your required status checks. Only if those checks pass will the PR actually be merged. This ensures the branch is never broken by incompatible changes, effectively giving the same safety as “require branches to be up-to-date” but without developers having to constantly rebase manually.

Using GitHub’s merge queue in practice means you should have a CI workflow configured to respond to GitHub’s merge queue events. In GitHub Actions, for example, you need to add the merge_group trigger to your workflow YAML so that when the queue creates a combined test branch, your tests run on it. (For third-party CI providers, they must be set to build branches with the special gh-readonly-queue/ prefix.) After setting up branch protection and CI integration, GitHub’s merge queue will handle the rest: you’ll see a queue list in the repository where pending PRs wait for their turn to merge in order. You can also configure the merge method (merge, squash, or rebase) for the queue merges and limit how many can run in parallel (GitHub allows some concurrency to speed things up). The built-in solution is convenient and integrated – if you’re on GitHub and meet the requirements, turning it on is straightforward and removes the need for external bots.

Graphite is a third-party developer workflow tool that provides an advanced merge queue, among other features. Graphite’s merge queue is unique in that it is stack-aware. In Graphite, developers often work with stacked pull requests (a series of dependent PRs). The Graphite merge queue understands these relationships and can optimize merges accordingly. For example, if you enqueue a stack of PRs together, Graphite can run CI for the entire stack in parallel and fast-forward merge them in one go once they’re all verified. This means no redundant re-testing of each PR in the stack – a big speed improvement, as the same combined changes won’t be tested repeatedly. In other words, Graphite can sometimes merge multiple PRs faster than a strict one-by-one queue, while still preventing breaks. (It achieves this by performing what’s essentially an optimized batch merge, or a “fast-forward merge” for stacks.)

Graphite integrates with GitHub via a GitHub App and CLI. To use it, you’d disable GitHub’s own merge queue (they are not compatible together) and enforce Graphite’s queue via branch protections. When a PR is added to the Graphite queue, the system automatically rebases it on the latest trunk (main) and runs the required checks. If everything passes, Graphite merges the PR on GitHub’s behalf. If a problem is found (conflict or failing test), the PR is removed from the queue and not merged, with a comment left on the GitHub PR about what happened. Graphite’s queue also offers extra capabilities like reordering PRs, parallelizing where safe, and even pausing the queue or handling hotfixes out-of-order. Teams using Graphite appreciate the faster workflow – especially those working with monorepo and stacked changes – because it keeps the trunk branch green and merges flowing quickly without manual intervention.

Mergify is a SaaS tool that adds powerful automation on top of GitHub. It’s well-known for automating PR merges through user-defined rules, and one of its core features is a merge queue. Mergify’s merge queue works by checking that each PR meets all the conditions you specify (required CI statuses, approvals, etc.) before allowing it to merge. It places approved PRs into a queue and manages them so that your main branch stays stable and free of broken code. Essentially, Mergify acts like a smart bot that enforces your project’s policies and only merges PRs from the queue when it’s safe to do so. This eliminates the need for developers to babysit merges or constantly update their branches.

One advantage of Mergify is its flexibility and support for complex setups, which is great for monorepos. You can define queue rules in a config file (YAML) to handle different scenarios. For example, you might have multiple queues or partitions for different parts of a monorepo (frontend vs backend, or by project directory). Mergify supports partitioning: a single PR can be placed into multiple queues (e.g., it touches Project A and Project B subfolders) and will only merge when it has passed the criteria for all those queues. This ensures that changes affecting multiple projects in a monorepo don’t land until they’re validated in each context. You can also set priorities (so urgent fixes merge before low-priority changes) and even batch multiple PRs together in one merge if desired. Mergify integrates with your existing CI – it watches for the status checks you require.

There are a variety of merge queue solutions, and one consideration is whether to go with a self-hosted approach or a SaaS product. Self-hosted options require you to run and maintain a bot/service. This gives you full control and usually no per-seat cost, but you’re responsible for updates, reliability, and scaling. In contrast, SaaS solutions like Mergify or Graphite handle the heavy lifting on their servers – you simply integrate them with your repo. SaaS tools tend to be easier to set up initially (often just a few clicks or config lines) and they roll out new features or fixes automatically. The trade-off is that they might cost money for private repositories or larger teams, and you have to grant a third-party service access to your code and PRs. In terms of capabilities, both self-hosted and SaaS queues achieve the same end goal of serialized, tested merges, but enterprise-focused SaaS products may offer more advanced features (priorities, multiple queues, dashboards, etc.) out of the box. Self-hosted solutions might require more manual configuration to achieve similar sophistication, or you might go without some bells and whistles.

When choosing between these options, consider your team’s needs and constraints: If you value a turnkey solution and are willing to pay or manage another subscription, a SaaS like Mergify or an integrated platform feature like GitHub Merge Queue is convenient. If you have an open-source project or prefer not to rely on external services, a self-hosted bot gives you independence. Some teams even start with a hosted service and later migrate to self-hosting (or vice versa) as their scale and requirements evolve.

Implementing a merge queue in your project requires some planning around your CI/CD pipeline. In fact, a merge queue and continuous integration go hand-in-hand: the queue will use your CI results to decide if a PR is safe to merge. If your team already has CI/CD set up (for example, running tests and linters on each PR), adopting a merge queue is usually straightforward. You’ll configure the queue tool to wait for the CI status checks to succeed on the queue’s test branch. For GitHub’s built-in merge queue, this means updating your GitHub Actions workflows (or other CI providers) to trigger on the merge queue event branches. For Mergify or Graphite, it typically means those services will monitor the same required checks you already have in branch protection – as long as your checks are passing, the queue will proceed. In all cases, having a robust suite of automated tests is what gives the merge queue confidence to merge a PR.

What if you don’t have a CI/CD pipeline yet? You can still use a merge queue, but its benefits will be limited without automated tests. A merge queue could still enforce an order on merges (so only one PR merges at a time, avoiding pure concurrency issues) and could require code reviews or other conditions. This might help reduce human error (for instance, it prevents two people from manually merging incompatible PRs at the same time). However, without CI, the queue isn’t verifying that the software actually works with the new changes – it’s mainly acting as a gate to serialize merges. If you’re in this situation (perhaps a very new project or a small team just setting up processes), you might start by using a simple queue to avoid overlap, but it’s highly recommended to introduce at least basic CI checks (even just a build or smoke test) as soon as possible. Most merge queue tools allow you to specify “conditions” for merging; you could start with conditions like “at least one approval review” or specific labels, and later add actual test results as conditions when CI is in place.

In summary, a merge queue can be introduced at any stage of your CI/CD maturity, but it truly shines when combined with automated tests. Teams with full CI/CD will find that a merge queue supercharges their workflow – no more broken main builds, less waiting around to rebase, and more confidence in shipping code. Teams without much CI yet can use a merge queue to improve discipline and gradually build up testing around it. In either case, implementing a merge queue (via GitHub’s feature or tools like Graphite or Mergify) is a step toward more reliable and efficient code integration. By adopting one, you make sure that “green means green” – the main branch stays green, and developers spend less time firefighting merges and more time building features.

Adopting a merge queue is a transformative step for any development team, especially those working in fast-paced or monorepo environments. By automating the process of serializing, validating, and merging pull requests, merge queues dramatically reduce the risk of integration conflicts and broken main branches. Whether you choose a built-in solution like GitHub Merge Queue, a flexible SaaS like Mergify, or an advanced workflow tool like Graphite, the result is a more stable, efficient, and confident release process. Investing in a merge queue not only keeps your codebase healthy but also empowers your team to focus on building features rather than fixing broken builds. As your project grows, a merge queue becomes an essential part of maintaining velocity and quality in your software delivery pipeline.

Built for the world's fastest engineering teams, now available for everyone