Skip to content

CONCEPT Cited by 1 source

Semantic merge conflict

Definition

A semantic merge conflict is the failure mode where two pull requests each pass branch-level CI cleanly in isolation but, when both merge into the same target branch concurrently, the combined result fails to build or fails tests on that branch — because their logical effects collide even though git's textual merge succeeded.

Distinct from a textual merge conflict, which git refuses to merge automatically. A semantic merge conflict does merge cleanly at the git level; the breakage manifests only at build / test / runtime.

"Pull requests passed branch CI and merged cleanly, yet collided logically on main when combined with other changes." (Source: sources/2026-04-29-atlassian-inside-atlassians-merge-queues)

The canonical example

  • PR-A renames function foo()foo_v2() across its own changes.
  • PR-B introduces a new caller of foo() in a different part of the codebase.
  • Each PR runs branch CI against the current main (which still has foo()) and passes.
  • Both PRs merge into main in short succession. Neither merge triggers a textual git conflict — the changes don't overlap at the line level.
  • The first post-merge CI run on main fails to compile because PR-B is calling a function PR-A just renamed.

The PR-level branch CI could not have caught this: it didn't know about the other PR's changes that hadn't landed yet.

Why branch-level CI can't catch this

Branch-level CI answers one question:

"Does this PR work on its own (against the current main)?"

The load-bearing question at merge time is different:

"Does this PR still work alongside everything else about to land?"

At scale (e.g. Jira: 800+ developers, 300+ merges/day) the combinatorics of concurrent green merges makes the two questions materially different. Atlassian names this precisely:

"The core gap: where validation ran versus where risk lived." (Source: sources/2026-04-29-atlassian-inside-atlassians-merge-queues)

Operational cost (measured on Jira)

When semantic merge conflicts went unmitigated on the Jira repo (pre-merge-queue):

  • 7–10% of CI failures on main were attributable to semantic merge conflicts.
  • 3–5 internal incidents per week caused by semantic merge conflicts.
  • Reactive recovery overhead: on-call and CI engineers reverted PRs, reran pipelines, hacked rollbacks until main came back green.
  • Human coordination overhead: engineers waited for "quiet windows", rebased and retried manually, coordinated merges in chat.

Post-merge-queue on the same repo:

  • Semantic-merge-conflict CI failures: near zero.
  • Incidents: rare edge cases only.

(Source: sources/2026-04-29-atlassian-inside-atlassians-merge-queues)

The canonical defence: merge queue

The architectural defence is a merge queue: queue accepted PRs, materialise a temporary branch that merges everything ahead of the PR into the target branch, run a dedicated pipeline against that future state, and merge only on green. This turns the merge-time question into the CI-time question, which is the load-bearing reframing in patterns/validate-against-future-state-of-main.

See systems/bitbucket-merge-queues for the Atlassian first-party implementation; peers include GitHub Merge Queue, Bors-NG, Graphite, and Rust's bors/homu.

Adjacent failure modes (same family)

Semantic merge conflict is one instance of a broader family — "your change looks fine in isolation but breaks when combined with other concurrent changes":

  • Database schema + code mismatch during non-atomic rollout — the defence is patterns/expand-migrate-contract + shadow-table OSC so that old code can run against the new schema and vice versa.
  • Library API changes + transitive consumers in a monorepo — the defence is an atomic cross-consumer change; see concepts/monorepo for the why.
  • Independent-PR API incompatibility — the exact scenario this page names; the defence is a merge queue.

The common thread: CI that validates each change against the current state can miss failures that only arise when changes are composed.

Seen in

Last updated · 433 distilled / 1,256 read