Skip to content

Stability & Versioning

The versioning guarantees you get today, the promise that a server and older clients keep working across a deploy, and the capabilities left out on purpose.

This page is the contract between the library and your app. It covers how versions move, what stays stable while we are pre-1.0, how a deployed server and older clients keep working across an upgrade, and which capabilities are deliberately not here.

The public API is in its intended final form: the connection model, the realm and action APIs, the error vocabulary, and the five wire formats. Every guide on this site is written against it.

We are still on the 0.x line, on purpose. The shape is settled, but staying pre-1.0 keeps the freedom to refine a rough edge in a minor release before committing to the stricter 1.0 guarantee below. Read 0.x as “stable enough to build on, not yet frozen.”

Every @nice-code/* package releases in lockstep at one version. @nice-code/wire, @nice-code/realm, @nice-code/action and the rest always share a version number. Upgrade them together; mixing versions across the set is unsupported.

We follow semver, read against where we are:

While 0.x (now):

  • 0.MINOR.0 may contain a breaking change. When it does, it ships with a changelog entry and a migration note. A breaking change is never silent.
  • 0.x.PATCH is fixes only, with no change to the public API.

From 1.0.0 onward (a later, separate release):

  • Breaking changes only in a major, always with migration docs.
  • Minors are additive and patches are fixes, which is the ordinary semver promise.

The move to 1.0.0 will be its own announcement, made once the 0.x surface has soaked with real consumers. Nothing on this page changes silently underneath you before then.

Realms and actions use one first-party protocol generation across the handshake, mux, action and realm codecs, HTTP exchange, reliability controls, and persisted session state. The current generation is protocol v1. Read it from WIRE_PROTOCOL_VERSION; the generations a particular build can actually speak are listed by WIRE_SUPPORTED_PROTOCOL_VERSIONS. Those constants describe the local build. They do not discover a remote peer.

Package versions and protocol generations are separate. Several lockstep package releases can use protocol v1. Within one client or server build, keep all @nice-code/* packages on the same package version. The client and server builds may differ when that release pair is covered by the compatibility matrix and the application definitions remain compatible.

The intended support window for a future wire-breaking release is the current and immediately previous generation. A generation is listed only when complete encoders, decoders, replies, pushes, reconnect, and persisted-session restoration have compatibility coverage. This release implements one codec and therefore lists only [1]; it does not claim a placeholder v2 or v0.

Compatible additions continue to use negotiated capabilities and optional fields. Do not infer that every unknown frame is safe to ignore, or that matching the library protocol makes changed application contracts compatible. Secure links reject a recognized no-common-generation failure with typed protocol_version_mismatch. Plain/no-handshake transports keep using the unnegotiated v1 format and cannot discover the remote version.

For a staggered deployment, keep action route order and payload/result semantics, realm definitions, authorization, and explicit dictionary/schema pins stable. Test both mixed directions against the actual released artifacts before claiming a rolling upgrade:

ClientServerWhy it is required
DeployedDeployedProves the frozen fixture is valid.
DeployedCandidateCovers a server-first upgrade and old-client reconnect.
CandidateDeployedCovers a client-first upgrade or server rollback.
CandidateCandidateCovers the final steady state.

Prefer a server-first canary after both mixed pairs pass, observe handshake/reconnect/realm attach failures, then deploy clients. Keep the tested server artifact available for rollback. If an application schema or handler contract must also change incompatibly, split that migration or use a maintenance window; a matching realm hash or action dictionary alone does not prove matching business semantics.

  1. Bump every @nice-code/* dependency to the new version together.
  2. Read the changelog for that minor. In a 0.x minor, a breaking change (if any) is called out there under Action required, with its migration note.
  3. Run the release’s old/new compatibility matrix with your unchanged application definitions.
  4. Deploy the server canary, then clients, while keeping the tested rollback artifact available.

Some capabilities are left out of this release by design, not by omission. Each is a boundary held on purpose, because building it into the core would cost every consumer for a need most don’t have:

  • Per-message priorities and TTLs in the core send path.
  • Exactly-once effects baked into the core. The primitives to build idempotent handling are there, and the delivery guides show the pattern, but the core does not silently promise effect-once for you.
  • Causal ordering across independent actions.
  • Reliable broadcast as a core guarantee.
  • Per-message delivery knobs on the core API.

Where a real need for one of these appears, it arrives as an additive change, such as a new option or a caps-gated protocol addition, that doesn’t break the code you write today. The skew promise above makes that possible: the library can grow into these without a breaking release.