Private beta — the @z0devs packages are available to design partners while we harden for general availability.Try the live demoRequest access

Versioning & compatibility

SemVer policy, SHELL_API_VERSION, per-package stability tiers, the deprecation lifecycle, and the compatibility matrix.

Two version axes matter, and conflating them is the usual source of confusion:

  1. Package versions — the npm semver of each @z0devs/* package. Independent per package, so mf-core and mf-plugin-kit move at different rates.
  2. SHELL_API_VERSION — the host↔plugin contract version, exported from @z0devs/mf-core and advertised by your store as apiVersion. 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#

BumpWhen
patchBug fixes, internal refactors, doc-only changes, build-output diffs. No type change that breaks a downstream compile.
minorNew exports, new optional fields, new optional props, expanded unions. Existing code compiles and behaves identically.
majorRemoved 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:

  • SharedStoreSchema shape (values / registries / events slots)
  • SharedInitProps shape passed to plugin init components
  • MFEManifestEntry required fields
  • RegistryApi<T> method signatures or event names (ITEM_ADDED / ITEM_REMOVED)
  • The OnboardingStep union (new required actions, renamed action values)
  • BaseRemote env-config conventions
  • The withRemoteNamespace decoration 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.

the plugin side of the contract
{ "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.

versionPolicyBehaviour 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#

PackageTierNotes
@z0devs/mf-contextStablecreateMFSharedContext, createEventsEmitter, useListenerContext are frozen-shape
@z0devs/mf-routerStableSmall, frozen dynamic-route contract
@z0devs/mf-plugin-kitStablecreatePluginStoreBridge + createLocalStubContext frozen-shape
@z0devs/mf-registryEvolvingCanonical addItem/removeItem stable; onboarding-step shape may grow
@z0devs/mf-coreEvolvingMFEManifestEntry may gain fields; env-config conventions may expand
@z0devs/mf-shell-kitEvolvingPluginRouteLoader props and defineSharedStore's returned shape may expand
@z0devs/mf-configStableBuild-time only. z0Shared / z0Federation return plain data, so a new emitted field is additive — spread-override keeps you in control
@z0devs/mf-testingExperimentalPin exact versions in CI
@z0devs/module-federationReflects siblingsSubpath exports stable
create-z0-pluginStableCLI 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.x2.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#

VersionDeprecatedReplacementRemoved in
mf-shell-kit@0.1.xuseStartupGateRunner, the gates prop on PluginRouteLoader, StartupGateContext re-exportsOnboardingStep 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
  • MFEManifestEntry required 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.js itself stays)
  • Non-peer sub-dependencies
  • Dev-only logging verbosity

Compatibility matrix#

Shell library setCompatible plugins (shellApiVersion)Notes
All @z0devs/mf-*@0.1.x, SHELL_API_VERSION=0.1.0"^0.1.0"Current state
@z0devs/mf-*@0.2.xTBDFirst 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.

Next#