Skip to content

Stability & Versioning

What versioning guarantees this library gives your app today, the wire-format skew promise across deploys, and the capabilities that are deliberately out of scope by design.

This page is the contract between the library and your app: 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 surface — the connection model, the realm and action APIs, the error vocabulary, the five wire formats — is the intended final form. It is what every guide on this site is written against.

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, no surface change.

From 1.0.0 onward (a later, separate release):

  • Breaking changes only in a major, always with migration docs.
  • Minors are additive; patches are fixes — 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 run over five wire formats (the handshake, the mux, control frames, the HTTP exchange, and the per-domain codecs). A deployed server and the clients already in the field are rarely on the same version — a browser tab left open for a week, a mobile app a user hasn’t updated. That skew is designed for, and the promise holds now, at 0.x, not only from 1.0:

  • Every format grows additively — a new realm frame kind, a new capability token negotiated in the handshake, a new optional field — never by changing the meaning of an existing byte.
  • A peer that meets something it doesn’t recognise drops it and carries on; an unknown addition is never fatal.
  • So a client and a server a release apart interoperate: the newer side’s additions are invisible to the older side, and the older side keeps working.

In practice: you can deploy your server and let old clients reconnect without a coordinated flag-day. Newer features simply light up as clients catch up.

  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 with its migration note.
  3. Deploy the server and the client independently; the skew promise above covers the window where they differ.

Some capabilities are not in this release by design, not by omission. Each is a boundary held on purpose — folding it into the core would tax 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 — 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 lands through an additive seam — a new option, a caps-gated protocol addition — that doesn’t break the code you write today. That is the whole point of the skew promise above: the library can grow into these without a breaking release.