Skip to content
← Back to merge queue guide Merge queue feature

Two-step CI

Most teams already run CI on pull requests. A merge queue adds a second validation step that only runs once the PR enters the queue, against the actual state main will have at merge time.

flowchart LR
  subgraph PR_phase ["PR phase (cheap, fast)"]
    A["PR opened"] --> B["PR CI<br/>lint + unit tests"]
    B --> C["Review & approve"]
  end
  subgraph Queue_phase ["Queue phase (full, expensive)"]
    C --> D["Enter queue"]
    D --> E["Queue CI<br/>full suite + E2E"]
    E --> F["Merge ✓"]
  end

  style B fill:#F2F4F7,stroke:#d4d6d9,color:#1A1D24
  style E fill:#E6F8F2,stroke:#1CB893,color:#1A1D24
  style F fill:#E6F8F2,stroke:#1CB893,color:#1A1D24

Cheap checks on every PR. The expensive suite runs only when a PR is about to land.

Why two steps?

PR CI catches the obvious things fast. Lint errors, broken unit tests, type mismatches. The developer gets feedback within minutes and can iterate.

Queue CI answers a different question: will this change still work when it actually lands on main, alongside everything else queued ahead of it? That answer needs the full suite. Integration tests, end-to-end browser flows, infrastructure spin-up, the works.

Running both on every PR wastes CI time and money. Skipping the second step means semantic conflicts slip through. Two-step CI splits the work so each step does what it is good at.

Common configurations

PR CI Queue CI Why this works
Unit tests onlyFull suite + E2EFast PR feedback, thorough merge validation
Lint + type checkAll tests + E2ECatch formatting issues early, integration last
Affected tests onlyFull suiteScale PR CI for monorepos
Same as queueSame as PRSimple setup when CI is fast and cheap

Catching what PR CI misses

Take two PRs that both pass PR CI on their own. PR #1 adds a required parameter to a shared API endpoint. PR #2 calls that endpoint without the new parameter. Each PR was tested against an older main where neither change existed. Both go green.

When PR #1 merges first, PR #2 is now broken. Queue CI catches that the moment PR #2 enters the queue, because it runs against main + #1. The test fails before reaching main, the PR is removed, and the author gets a notification with what changed.

This is the kind of semantic conflict that no amount of PR CI can prevent. PR CI tells the developer "your change works in isolation." Queue CI answers "your change works in the world it is about to enter."

CI cost math

Two-step CI is also a cost optimization. The expensive checks (browser tests, load tests, full integration spin-up) only run on PRs that are actually about to merge.

Take a team with 50 PRs per week where 30 pass review and reach the queue:

  • Without two-step: 50 full CI runs per week.
  • With two-step: 50 lightweight PR CI runs + 30 full queue CI runs.

The savings compound when full CI involves spinning up databases, browser farms, or staging environments. For teams paying for CI by the minute, this can be a real line item.

Related features

See how to configure two-step CI in Mergify.

See two-step CI in production.

Mergify runs queue CI as a separate workflow on a queue branch. Your existing PR CI keeps running unchanged.