Skip to content

Schema Extension and Override Check

Compatibility review gate — instantiates Controlled Inheritance Propagation

Gates a schema that extends or overrides a base, verifying the descendant stays substitutable for the base — and, when a base change would break that, requiring a migration and backfill plan.

A Schema Extension and Override Check runs whenever a descendant schema extends or overrides a base — adding fields, narrowing types, replacing defaults — and verifies the result is still a legal descendant: required base fields survive, overrides do not contradict base constraints, and data written under the base still validates under the extension. Its distinctive move among the siblings is enforcing substitutability at the data-contract level — a document valid against the base must remain acceptable where the base is expected — and, when a base change would break that, producing the migration and backfill plan that carries existing descendants across. It is the sibling that says "you may evolve the shared structure, but prove old data survives first."

Example

A platform defines a base Event schema — id, timestamp, type — consumed by dozens of downstream services. A team extends it into PurchaseEvent, adding amount and currency and narrowing type to a fixed enum. The check runs at merge time: it confirms the extension only adds optional or defaulted fields (so existing producers still emit valid events), that the type narrowing stays within the base's allowed values rather than contradicting them, and that every PurchaseEvent is still a valid Event for services reading only base fields — substitutability, in the Liskov sense, applied to data.

Later someone proposes making currency required on the base itself. The check flags it as breaking: historical rows have no currency. It refuses to pass until a migration and backfill plan exists — backfill a default for old rows, dual-write during transition, then flip the constraint. That refusal is the whole value: it turns "the schema evolved" from a silent break discovered in production into a governed step with a plan attached. The compatibility rules it enforces are the same ones formats like Avro and Protobuf codify for schema evolution.

How it works

  • Diff descendant against base. Enumerate added fields and every overridden type, default, or constraint.
  • Enforce the invariant boundary. Required base fields and locked constraints may not be removed or contradicted; only declared-open slots may change.
  • Run the substitutability test. Descendant instances must validate wherever the base is expected (and, under a full-compatibility policy, base instances against the descendant).
  • Classify and gate. A compatible change passes; a breaking one is blocked until a migration and backfill plan accompanies it.

Tuning parameters

  • Compatibility direction — backward, forward, or full. Backward lets new readers read old data; forward lets old readers read new data; full demands both and is strictest.
  • Override permissiveness — whether overrides may only narrow the base (safe) or also widen and replace it. Narrow-only preserves substitutability; freer overrides risk breaking base consumers.
  • Break policy — whether a breaking change is blocked outright or allowed with a mandatory migration plan and version bump. Strict blocking protects consumers; plan-gated evolution keeps the schema moving.
  • Enforcement point — design-time lint, merge-time CI gate, or runtime validation. Earlier is cheaper to fix; runtime catches what static analysis misses but fails late.

When it helps, and when it misleads

Its strength is making schema evolution safe by construction: an extension that would silently break a downstream consumer is caught before it ships, and an unavoidable break arrives with a plan to carry data across. The substitutability test is the Liskov substitution principle[1] enforced at the data layer rather than in code.

It misleads because the check sees only the constraints it is told about. Semantic incompatibility it cannot express — a field technically optional but functionally required by a downstream report — passes clean and lends false confidence. The classic misuse is relaxing the compatibility mode to "none" to shove a breaking change through a red gate, exporting the pain to consumers. The discipline that guards against it is to keep the compatibility contract explicit and versioned, and to treat a required migration plan as part of the change itself, not paperwork filed after it.

How it implements the components

  • invariant_vs_default_boundary — separates the required base fields and locked constraints a descendant may never touch from the open slots it may override.
  • substitutability_check — the test that a descendant instance stays valid wherever the base is expected; the gate's core assertion.
  • migration_and_backfill_plan — when a base change breaks compatibility, the plan to backfill, dual-write, and cut existing descendants across before the change lands.

It does not enumerate the catalog of variable options (inheritable_property_catalog — that's Platform Variant Option Model), resolve precedence among competing overrides (precedence_and_conflict_ruleTrait or Mixin Composition Rule), or own the lineage (lineage_stewardship_ownerPolicy Inheritance Matrix).

Notes

The check validates structural compatibility, not semantic: it proves old data still parses, not that downstream meaning survives. Because it must know which descendants a base change touches, it naturally consumes an impact trace rather than computing lineage itself — keeping the two mechanisms distinct.

References

[1] The Liskov substitution principle holds that an instance of a subtype must be usable anywhere the supertype is expected without breaking correctness. Applied to schemas, it becomes the rule that a document valid against a descendant must remain valid against the base it extends.