Skip to main content

Four surfaces, one version number

A pin protects a site from package changes only. The API and database move under every site instantly, which is why the compatibility rule exists.

The two standing rules

1

API and database changes are additive and backward-compatible

After any rollback an old package talks to the newest API and schema, so the newest must keep working for the oldest pinned version in the fleet.
2

Release small and often

A big batched release destroys rollback granularity: rolling back past a two-week batch loses two weeks of fixes, not one day’s.

Expand, migrate, contract

  • Expand freely: tables, nullable or defaulted columns, new routes, optional parameters, new response fields. Old clients ignore what they do not know.
  • Migrate: release the package version that uses the new thing; roll sites forward.
  • Contract only in a breaking release, and only once every site is at or above the version that stopped depending on the old shape.
Never in one step: renames (add the new, dual-read, drop the old later), narrowing a type, making an optional parameter required, repurposing a field.

The release flow

  1. A change to the packages carries a changeset. Its first paragraph is the changelog post.
  2. Merging to main ships nothing. A “Version Packages” pull request accumulates pending changesets.
  3. Merging that pull request publishes. Nothing deploys to any site.
  4. Each site adopts a version deliberately: a bump pull request in its own repository, or an explicit version move for a rollback. Merging it is that site’s go-live.
While versions are 0.x, a minor bump means breaking and a patch means everything else. A breaking changeset carries its migration note right then.

Rollback

Edit the site’s pin back and redeploy. Deployed sites are never touched by any of this machinery; only the next build changes. A rolled-back site is an old package on the newest API, protected only by the compatibility rule. Rollback is a tourniquet, not a destination.