Skip to main content

1. The quarantine rule

A published version MUST be at least 2 days old before it may be installed or committed. No exception for convenience, urgency, or “it is just a patch”. The reason is specific: the supply-chain attack vector is the freshly published version. A compromised release is typically detected and yanked within hours to days, so the days-old window is where the danger lives. Waiting costs nothing and removes the entire class. The one exception: a version that fixes a security advisory affecting us skips the quarantine and ships immediately. Security wins over caution about security — but the bypass is recorded in the PR with the advisory id. Enforced by renovate.json (minimumReleaseAge: "2 days"). Enforcement is not a substitute for the rule: manual pnpm add of a same-day release violates this standard even though nothing mechanically blocks it.

2. Pinning

  • Exact versions everywhere. No ^, no ~, no ranges. (save-exact=true in .npmrc.)
  • The lockfile is committed and is reviewed like code. An unexplained lockfile diff blocks a PR.
  • packageManager and .nvmrc pin the toolchain itself; they are updated by the same process.

3. Adoption

Latest stable at the moment of adoption. When a dependency enters the repository it enters at the current stable release — never at an older version “to be safe”, never at a prerelease, alpha, beta, canary, rc or next tag. Two qualifications that “latest” alone does not capture:
  • Peer-major alignment beats absolute latest. @types/node tracks the Node major we actually run (Node 24 ⇒ @types/node 24.x), regardless of what latest points at. The same logic applies to any package whose major is coupled to a runtime or framework.
  • A 0.x dependency has no stable API. In 0.x, a minor bump may break. Any 0.x package on a critical path (today: drizzle-orm) is upgraded deliberately, with its own PR and its own test pass — never grouped, never automerged.
A dependency that is unmaintained, or whose latest stable is old enough to be abandoned, is not adopted. That judgement belongs in the ADR that introduces it.

4. Introducing a dependency

The stack is closed. A new dependency or vendor requires an ADR before the first import (AGENTS.md §2.2). The ADR states what it replaces or enables, why the alternatives lost, its maintenance signals, its license, and — for anything touching personal data — whether it becomes a sub-processor requiring an entry in docs/internal/legal/subprocessors.md. Upgrading an existing dependency never needs an ADR. Replacing one always does.

5. Keeping current

A quarterly window handles the accumulated majors with a named owner. Majors do not drift: an unapplied major upgrade older than two quarters becomes a tracked debt item, because the cost of catching up grows faster than the cost of keeping up.

6. CI gates

  • pnpm audit --audit-level=high fails the build. A finding is fixed or explicitly waived with an expiry date — never waived indefinitely.
  • Lockfile drift check: the lockfile must be in sync with the manifests.
  • No prerelease specifiers anywhere in a manifest.
  • License allowlist: copyleft licenses incompatible with a proprietary product are rejected at CI, not discovered at diligence.

7. Current toolchain and its pending upgrades

Recorded so the gap is visible rather than forgotten: Adopting TypeScript 7 is a spike, not a bump. “It is the latest” is not a reason to adopt a compiler rewrite before the toolchain that consumes it has certified it.