Tags: web-dev concept

Versioning

Date: 2026-08-17


Naming releases so consumers know what a change will do to them. Semantic versioning promises a lot and delivers less, because “breaking” is judged by the publisher and experienced by you — and those two rarely agree.


Versioning assigns identifiers to releases so consumers can reason about upgrades.

Semantic versioning

MAJOR . MINOR . PATCH
  2   .   4   .   1

MAJOR  breaking change
MINOR  new feature, backwards compatible
PATCH  bug fix, backwards compatible

The promise: you can take any minor or patch upgrade without reading the changelog.

Why it under-delivers: “breaking” is a judgement the publisher makes about their intent, and consumers depend on behaviour the publisher never considered part of the contract.

A "PATCH" THAT BROKE SOMETHING

a bug fix — someone depended on the bug
a performance change — someone depended
  on the timing
an error message change — someone parsed it
new validation — someone was sending
  invalid data that used to pass
a changed sort order — nobody promised
  it, everyone relied on it

Hyrum’s law names this: with enough consumers, every observable behaviour of your system will be depended on by somebody, regardless of what you promised.

Range specifiers, and the risk they carry

2.4.1     exact
~2.4.1    patch updates    → 2.4.x
^2.4.1    minor updates    → 2.x.x
*         anything         → never

^ is the npm default, which means a fresh install can pull code the author published minutes ago into your build. That’s the mechanism behind supply-chain incidents — the compromise doesn’t need to reach your repository, only the registry.

A lockfile is the control. It pins the entire resolved tree, so installs are reproducible and upgrades are deliberate rather than incidental. Commit it, and treat changes to it as reviewable — Dependency Management.

Versioning an API

URL PATH        /v1/orders
  visible, cacheable, obvious
  ugly, and duplicates routes

HEADER          Accept: application/
                vnd.api.v2+json
  clean URLs
  invisible, harder to test and cache

QUERY PARAM     /orders?version=2
  easy
  easy to omit accidentally

DATE-BASED      2026-04
  ← what Shopify, Stripe and Adobe use
  no argument about what "breaking"
  means; consumers pin a date and
  migrate on a schedule

Date-based versioning has quietly won for platform APIs, and the reason is that it sidesteps the semver judgement entirely. There’s no debate about major versus minor — there’s a release, and a support window — Shopify, Reference - AEM.

Versioning things that aren’t code

DATABASE SCHEMA   sequential migrations,
                  each with an up and
                  ideally a down

EVENT SCHEMAS     a version on the event,
                  or additive-only change

DESIGN TOKENS     renaming one is breaking
                  with NO compiler to catch
                  it

CONTENT / CONFIG  usually unversioned, and
                  the source of "who changed
                  this?"

See: Database Migrations · Event Taxonomy Design · Design System Versioning

Practical rules

  • Commit the lockfile. Non-negotiable
  • Pin exactly in applications; use ranges in libraries. An application wants reproducibility; a library that pins exactly forces version conflicts on everyone who consumes it
  • Read changelogs for major upgrades, and diff the lockfile for the rest
  • Automate the boring upgrades — patch and minor, with tests as the gate. Batching six months of them into one afternoon is how upgrades stop happening
  • Don’t version internal things prematurely. A service deployed alongside its only consumer doesn’t need an API version; it needs them deployed together
  • Support one previous major for anything public, with a stated window

The version to carry

Semver is a communication convention, not a guarantee. It tells you the publisher’s intent, which is genuinely useful and is not the same as knowing your build will still work — which is what tests and a lockfile are for — Backwards Compatibility.