Two version axes matter, and conflating them is the usual source of confusion:
- Package versions — the npm semver of each
@z0devs/*package. Independent per package, somf-coreandmf-plugin-kitmove at different rates. SHELL_API_VERSION— the host↔plugin contract version, exported from@z0devs/mf-coreand advertised by your store asapiVersion. Independent of any package's npm version.
A third axis lives entirely outside the library: shared npm libraries (React, your design system)
are negotiated by Module Federation's shared config.
Package SemVer#
| Bump | When |
|---|---|
| patch | Bug fixes, internal refactors, doc-only changes, build-output diffs. No type change that breaks a downstream compile. |
| minor | New exports, new optional fields, new optional props, expanded unions. Existing code compiles and behaves identically. |
| major | Removed or renamed exports, narrowed types, prop renames or semantics changes, default-behaviour changes, dropped runtime support, SHELL_API_VERSION bump. |
The shell API version#
Bump SHELL_API_VERSION when any of these changes in a way that breaks plugin assumptions:
SharedStoreSchemashape (values/registries/eventsslots)SharedInitPropsshape passed to plugin init componentsMFEManifestEntryrequired fieldsRegistryApi<T>method signatures or event names (ITEM_ADDED/ITEM_REMOVED)- The
OnboardingStepunion (new required actions, renamedactionvalues) BaseRemoteenv-config conventions- The
withRemoteNamespacedecoration scheme
Adding a new optional field is not a contract break — plugins compiled against an older contract keep working, and it ships as a minor.
{ "shellApiVersion": "^0.1.0" }Read as: "I work with any 0.1.x shell. Block me on 0.2+." PluginRouteLoader compares it against
sharedStore.apiVersion:
Don't set `apiVersion` unless you mean to
defineSharedStore defaults apiVersion to SHELL_API_VERSION from @z0devs/mf-core — currently
0.1.0, the same value the plugin scaffold targets. Hard-coding a different one ("1.0.0" is the
tempting guess) silently blocks every plugin built from the template: they ask for ^0.1.0, your
shell answers 1.0.0, and versionPolicy: "block" skips them before they load. Omit it, and the two
sides agree by construction. Set it only when you are deliberately pinning an older contract.
versionPolicy | Behaviour on mismatch |
|---|---|
"block" (default) | Skip the entry, log, record version-blocked |
"warn" | Load anyway, log — for a vetted upgrade window |
"off" | No check — tests only |
Supporting two shell majors at once is a range:
{ "version": "1.4.0", "shellApiVersion": ">=0.1.0 <0.3.0" }Pre-1.0 reality#
Everything is 0.x today
Per SemVer convention, minor bumps may contain breaking changes while packages are 0.x. Pin
^0.1.x and read release notes. When the first package reaches 1.0.0, full SemVer guarantees apply
and SHELL_API_VERSION resets to 1.0.0.
Stability tiers#
| Package | Tier | Notes |
|---|---|---|
@z0devs/mf-context | Stable | createMFSharedContext, createEventsEmitter, useListenerContext are frozen-shape |
@z0devs/mf-router | Stable | Small, frozen dynamic-route contract |
@z0devs/mf-plugin-kit | Stable | createPluginStoreBridge + createLocalStubContext frozen-shape |
@z0devs/mf-registry | Evolving | Canonical addItem/removeItem stable; onboarding-step shape may grow |
@z0devs/mf-core | Evolving | MFEManifestEntry may gain fields; env-config conventions may expand |
@z0devs/mf-shell-kit | Evolving | PluginRouteLoader props and defineSharedStore's returned shape may expand |
@z0devs/mf-config | Stable | Build-time only. z0Shared / z0Federation return plain data, so a new emitted field is additive — spread-override keeps you in control |
@z0devs/mf-testing | Experimental | Pin exact versions in CI |
@z0devs/module-federation | Reflects siblings | Subpath exports stable |
create-z0-plugin | Stable | CLI flags + template tokens survive minors |
Deprecation lifecycle#
Announce
Release notes for the minor call out the deprecation and the replacement.
Grace window
At least one full minor cycle between deprecation and removal — deprecate in 0.2.0, remove no
earlier than 0.3.0 (pre-1.0), or 1.x → 2.0.0 after that.
Warn at runtime
Deprecated functions log once per process in dev:
[@z0devs/<pkg>] '<name>' is deprecated; use <replacement> instead.
Warn at type level
@deprecated JSDoc so IDEs strike it through.
Remove
In the next major (or the next minor, pre-1.0).
Deprecation log#
| Version | Deprecated | Replacement | Removed in |
|---|---|---|---|
mf-shell-kit@0.1.x | useStartupGateRunner, the gates prop on PluginRouteLoader, StartupGateContext re-exports | OnboardingStep registry + OnboardingHost (auto-mounted) | mf-shell-kit@0.2.0 — already removed |
What won't break in a patch#
- Existing public exports and their signatures (adding an optional arg is a minor)
- Existing prop names, and whether they're required
- Behaviour on inputs that previously worked
MFEManifestEntryrequired fields (name,service.id,service.remoteEntry)SHELL_API_VERSION- Package entry-point structure (
main/module/exports/types)
What may change in any release#
- Internal implementation details, private functions, undocumented behaviour, debug logging
- Error message wording — match on error
kind/name, never on message text - Bundle size, sourcemap structure
- Chunk filenames (
remoteEntry.jsitself stays) - Non-peer sub-dependencies
- Dev-only logging verbosity
Compatibility matrix#
| Shell library set | Compatible plugins (shellApiVersion) | Notes |
|---|---|---|
All @z0devs/mf-*@0.1.x, SHELL_API_VERSION=0.1.0 | "^0.1.0" | Current state |
@z0devs/mf-*@0.2.x | TBD | First contract bump if needed; migration guide will ship |
@z0devs/mf-*@1.0.0 | ">=1.0.0 <2.0.0" | First stable line |
Runtime peers: @module-federation/runtime >=0.6.0 <3.0.0 (validated against 2.5.1),
react >=18 (19 permitted), react-router-dom >=6.4 <8.
Migration guides#
Every major ships a MIGRATING.md covering renamed exports as a search-replace table, prop signature
changes, behaviour changes, before/after type snippets, and codemods where the change is mechanical.
They ship with the release notes for the version that needs them — check the changelog entry for the
target version, or ask us if you can't find the one you need.
If a release breaks you#
Unblock
Pin the previous version with --save-exact.
Report
File an issue with the type or runtime error plus a minimal repro.
Expect a fix
A patch revert or a migration note within one minor cycle.