Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Reviews a change to a table, event, or message schema for backward compatibility with the downstream consumers that read it — enforcing additive-only-by-default, optional/defaulted new fields, no in-place rename, no type or domain narrowing, no reuse of a retired field identifier, deprecate-then-remove over a window, and an explicit versioned contract with a chosen compatibility mode. Use when someone is evolving a shared data or event schema that other services, jobs, or teams consume and asks whether the change will break readers. Do NOT use for the production-DDL lock, downtime, and rollback safety of running a migration (that is migration-safety-review), for classifying a function-signature or REST-endpoint diff and its semver bump (that is api-compatibility-classifier), or for designing a brand-new schema with no existing consumers.
.claude/skills/schema-evolution-compat/SKILL.md| Model | Eval pass | Runs |
|---|---|---|
| gemini-3.6-flash | 97% | 35 |
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | — | — |
| case-04 | ✗→✓ | ▲ Improved | — | — |
| case-05 | ✗→✓ | ▲ Improved | — | — |
| case-09 | ✗→✓ | ▲ Improved | — | — |
| case-13 | ✗→✓ | ▲ Improved | — | — |
Reviews a proposed change to a schema that other people's code already reads — a warehouse table queried by dashboards, an event on a shared topic, a message format between services, a dataset in a lake. The goal is a change that ships without breaking any existing consumer, and that lets each consumer upgrade on its own schedule instead of being forced to redeploy in lockstep with the producer.
A schema is a contract, not private state. Once anything else reads it, its shape is a promise. The base model knows compatibility rules but does not apply them by default: asked to "add a field" or "rename a column" it tends to hand back the direct one-step change. This skill makes the compatibility checks mandatory before the change ships.
This is about the reader-vs-writer contract, not the database operation. Table locks, downtime, and rollback safety of the migration itself are a separate review; so is semver on a function or REST signature. Here the only question is: does an existing consumer break?
Prefer changes that only add, and make every added field optional — nullable, or carrying a non-volatile default. An optional added field is the one change that is safe in every direction: old consumers ignore it, old producers omit it, old records simply lack it. Everything else needs the checks below.
Run the change through all of these. A "no" is a finding.
required field is breaking: old producers won't populate it and old records don't have it, so any reader validating against the new schema fails on existing data. Making an existing optional field required is the same break. Fix: add it optional; require it only after every producer emits it and history is backfilled.
name changes, all readers of the old name break. Never rename in place. Add the new name alongside, populate both (dual-write), move readers over, then retire the old name later.
readers. Narrowing includes: shrinking a type (string→int, long→int), making a nullable field non-null, removing an allowed enum value, tightening a length/range/precision, and splitting one field into several. Same type but changed meaning or units is also a silent break — dollars→cents, local-time→UTC, an id that starts pointing at a different entity. Treat a meaning change as a new field, not an edit to the old one.
semantics. In Protobuf, never reuse a field number (or tag) — reserved it so no one can. In Avro, field name and position carry meaning; don't repoint them. In JSON or columnar data, don't give an old key a new meaning. A stale consumer will read new data into the old field and be silently wrong.
writing it — a consumer may still read it. Mark it deprecated, keep emitting/keeping it, confirm from telemetry that no consumer reads it across a communicated window, then remove. Consumers upgrade on their timeline; a same-day drop breaks the slow ones.
explicit version — an envelope schemaVersion, a versioned topic/subject, a registry entry — and a declared compatibility mode the registry enforces (below). Changing a shape with no version and no mode is how a silent break ships.
ignore unknown fields instead of failing closed. If a consumer crashes on a new field, that is the consumer's bug (fix it to skip unknowns) — but until every consumer reads tolerantly, treat even additive changes as risky and stage them.
The same change is safe or breaking depending on who upgrades first. Name the direction.
| Change | Who breaks | Safe rollout | |---|---|---| | Add optional field | no one | ship in any order | | Add required field | old producers + old data on a strict reader | backfill + upgrade all producers first, then require | | Remove a field | consumers still reading it | stop all consumers reading it first, then remove (deprecate-then-remove) | | Rename | every reader of the old name, immediately | add-new + dual-write + migrate readers + drop-old | | Narrow type / domain | consumers relying on the old range | add a new field with the new type; deprecate the old | | Change meaning/units, same type | every consumer, silently | new field; never edit in place |
A registry compatibility mode is a promise about upgrade order. Pick it from who upgrades independently, then only allow the changes it permits.
upgrade after producers. Allows: add optional field, remove a field. Default for a stream where consumers lag.
upgrade first, consumers later. Allows: add a field, remove an optional field.
contract with many independent consumers.
For a shared topic with consumers you don't control, default to BACKWARD or FULL and reject anything the mode forbids.
Exact per-format rules — Protobuf field-number and reserved discipline, Avro default and type-promotion requirements, JSON-Schema required/additionalProperties, Parquet/columnar add-at-end and by-name reads — are catalogued in references/format-evolution-rules.md.
Present findings as a table, then a verdict.
| # | Severity | Check failed | Finding | Safe alternative | |---|----------|--------------|---------|------------------| | 1 | 🔴 Block | Rename | In-place column rename breaks every dashboard selecting the old name at once | Add new column, dual-write, migrate readers, drop old later | | 2 | 🟡 Warn | Deprecation window | Field dropped the same week writes stopped; a reader may remain | Deprecate, confirm no reads over a window, then remove |
Verdict: COMPATIBLE / STAGE FIRST / BREAKING — one line naming the single biggest reason.
doesn't apply — change it freely. Don't over-stage a schema nobody else consumes.
default/unknown branch (tolerant reader). Otherwise the new value breaks an exhaustive switch — gate on how consumers handle unknowns.
data, but a producer writing long breaks an int-schema reader. Check the direction, not just "wider."
sides atomically, some staging collapses — but a change to a shared/streamed contract still needs the full discipline because replayed history and lagging consumers persist.
A good execution of this skill should:
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.5-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 14 cases were attempted. The headline lift of +57 percentage points is the difference between those two pass rates over the 14 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.