Context
Since v2 the client is where all the logic lives, and it is built with TypeScript + ESBuild. For years that toolchain ran on Deno, chosen early when Deno’s sandboxed-worker permissions also suited running plugs on the server (see ADR/010 Rust Backend).
Once the server became a thin file store, only the client remained to build, and the trade-off shifted: Deno’s distinctive advantages no longer bought SilverBullet much, while its churn — repeated changes in package management (HTTP imports → import maps → JSR → varying npm support) and the occasional regression — was a recurring distraction.
Decision
The client build toolchain moved from Deno to Node.js (npm + ESBuild, with vitest for tests). It was done gradually and completed in the to-node migration.
Consequences
Positive
- Lower barrier to contribution. Far more developers know Node/npm than Deno.
- Richer ecosystem. Unlocked biome for formatting/linting and a Playwright end-to-end test suite.
- Off a moving target. No longer tracking Deno’s shifting package-management story.
Negative / trade-offs
- More moving parts. Deno bundled fmt/lint/test out of the box; on Node these are assembled from separate packages (biome, vitest), a slightly larger dependency surface.
- Left the “better” runtime. Deno is arguably the nicer JS runtime; the move trades that for familiarity and ecosystem reach.
Alternatives considered
- Stay on Deno. Rejected: with only the client left to build, Deno’s specific benefits no longer justify the unfamiliarity and thinner ecosystem.
- Switch to Bun. Rejected: less mature than Node, and maturity is weighted heavily in stack choices.
References
- Client toolchain migration: PR1839.
- Blog: On Tech Stacks — the decision criteria behind these moves.