Toolchain Version Unification Workflow
Discover the versions actually in use, converge on one supported line, and pin it everywhere from CI to editors via a single version source.
Objective
Eliminate works-differently-on-my-machine by converging tool versions across the team through one declared source of truth.
Environment
- A version manager supporting version files (mise, asdf, or native)
- CI configuration where versions can be pinned
- Tolerance for a short migration window per developer
Steps
1. Inventory the versions in the wild
Collect actual local versions across the team and CI, quantifying the spread (which runtimes, how many minor lines).
Expected state: A version census exists showing the real distribution.
2. Choose the line deliberately
Pick one supported version line per tool based on the census, compatibility needs, and support horizon, documenting the rationale.
Expected state: A chosen-line table exists with reasons and dates.
3. Declare it in one source
Pin the chosen versions in a committed version manifest that CI, the version manager, and editor tooling all read.
Expected state: One file governs versions everywhere; no other place declares them.
4. Migrate with a window and a smoke check
Give the team a migration window, with the smoke suite as the acceptance that each machine works after switching.
Expected state: Every developer passes the smoke suite on the unified line within the window.
5. Guard against re-fragmentation
Have CI reject PRs from environments off the declared line, and schedule the next deliberate upgrade rather than drifting again.
Expected state: Version divergence is mechanically detected and blocked.
Expected outcome
One toolchain line across every machine and CI, declared in a single committed source, with upgrades becoming scheduled events rather than drift.
Verification
The version census repeated after migration shows a single line; CI rejects off-line environments; the next upgrade ran as a planned window.
Version spreads create bugs that reproduce nowhere
Three Node minors across the team means a bug that exists on two machines and not the third, and CI sometimes. A census, one chosen line, and a single committed version source make the environment a shared fact.
version census (before):
node: 20.9 (2), 20.18 (5), 22.5 (3), 22.11 (6)
pnpm: 8.x (7), 9.x (9)
-> 4 node lines, 2 pnpm lines
chosen line (rationale documented):
node 22.11 LTS, pnpm 9.12
single source (committed .mise.toml):
[tools]
node = "22.11.0"
pnpm = "9.12.3"
CI: reads the same file; off-line runners fail
setup: "node 22.5 detected, expected 22.11.0"
census (after window + smoke): 16/16 on line
next upgrade: scheduled 2027-01 (not drift)
The census grounds the choice in reality
Choosing the newest line without looking at usage strands whoever cannot move yet; choosing the majority line without data keeps the median by accident. The census turns version selection into a decision about actual distribution and support horizons.
One file, no side doors
Version pinning fails when three places declare versions (CI matrix, README instructions, Dockerfile) and disagree. A single manifest that everything reads — runners, local managers, devcontainers — means an upgrade is one commit instead of an archaeology project.