In a design system released as one semantically versioned package, how can experimental components keep changing without breaking the package's stability promise?
answer
- what the version number covers
- semver needs a declared public API
- keep experiments outside that declaration
- make the exclusion visible where used
- promotion adds to the public API
basics
~20 sKeep experimental components outside the declared public API, since semantic versioning's guarantees cover only that API. Make the exclusion visible at the point of use - a separate entry point or naming prefix - and document that they may change.
solid answer
~50 sSemantic versioning requires the software to **declare a public API**, in code or in documentation, and its MAJOR.MINOR.PATCH rules apply to that API. So a design system can publish experimental components in the same package while declaring them **outside** the public API. The exclusion has to be visible where consumers meet it: typically a **separate entry point** or namespace for experimental components, or a **naming prefix**, backed by documentation. Then they can change in any release without a major bump. **Promotion** to stable adds the component to the public API, which is new backward-compatible functionality and so a minor release. At the other end, marking stable functionality deprecated requires a minor release, and removing it requires a major one. The alternative is versioning experiments as a separate package on a 0.y.z line, which is clearer but adds release overhead.
go deeper
Recall that semantic versioning's promises apply to a declared public API, so experimental components can be kept outside it and allowed to change.
Explain the ways to express the exclusion, and map promotion, deprecation and removal onto minor and major releases with the reasoning for each.
Show how you would choose between an entry point, a prefix and a separate package for a real system, including transition paths on promotion and how beta is treated.
Weigh the organisation's appetite for release overhead and consumer friction against how clearly stability must be communicated across many teams.
## The tension A design system's coded components are usually shipped in a **versioned package** that follows **semantic versioning** (semver): version numbers of the form MAJOR.MINOR.PATCH, where a **major** bump signals backward-incompatible changes, a **minor** bump backward-compatible new functionality, and a **patch** bump backward-compatible fixes. The system also has **maturity stages**. Experimental components are supposed to change freely. If they sit in the same package under the same promise, every experimental change is technically a breaking change, and the system faces a bad choice: bump the major version constantly, which makes majors meaningless and scares consumers off upgrading, or break semver silently, which destroys trust. ## What semver actually binds Semver 2.0.0 says software using it **must declare a public API**, either in the code or strictly in documentation, and that the declaration should be precise and comprehensive. The versioning rules are defined relative to that public API. Anything not declared public is not covered by the compatibility promise. That gives the design system its lever: **declare experimental components outside the public API**, and they may change in any release. ## Ways to express the exclusion The exclusion only works if consumers can see it **at the point of use**. Documentation alone is weak, because people copy usage from other screens without re-reading docs. | Approach | How it works | Strength | Cost | |---|---|---|---| | Separate entry point or namespace | experimental components are reached through a distinct, clearly named path | visible in every usage; easy to scan for | consumers change their usage on promotion | | Naming prefix | experimental components carry a marker in their name | visible in every usage and in design files | the name changes on promotion | | Documentation only | the public API declaration lists stable components; experimental ones are excluded in prose | no usage change on promotion | easy to miss; relies on reading | | Separate package | experiments ship in their own package, often on a 0.y.z version line | independent versioning; unmistakable | more release and dependency overhead | Semver itself notes that **major version zero (0.y.z)** is for initial development, where anything may change and the public API should not be considered stable. It also allows **pre-release** versions such as 1.0.0-beta, which indicate the version is unstable and might not satisfy the compatibility its normal version promises. Both are natural fits for a separately versioned experimental package. ## How promotion and deprecation map onto version numbers 1. **Promotion from experimental to stable** adds the component to the public API. Under semver that is new backward-compatible functionality, so a **minor** release. If the component's path or name changes on promotion, the old experimental form can stay available for a transition period so consumers migrate on their own schedule. 2. **Changes to a stable component** follow the normal rules: incompatible changes only in a **major** release. 3. **Marking a stable component deprecated** is itself a change to the public API. Semver says the minor version **must** be incremented when public API functionality is marked deprecated. 4. **Removing it** is an incompatible change, so it requires a **major** release; semver's guidance is that at least one minor release should carry the deprecation before removal, so users can transition. ## Beta: covered or not? Systems differ on beta. Some include beta components in the public API and accept that a breaking change to one needs a major release; others exclude beta too and promise only advance notice. Either is defensible if it is **declared** and visible. What is not defensible is leaving consumers to guess. ## Applying it In a smart-home control app's design system, an experimental **thermostat dial** ships through the experimental entry point. Over three releases its interface changes twice, each in a minor release, with release notes flagging the experimental changes. When it is promoted, it appears in the stable entry point in a minor release, and the experimental path keeps working for one more release before being removed from the experimental area. ## Common mistakes - Putting experimental components in the main public API and then breaking them in minor releases. - Relying on a documentation footnote nobody reads. - Bumping the major version for every experimental change, so majors stop carrying meaning.
- Why is a documentation note alone a weak way to mark a component as experimental?Consumers mostly learn usage by copying from existing screens and examples, not by re-reading docs. A note on the docs page is invisible at the point of use, so a team can depend on an experimental component without ever seeing the warning. A distinct entry point or name keeps the status visible wherever the component is used.
- Under semantic versioning, what release must accompany marking a stable component as deprecated, and what must follow before it is removed?Marking public API functionality as deprecated requires a minor version increment. Removal is backward-incompatible, so it happens only in a later major release, and semver's guidance is that at least one minor release carrying the deprecation should come first so consumers can transition.
saying these in an interview costs you the question
- Semantic versioning guarantees cover everything in the package, declared or not.
- Every change to an experimental component requires a new major version.
- Marking a component deprecated needs no version change until it is removed.
- A documentation footnote is enough to exclude a component from the public API.
- Promoting a component to stable is a breaking change and needs a major release.