---
type: Axis
title: schema-evolution (migration-discipline axis)
description: The schema-evolution architecture axis — how the persisted shape is allowed to change over time (recreate → forward-only → reversible → online). A per-entity-with-default axis whose commitment surface is the persistent-data schema; worked example is the generate-migrations + rollback machinery (#778).
tags: [axis, architecture, schema-evolution, migration, rollback, rfc-75, deep]
timestamp: 2026-06-18T00:00:00Z
---

# Schema-evolution (migration-discipline axis)

> **DEEP axis (5A.2).** Full §6 depth with a worked example from the skill's own
> history — the document-mapping migration generator + rollback (#778, 4.3.0a4).

## Concept

Schema-evolution is the **migration-discipline** axis (RFC #75 §3): how the
persisted shape is *allowed to change* over time. The ladder runs recreate (drop
+ rebuild; data is lost) → forward-only (apply ordered migrations, never undo) →
reversible (every migration has a tested down-step) → online (migrate without
downtime — expand/contract, dual-write). Its **commitment surface is the
persistent-data schema** (§4): once data lives in a shape, changing the shape
costs a migration, and the *discipline* by which you migrate is itself a
commitment. Its **scope is per-entity-with-default** (§5): a system-wide default
migration discipline ("every schema change ships a numbered migration"), with
per-entity deviations (a high-churn entity may need online; an append-only log
may only ever forward-migrate).

## Why it is an axis (discriminator test — §2)

1. **Ordered tiers** — each rung adds safety/reversibility to the change path.
2. **Cross-cutting bundle** — a rung dictates the migration tool, the generator,
   the rollback path, the deploy procedure, and the migration tests.
3. **Asymmetric migration cost** — retrofitting reversibility/online onto a
   forward-only history is expensive; you cannot synthesize down-steps for
   migrations that already ran irreversibly.
4. **Requirement-keyed selection** — falsifiable probes pick the rung.

## Schema view (the §6 entry — validated by `axis_catalog`)

```yaml
id: schema-evolution
kind: architecture-axis
scope: per-entity-with-default
default_tier: forward-only
maintenance_discipline: >-
  every schema change ships an ordered, numbered migration applied exactly once
  and never edited after it has run anywhere; the generator EMITS migrations but
  never auto-applies them (#778 — generate, review, apply deliberately); a
  reversible-tier entity additionally ships a tested down-step. Per-entity
  deviations (online for a hot table; forward-only for an append log) live in the
  entity record, not the standing policy.
selection:
  probes:
    - "Can the store be wiped + rebuilt on every change (dev scratch / pure cache)? → recreate."
    - "Must existing data survive a shape change? → at least forward-only."
    - "Must a bad migration be undoable in production? → reversible (tested down-step)."
    - "Must the schema change without downtime / while readers are live? → online (expand/contract)."
    - "Is the change applied by a numbered, apply-once, never-edited-after-run migration? → forward-only discipline holds."
composes_with:
  - "migration complexity = schema-flexibility × schema-evolution"
  - "right-to-erasure = principal × schema-evolution"
applies_when: []
tiers:
  - tier: recreate
    commitment_delta:
      surface: persistent-data-schema
      delta: "no migration path; the store is dropped and rebuilt on every shape change. All persisted data is lost on change."
    admin_tool: "drop + rebuild script."
    enforcement:
      - "the store is treated as disposable; nothing durable depends on it."
    tests:
      - "rebuild produces a usable empty/seeded store."
    forecloses: "any durable data across a shape change."
    migration_cost_to_next: "low — introduce a numbered, ordered migration runner."
  - tier: forward-only
    commitment_delta:
      surface: persistent-data-schema
      delta: "ordered numbered migrations applied exactly once, never edited after running, never undone. Data survives changes; history of changes is the migration sequence."
    admin_tool: "migration runner + a generator that EMITS (never auto-applies) migrations from a declared mapping (#778)."
    enforcement:
      - "a schema change without a corresponding numbered migration is rejected."
      - "an already-run migration is immutable; fixes ship as a new forward migration."
    tests:
      - "migrating a populated store from version N to N+1 preserves data; re-running a migration is a no-op."
    forecloses: "in-place undo; a mistake is corrected only by a compensating forward migration."
    migration_cost_to_next: "medium — author + test a down-step for each migration."
  - tier: reversible
    commitment_delta:
      surface: persistent-data-schema
      delta: "every migration carries a tested down-step; a bad deploy can roll the schema (and compatible data) back."
    admin_tool: "rollback command exercising the down-step; migration-pair test harness."
    enforcement:
      - "no migration merges without a down-step that round-trips on a populated fixture."
    tests:
      - "up then down returns the schema (and round-trippable data) to the prior version."
    forecloses: "irreversible destructive changes within the reversible window (they must be staged)."
    migration_cost_to_next: "high — make the change safe while readers/writers are live (online)."
  - tier: online
    commitment_delta:
      surface: persistent-data-schema
      delta: "expand/contract migrations + dual-write windows let the shape change with no downtime and no reader/writer outage."
    admin_tool: "phased-rollout console (expand → backfill → switch → contract); dual-write toggle."
    enforcement:
      - "every change decomposes into additive-expand, backfill, cutover, contract phases; no phase breaks a live reader."
    tests:
      - "a reader/writer on the old shape and one on the new shape both succeed during the dual-write window."
    forecloses: "single-shot blocking DDL; every change becomes multi-phase."
    migration_cost_to_next: "n/a — top of the ladder."
examples:
  - "The skill's own climb: the document-mapping migration generator + rollback (#778, 4.3.0a4) — generate-migrations from a declared mapping, NEVER auto-apply (a separate deliberate step); the migration is reviewed then applied. Sits at forward-only with reversible aspirations; the canonical DB itself climbed v2→v8 (RFC #59) as ordered forward migrations applied to the live store on boot."
```

## Selection procedure (§8)

Bidirectional check over the ladder, per entity under the default discipline.
Recorded in the entity record; `architecture_drift_gap` flags a schema change
that landed without the migration the declared tier requires.

## Composition (§5)

Schema-evolution × schema-flexibility = **migration complexity** (a flexible
shape is harder to migrate safely); principal × schema-evolution =
**right-to-erasure** (a principal's data deletion is a migration-shaped event).
Rules authored in 5A.3.
