Stability — DSDS 0.21.2
The DSDS compatibility policy: what counts as breaking, version semantics, the deprecation window, what's safe to build tooling around, and the criteria for declaring 1.0.
DSDS is a pre-1.0 draft, maintained by one person, not a working group. Some parts of the schema can grow to cover new cases without a spec change; other parts are locked in. This page is the compatibility contract behind that status — what can change, how you'll be told, and what never changes regardless. For the rules themselves and how each is enforced, see Conformance.
#
What counts as a breaking changeA change is breaking if a document, tool, or workflow that worked before it ships stops working (or starts failing validation) after — not just a rename or removal. Concretely, all of the following count as breaking, even when they look like tightening rather than deleting:
- Renaming, removing, or restructuring a field.
- Adding a new required field to something that already exists.
- Narrowing an existing constraint — an enum losing a value, a pattern getting stricter, a type narrowing.
- Adding a new validator rule to
scripts/validate/validate.js 'sDSDS-01 –DSDS-11 catalog. A document that validated cleanly yesterday can fail today with no schema change at all, because the new rule now runs against it. This is easy to overlook precisely because no field moved — treat a new conformance rule with the same weight as a schema change, because it has the same effect on existing documents. - Any change to what
npm run check accepts that a previously-passing document, run today, would fail.
Not breaking: new optional fields, new union members, relaxed constraints, new advisory (
#
Version semantics| Change | Version bump | Your documents |
|---|---|---|
| Documentation-only edits | none | Unaffected |
| Additions and loosenings (new optional fields, new union members, relaxed constraints, new advisory lint rules) | patch | Remain valid unchanged |
| Tightenings and behavior changes (new constraints, new required fields, moved fields, a new validator rule) | minor | May need edits; the CHANGELOG lists every affected position |
| Renames and removals | batched minor, pre-1.0 (major, post-1.0) | Migration script provided ( |
Pre-1.0, a minor release can carry a tightening or a breaking change with no deprecation window first — that is what "pre-1.0" commits to here, and it's why the table above says "minor" rather than "major" for a rename. Read the CHANGELOG before upgrading a minor version, not just a major one. Post-1.0, tightenings and removals wait for a major version and follow the deprecation window below; see "The post-1.0 contract."
#
Deprecation policyFrom the point a form is scheduled for removal, DSDS commits to:
- Marking it deprecated in the schema — the standard JSON Schema
deprecated: true annotation where the schema allows it, a$comment explaining the removal where it doesn't (e.g. a whole definition being replaced). - Naming its replacement in the same schema description or
$comment — never just "removed," always "removed, use X instead." - Flagging it via
lint-docs.js (a warning, never a build failure) so it shows up in CI before it disappears. - Keeping it valid for at least one further minor release after the deprecation notice ships, before the removal that breaks it.
This is a process commitment, not yet a schema-level machine-readable one — a consumer currently learns "deprecated, replaced by X" from the annotation and description text, not a dedicated
#
Breaking-change windowsPre-1.0 breaking changes land batched into a named release, not trickled one field at a time across several minors — a single CHANGELOG entry and migration script to read, not several. When a breaking window is being prepared, it's announced on this page (what's changing, and the release it lands in) before that release ships, the same practice earlier 0.15.x releases followed. Each window ships with its own migration script, following
0.21.0 was a breaking window, and it is now closed. It changed one shape — a component trait — and it changed it twice:
#
How schema changes get madeEvery schema file under
#
Migrating a 0.15.2 documentAnything the script can't place in a typed field is preserved under the migrated item's own
#
Migrating a 0.20.x documentA document already in the 0.20 YAML shape takes a different script:
#
Designed to grow without a version bumpA handful of fields accept any string matching a pattern, instead of a fixed list of allowed values — so adding a new value never needs a schema change. Safe to build tooling around, but don't treat today's set as complete:
entries/token.tokenType — a token's category (color ,spacing ,typography , …), checked by pattern, not a fixed list.common/ref.rel — a reference's relationship (depends-on ,same-as ,implements , …), open-ended; new values just get documented in the schema's own comments.metadata.status 's status value —stable ,experimental ,deprecated , and more, open for the same reason.entry.id andcommon/id — a lowercase-dash-dot pattern, not a fixed list of parts, so ids fit whatever hierarchy your system uses.$extensions — vendor or tool-specific data, grouped by namespace; a tool integration can add a field of its own any time, without waiting on a release.
#
More likely to require a spec changeA few fields are locked to a fixed list of values, because the number of possible cases is a fact about how the spec itself works, not an open-ended vocabulary. Be defensive about this list, not the one above:
entry.kind — five well-known values (system ,component ,token ,theme ,entry ), four with their ownentries/ file, plus a custom dot-separated kind name (e.g..schema.yaml acme.icon-library ) for a document that wants its own recognizable name. Adding a well-known value changes what this spec can describe at all, so that bar stays high.common/requirement-level — five values (must ,should ,should-not ,must-not ,may ), taken directly from RFC 2119. This vocabulary belongs to that standard, not DSDS.sections/* (the four section kinds) —guidelines ,definitions ,steps ,section . Any entry kind can use any of these.section is the generic fallback, the same roleentry plays for entries;freeform isn't a section kind at all, it's a field every section kind can carry. The part of the schema most likely to still change before 1.0.
#
Criteria for declaring 1.01.0 is declared when, at minimum:
- The kind lists stop changing — across at least one real pass of merging or splitting them, with no further changes needed.
- A second independent tool exists — at least one tool the spec authors don't maintain reads or writes DSDS documents for real.
- The validator's extra rules stop changing —
scripts/validate/validate.js 'sDSDS-01 –DSDS-11 (see the rule catalog) stop being added or renamed release to release.
Until then, the fixed lists above are the most stable part of the schema. Everything else can still change between minor versions, including the exact fields in any one
#
Released versions never changeWhatever else moves, a released version directory doesn't. Every version DSDS has shipped stays served at its own URL —
Released means tagged. A version is frozen from the moment its
Three mechanisms keep this honest, and none of them is a promise you have to take on faith.