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 thatdiff1(main) = branch1.diff2 = diff(main, branch2)— similarly,diff2(main) = branch2.
Perform the commutativity check:
- Attempt
diff1(diff2(main)). If invalid → conflict. - Attempt
diff2(diff1(main)). If invalid → conflict. - If both are valid but
diff1(diff2(main)) != diff2(diff1(main))→ conflict. - 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 anddiff1(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 columnnametocustomer.branch2: create new tabledelivery.
diff1 and diff2 touch disjoint entities; composition
commutes; both orders yield identical schemas. No
conflict.
Conflict (same column, different types)¶
branch1: addsubscription_type enum('free', 'promotional', 'paid').branch2: addsubscription_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: addsubscription_type.branch2: addjoined_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: addnametocustomer; add tabletbl1.branch2: addnametocustomer; add tabletbl2.
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 TABLEoutput 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 NULLcolumn that the application's write path doesn't yet populate). - Two-branch test. The presentation covers two
concurrent branches. Generalisation to
N > 2concurrent 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).
Related¶
- concepts/semantic-schema-diff
- concepts/diff-commutativity-check
- concepts/column-order-conflict
- concepts/diff-overlap-auto-adaptation
- concepts/queue-admission-conflict-warning
- concepts/database-branching
- concepts/deploy-request
- concepts/schema-change-queue
- concepts/pre-flight-schema-conflict-check
- patterns/three-way-merge-for-schema-changes
- patterns/early-queue-conflict-warning
- systems/vitess-schemadiff
- systems/planetscale
-
sources/2026-04-21-planetscale-the-challenges-of-supporting-foreign-key-constraints — FK-aware composition altitude. The 2023-12-05 Noach
- Gupta post canonicalises that the semantic-diff computation underlying three-way merge extends naturally to FK topology: the schemadiff engine that powers three-way merge is the same one that computes the FK-ordered migration path in the deploy request (parent before child; index before FK; cannot-start-until-parent-completes concurrency blocks). The mechanism is unified — detecting conflicts between two branches and computing a valid linear order through one branch's diffs share the same dependency-analysis substrate in schemadiff. First wiki datum that three-way merge's scope includes FK-topology conflicts, not just column-level differences.