Skip to content

CONCEPT Cited by 2 sources

Schema three-way merge

Definition

Schema three-way merge is the schema-flavoured analog of Git's three-way merge: given a base schema (main) and two developer branches (branch1, branch2) that each propose a schema change, determine whether the two changes can be merged without conflict by computing the semantic SQL diff from main to each branch and testing whether the two diffs commute over composition.

The three branches give the mechanism its name. main is the merge base; branch1 and branch2 are the two sides of the merge. The conflict test operates on the two diffs, not on the three schemas directly.

Canonical verbatim framing (Noach, 2023 PlanetScale):

"You may be familiar with Git's three-way merge as a way to resolve source code changes made by developers on their independent branches. PlanetScale offers three-way merge for your schema branches, making schema change collaboration simpler and safer. It's similar in concept, but completely different in implementation." (Source: sources/2026-04-21-planetscale-database-branching-three-way-merge-for-schema-changes)

Why it exists

Concurrent developers on the same production database will submit overlapping or conflicting schema changes. Without an early check, a conflict is only detected at cutover time — after the migration has already run through the deploy queue and potentially waited hours. The three-way merge check runs at deploy-request submission, giving developers immediate feedback.

Git's three-way merge solves the same coordination problem for source code. The borrowed terminology ("merge-base", "three-way") makes the mental model portable from version-control fluency to database-schema ops.

The algorithm

Given:

  • main — the base (production) schema at branch-fork time.
  • branch1, branch2 — two concurrent development branches with proposed changes.

Compute the two semantic diffs:

  • diff1 = diff(main, branch1) — the function such that diff1(main) = branch1.
  • diff2 = diff(main, branch2) — similarly, diff2(main) = branch2.

Perform the commutativity check:

  1. Attempt diff1(diff2(main)). If invalid → conflict.
  2. Attempt diff2(diff1(main)). If invalid → conflict.
  3. If both are valid but diff1(diff2(main)) != diff2(diff1(main))conflict.
  4. If both are valid and equal → no conflict.

"If both are valid but diff1(diff2(main)) != diff2(diff1(main)), there is a conflict. If both are valid and diff1(diff2(main)) == diff2(diff1(main)), there is no conflict between the two branches." (Source: sources/2026-04-21-planetscale-database-branching-three-way-merge-for-schema-changes)

Worked examples

Non-conflicting (disjoint tables)

  • branch1: add column name to customer.
  • branch2: create new table delivery.

diff1 and diff2 touch disjoint entities; composition commutes; both orders yield identical schemas. No conflict.

Conflict (same column, different types)

  • branch1: add subscription_type enum('free', 'promotional', 'paid').
  • branch2: add subscription_type int unsigned NOT NULL DEFAULT 0.

diff1(diff2(main)) is invalid (can't add a column with the same name twice). Conflict.

Conflict (column order)

  • branch1: add subscription_type.
  • branch2: add joined_at.

Both diffs succeed in any order, but the resulting schemas differ in column position. diff1(diff2(main)) != diff2(diff1(main)). Conflict because SELECT * … positional semantics differ.

Non-conflicting (overlap)

  • branch1: add name to customer; add table tbl1.
  • branch2: add name to customer; add table tbl2.

Both branches agree on the name column. PlanetScale recognises the identical overlap and auto-adapts: whichever branch merges first causes the other branch's name add to drop out of its remaining diff set.

Distinction from Git

Aspect Git three-way merge Schema three-way merge
Diff unit Text lines SQL DDL statements
Conflict detector Textual line-overlap + content mismatch Diff composition commutativity
Output on success Merged file No-op — two diffs still run separately
Output on conflict Merge conflict markers Deploy-request rejection
Runs where Local workspace Deploy-request submission, server-side

The schema three-way merge does not synthesise a merged diff. It is purely an admission test: pass → both branches can be queued; fail → one branch must be edited or rebased.

Relationship to schemadiff

The actual computation is delegated to Vitess's schemadiff library, which already implements the semantic SQL diff + in-memory schema validation. The three-way merge is schemadiff invoked twice (once per branch against main), with the commutativity check layered on top of the two resulting diff sequences.

Policy layer on top of the mathematical check

PlanetScale's implementation is not a pure commutativity check. Two designed-in deviations:

  • Index ordering exemption. Two branches adding different indexes in different orders yield different SHOW CREATE TABLE output but identical query semantics. The post states: "PlanetScale disregards index ordering." The commutativity check is weakened to accept this case.
  • Identical-overlap auto-adaptation. Mechanical composition of two branches both adding the same column would fail at the second application. The policy layer recognises the identical sub-diff and short-circuits the second application. See concepts/diff-overlap-auto-adaptation.

Limitations

  • Structural only. The check catches DDL-level conflicts (column name collisions, order differences, index name collisions). It does not catch semantic application-level conflicts (adding a NOT NULL column that the application's write path doesn't yet populate).
  • Two-branch test. The presentation covers two concurrent branches. Generalisation to N > 2 concurrent deploy requests uses pairwise checks implicitly through the deploy queue + pairwise admission.
  • Intermediate branch state is ignored. Nothing is recorded between branch creation and deploy-request creation. The three-way merge operates purely on the branch's final schema vs main.

Seen in

  • sources/2026-04-21-planetscale-database-branching-three-way-merge-for-schema-changes — Shlomi Noach's canonical 2023 post introducing the mechanism. Frames the algorithm as "similar in concept, but completely different in implementation" to Git's three-way merge, grounds it in Vitess's schemadiff, and walks through the commutativity check with four worked examples (disjoint, name-collision, column-order, identical-overlap).
Last updated · 550 distilled / 1,221 read