Nicholas Zakas described the exact moment the old workflow dies in a 2026 post about developer velocity1. You work on a GitHub repo with a linear commit history, which means every pull request has to pass CI on top of the latest main before it merges. Your CI run takes ten minutes. You click "Update Branch," go get coffee, and when you come back the checks have passed. The "Update Branch" button is enabled again, because in those ten minutes someone else merged, so your branch is stale all over again. With a handful of open pull requests, this becomes a full-time babysitting job: update, wait, watch the button light up, start over.

This is the rebase storm, and it is the reason the merge queue stopped being a niche tool and became a first-class feature in every major platform. It is also a genuinely different story class from the infrastructure pieces we have covered here, from control-plane delivery to CI guardrails for AI-generated code. This one is about what happens in the few minutes between a pull request being approved and it landing on main, and why that window is where green builds go to die.

Every PR passes CI. Main still breaks.

Here is the uncomfortable fact that a busy team discovers around the same time it starts having merge fights: CI passing on every pull request is not the same as main staying green. The two only diverge under one specific condition, and it is the condition trunk-based development is built on, many small changes landing all the time.

The failure has a name. The Rust community calls it merge skew, and it is what the original merge bot was built to prevent2. Two changes are each correct against the branch they were tested on, but wrong against the combination. Faire's engineering team wrote the clearest example I have seen3. Developer A merges a change that uses a library called Alpha. Developer B merges a change that deletes library Alpha. Git is perfectly happy: neither commit touches the same file, so there is no merge conflict and both land cleanly. But once they sit on top of each other on main, the branch can no longer compile.

The kicker is that no one failed. Both developers did the right thing, reviewed the code, ran the full suite, and merged code that passed on its own. The failure only exists in the combination, and a branch that breaks this way tends to break everyone at once. Faire called these green-green conflicts, and as their team grew from about thirty engineers to over a hundred, they became a daily hazard3. At 2 to 5 engineers on a shared repo, a green pull request that breaks main is rare enough to fix by hand when it happens. It is not rare at scale.

The green-green conflict: two pull requests each pass CI in isolation, one adds a library the other deletes, and main stops compiling once both land because the failure only exists in their combination.
The green-green conflict: two pull requests each pass CI in isolation, one adds a library the other deletes, and main stops compiling once both land because the failure only exists in their combination.

What a merge queue actually does

The merge queue closes the gap between approved and landed. Instead of merging each pull request the instant it is ready, you add it to a queue. The queue combines the queued pull requests onto a temporary branch, one at a time, in order, on top of the latest main, and runs CI on each combination. A pull request only lands once it has passed CI sitting on top of everything that came before it in the queue1.

The idea predates the platforms by more than a decade. Graydon Hoare, the creator of Rust, built a bot named Bors in 2013 to enforce what Ben Elliston had called the "Not Rocket Science Rule" of software engineering: maintain a branch that always passes all the tests, and only promote a change to main after testing it first2. Bors merged a pull request into a temporary branch, ran the full suite, and only then fast-forwarded main to the tested commit. If the test failed, main stayed untouched. Rust's tooling evolved into Homu, and a parallel concept appeared across the industry in Shopify's Shipit, Palantir's Bulldozer, and later as a service in Mergify and Aviator. When GitHub shipped its native merge queue in 2023, the author of the open-source successor Bors-NG quietly deprecated it in favor of the official feature. A community hack had become a default2.

The mechanics are worth knowing because they are also the tuning knobs. On GitHub, adding a pull request to the queue triggers the creation of a temporary branch under the refs/heads/gh-readonly-queue/ prefix, and CI is run against that branch via a merge_group webhook event1. The queue batches up to five pull requests by default, testing each new one on top of the previous ones. If a pull request fails, it is evicted and the rest continue. Once a set passes, it merges to main in a single operation, in queue order. CI effectively runs twice per pull request: once on its own, once combined on the temporary branch1.

How a merge queue works: queued pull requests are combined one at a time on a temporary branch built on the latest main, each combination runs CI, a failing pull request is evicted, and only the tested set merges atomically.
How a merge queue works: queued pull requests are combined one at a time on a temporary branch built on the latest main, each combination runs CI, a failing pull request is evicted, and only the tested set merges atomically.

GitLab built the same idea with a different name. A merged results pipeline tests one merge request combined with the target branch, but it does not account for the other merge requests merging at the same time, so two merge requests can each pass and still break the target branch when both land. Merge trains go one step further: each merge request is tested against the other, earlier merge requests, and the pipelines run in parallel with redundant ones cancelled automatically4.

The 2026 data: when it stops being optional

The strongest argument for the merge queue, and the freshest numbers on it, come from Mergify's State of Merge Queues 2026 report, which pulled every pull request that went through its queue over 90 days and measured the subset that ran a real queue: roughly 153,000 merges across 160 teams5. The headline finding is a curve, not a single number. The share of green pull requests that break main scales about 16x with team size. At 2 to 5 engineers it happens roughly once in 130 merges. At 40 or more engineers it is one in eight, a 12.5 percent standing tax on every busy week5. The curve bends between the 6-to-15 and 16-to-40 engineer buckets, which is exactly where teams start feeling merge pain without being able to name it. The mechanism is arithmetic: more engineers means more pull requests in flight, and more chances that two individually correct changes touch the same assumption.

The same report answers a question most teams ask too late: does this apply to us? Private code breaks main about 4.5 times more often than open source, 5.1 percent against 1.1 percent5. The reason is structural, not a matter of discipline. Open-source contribution is mostly isolated work, one person touching one corner of a codebase. Private monorepos run the other way, with dozens of engineers editing interlocking code against a shared deadline, which is the exact condition that turns two green pull requests into a broken main.

The broken-main rate by team size and by comparison: it scales roughly 16x from small to large teams, reaching 12.5 percent at 40-plus engineers, while AI-assisted pull requests break main about half as often as human ones and private code breaks 4.5x more than open source.
The broken-main rate by team size and by comparison: it scales roughly 16x from small to large teams, reaching 12.5 percent at 40-plus engineers, while AI-assisted pull requests break main about half as often as human ones and private code breaks 4.5x more than open source.

The report also produced a result that runs against almost everyone's instinct, and it matters for how you gate AI-generated code. Pull requests written with AI assistance broke main at 1.9 percent, against 4.4 percent for human-authored ones5. The report authors tried to kill the result, because the common worry is that AI floods the branch with confident, subtly wrong code. AI-assisted pull requests were larger on average, 137 changed lines against 84, and the gap held in every size bucket, and AI came out lower in 21 of the 27 repositories where the rates differed5. It is observational data, not a controlled trial, and the report says so plainly. But the two easy explanations both fall apart under scrutiny, and there is no support in the data for the idea that AI is making main more fragile. The practical implication for a team that has added extra review friction to anything a model touched is uncomfortable: put that review attention where the failures actually are, on large human-authored changes in big teams, not on the AI-labeled ones5.

Setting one up is three steps and a few knobs

The setup on GitHub is short enough that the main risk is skipping a step1. First, add the merge_group trigger to the same workflow file that runs CI for pull requests, so the queue runs the same checks. As of this writing, checks_requested is the only merge_group type available. Scope any job that should not run on the temporary branch, such as a lint job that needs pull request metadata, with an if condition checking for the gh-readonly-queue branch. Second, enable squash merges, because the queue merges the temporary branch into main and each pull request should add exactly one commit to keep reverts clean. Third, enable the merge queue through a branch ruleset by checking "Require merge queue."

The defaults work for most teams, and the configurable settings are where you tune for your velocity1. Build concurrency controls how many queued pull requests run checks at the same time. Minimum group size defaults to 1, and you would only raise it in a high-velocity repository where you want to throttle temporary branch creation. Maximum group size defaults to 5, a balance between too many temporary branches and too high a chance a large batch fails. Wait time defaults to five minutes, the window the queue gives itself to hit the minimum group size before merging anyway. There is a harder decision buried in one setting: "require all queue entries to pass required checks," the default, tests every pull request on top of the ones before it, which catches the culprit precisely but costs more. Unchecking it tests only the head of the batch, which is faster and cheaper but makes failures harder to diagnose.

The cost of running CI twice per pull request is the one thing teams underestimate, and it is worth planning around3. Faire's answer was to run a streamlined subset of validations on the merge queue branch rather than the full suite, and to use parallel processing that routes each pull request behind only the ones that touch the same services, so unrelated changes do not queue up behind each other3. GitLab handles the redundancy differently, with automatic pipeline cancellation for merged result pipelines that no longer matter4.

The operating tail nobody prices

The most useful warning about merge queues, and the reason the buy-versus-build decision is more subtle than it looks, came from a Mergify post in August 20266. A basic merge queue is genuinely easy to build: take the pull requests in order, rebase each on the ones ahead of it, run CI, merge if green. With an LLM you can have that working in an afternoon. The problem is not the prototype. It is the operating tail that shows up the day it touches main. Batching and speculative parallelism, because a serial queue becomes the bottleneck as the team grows. Bisection, because the moment you batch, a red batch means figuring out which pull request broke it instead of punishing the whole batch. Flaky test quarantine, because one flaky test stalls the entire train and the queue cannot tell the failure was noise. Priorities, because a production hotfix needs to jump the line. The integrity invariant, that the commit CI tested has to be the commit that lands byte for byte; GitHub's own merge queue shipped a bug in this class and corrupted merges for four and a half hours6. Monitoring, and surviving GitHub itself, with its outages, rate limits, and late webhooks. Every one of those is a feature your team now maintains instead of shipping your product.

The merge queue operating tail: batching and parallelism, bisection, flaky test quarantine, priorities, the integrity invariant, and monitoring are all owned forever once you build, so build is a one-time line item while run is a payroll line of 10 to 20 percent of an engineer every month.
The merge queue operating tail: batching and parallelism, bisection, flaky test quarantine, priorities, the integrity invariant, and monitoring are all owned forever once you build, so build is a one-time line item while run is a payroll line of 10 to 20 percent of an engineer every month.

Faire, which runs its own merge queue at scale, lists the same tradeoffs from the other side3. A stuck queue is very detrimental, because a CI check that never arrives makes every downstream commit wait. Repositories with heavy delivery and no parallel processing get long queues and slower delivery. And a separate CI pipeline is an ongoing maintenance burden on the developer productivity team. Their honest framing is that a merge queue trades one set of problems for another, and the new set is far more manageable than a main branch that breaks on a weekly basis3.

What this means for teams shipping agent-generated code

The 2026 twist is that this is no longer just a human workflow. Agents and AI-assisted tooling produce pull requests faster and in larger volume than a team of humans does, which is precisely the condition that makes a merge queue stop being optional. The data cuts against the reflexive response. Teams that added extra review friction to anything a model touched are gating the wrong thing, because AI-assisted pull requests break main about half as often as human ones5. The queue, not extra friction on AI-labeled work, is the mechanism that keeps the branch green while volume climbs.

We run on this pattern internally. Every piece of content this pipeline produces, including this article, goes through a mechanical gate that rejects broken links, banned formatting, and uncited claims before it is staged, and a human editor reviews and publishes the result. Nothing auto-lands. That is the same deal a merge queue makes: the thing that ships is the thing that passed the checks, tested against the real state of what is already there. It is a small pipeline, not a busy monorepo, so we are fixing broken output by hand rather than running a queue. But the principle transfers directly to a client's CI system, and it transfers even more directly to a client's agent rollout. The question to ask is not whether to trust the agents. It is whether your merge path can absorb the volume agents will generate without breaking main every week. Below roughly fifteen engineers on one shared codebase, the answer can be no, and a queue is optional. Above it, the 12.5 percent figure is roughly how often a green pull request is quietly costing you a revert and a context switch5. That is the line where the rebase storm ends and the queue becomes the default.

Sources

  1. Nicholas C. Zakas, "Improving developer velocity with GitHub merge queue" (April 2026). humanwhocodes.com 2 3 4 5 6

  2. Julien Danjou, "The Origin Story of Merge Queues," Mergify (September 10, 2025). mergify.com 2 3

  3. George Jacob, "How merge queues helped us stabilize our main branch," Faire (The Craft), April 2, 2025. craft.faire.com 2 3 4 5 6

  4. GitLab Docs, "Merge trains." docs.gitlab.com 2

  5. Mergify, "State of Merge Queues 2026: AI-assisted PRs break main half as often as human ones" (July 27, 2026). mergify.com 2 3 4 5 6 7 8

  6. Julien Danjou, "Should you build your own merge queue?" Mergify (August 5, 2026). mergify.com 2