Skip to content

ADR-0016: Quarterly stack-bump review cadence

Context

961tech is built on the bleeding edge — Next.js 16 (released months ago), React 19, Prisma 7, Tailwind 4, Workers Paid. The bleeding-edge bet pays off (Server Actions, RSC, Suspense, Workers cost) but introduces a maintenance question: when do we bump?

Renovate (per #160) auto-merges minor + patch updates on green CI. Major bumps need human eyes. Without a cadence, two failure modes:

  1. Bump-when-you-think-of-it — we never think of it; the stack drifts; the next bump is a multi-major nightmare migration.
  2. Bump-immediately-on-release — every Friday becomes a migration day; productive feature work suffers.

A quarterly cadence threads the needle.

Decision

Bump major versions of the foundation stack on the first Monday of each quarter (January, April, July, October). The session is timeboxed: bumps that don't land cleanly within 1 day get reverted and queued for the next quarter (or a dedicated migration project if the gap is becoming costly).

Stack covered by the quarterly review: - Next.js, React, React DOM - Prisma, @prisma/client, @prisma/adapter-pg - TypeScript - Tailwind CSS, @tailwindcss/postcss - ESLint, eslint-config-next - Vitest - shadcn/ui (when introduced) - Auth.js (when introduced)

Renovate handles minor + patch automatically with auto-merge on green CI; the human-review surface is just majors. Renovate config is configured to surface major-bump PRs but not auto-merge them.

Workflow: 1. Quarterly Monday: pull main, branch chore/q<n>-stack-bump, run npm outdated --json to see major-version deltas. 2. For each major bump: read the migration guide, run the codemod (if available), bump the package, run npx tsc --noEmit && npm run lint && npm run format:check && npx vitest run && npx next build. 3. If anything breaks, fix it forward. If a fix is bigger than ~1 hour, revert that bump and file an issue for the next quarter. 4. Commit per-package: each PR title is chore(deps): bump <pkg> to <vX.Y>. PR body lists the migration guide link + breaking changes encountered. 5. Merge in dependency order (TS first, then build chain, then app frameworks last). 6. Open a quarterly review note at docs/plans/YYYY-Q<n>-stack-bump.md with the changelog summary.

Out-of-band bumps allowed when: - A security advisory drops on a dependency we use in prod (CVSS ≥ 7). - A bug we're hitting is fixed in a newer release. - The current version is end-of-life and the next stable is the only path forward.

Out-of-band bumps still go through the same workflow but in a smaller per-package PR rather than a quarterly batch.

Consequences

Positive

  • Bounded migration cost. A major Next.js bump quarterly is cheap; once-a-year is expensive; never is catastrophic.
  • Predictable maintenance. MASTER's calendar carries 4 days per year reserved for this.
  • Continuous benefit. Bleeding-edge upside (Workers cost, Server Components) accrues without bleeding-edge downside (drift).
  • Renovate handles the boring 80%. The human surface is just the majors that warrant review.

Negative

  • A quarter can pass between a release and our adoption. A Next.js 17 release the week after our quarterly review waits 12 weeks. Mitigated by the out-of-band bump trigger when a new major is genuinely worth the early adoption.
  • Dependency-version skew between quarters. If we ship a feature that depends on a Next.js 17.2 API, but our review cycle hasn't reached 17.2, we either backport or postpone the feature. Acceptable.
  • Calendar drift. Quarterly reviews can slip. Mitigation: the project board carries a recurring "Q stack bump" issue created at the start of each quarter.

Neutral

  • This ADR doesn't pin specific majors — it sets the cadence. Specific version locks live in package.json and are documented per-bump in docs/reference/tech-stack.md.

Alternatives considered

Bump-on-every-release (continuous adoption)

Rejected. Productive feature work suffers when every Friday is a migration day. Even with auto-codemods, the testing-and-verifying surface is real.

Bump-once-a-year (annual review)

Rejected. The migration debt accumulated over 12 months is materially harder than 4 quarterly migrations. A skipped Next.js 16 → 19 jump is a project; 16 → 17 → 18 → 19 in quarterly steps is four small reviews.

Pin-and-never-bump (LTS-only branches)

Rejected. Next.js doesn't ship LTS. React's LTS is implicit and slow. Prisma rolls forward fast. Pinning means we forfeit the bleeding-edge upside that motivated the stack choice.

Defer bumps to feature-driven need

Rejected. Means we bump only when forced — usually because a security advisory or an unfixable bug. The bump-in-crisis mode is the worst-time-to-bump mode.

Implementation plan

  • This ADR (locked 2026-04-28)
  • #160 Renovate config (shipped #167) — handles minor + patch with auto-merge on green CI; surfaces majors for review without auto-merging
  • First quarterly review: 2026-07-06 (Q3 first Monday). Recurring issue auto-filed by GitHub Project workflow.
  • Reference → Tech stack updated per quarter with current versions + last-bumped date.

References