Node 26 and the build step that finally disappeared
Type stripping, a mature test runner, and a standard library that absorbed the ecosystem. You can ship TypeScript with no toolchain.
Node.js 26 landed this week, and the accumulated effect of the last several major versions is worth stating plainly: you can now write a TypeScript server application, test it, and run it in production, with zero build tooling.
That was not true three years ago and it is a meaningful simplification.
the workflow#
{
"compilerOptions": {
"erasableSyntaxOnly": true,
"verbatimModuleSyntax": true,
"module": "nodenext",
"strict": true,
"noEmit": true
}
}node --test # test runner, built in
node --watch app.ts # dev, with reload, running TypeScript directly
npx tsc # typecheck only, in CI
node app.ts # productionNo bundler. No transpiler in the runtime path. No ts-node. No nodemon. No dotenv. No test framework. No node-fetch.
The erasableSyntaxOnly flag is what makes it safe: it rejects any TypeScript syntax that cannot be removed by whitespace substitution — enums, namespaces with runtime values, parameter properties — so your source is guaranteed to run under type stripping.
what got absorbed, cumulatively#
Over the last several major versions Node has taken into the runtime:
| capability | the package it replaced |
|---|---|
| test runner | jest, mocha, ava |
| watch mode | nodemon |
.env loading | dotenv |
| TypeScript execution | ts-node, tsx |
| SQLite | better-sqlite3 |
fetch, WebSocket | node-fetch, ws, axios |
| permission model | (nothing existed) |
using / disposal | various |
Every one of those is a dependency removed, which is a supply chain surface removed, an upgrade treadmill removed, and a postinstall script removed.
For a small service, a fresh project's dependency count is now dramatically lower than it would have been in 2022. That is the most important consequence and it is rarely framed that way.
the division of labor that makes sense#
Typechecking belongs in CI and your editor. Not in the production runtime hot path. tsc --noEmit in CI, language server in your editor, type stripping at runtime.
A type error will happily run under stripping and fail at runtime. That is the correct trade — you check types where checking is cheap and run where running should be fast — and it requires that CI actually gates on the typecheck. If it does not, you have given up types.
Bundling is still right for some things. If you deploy to a serverless platform where cold start scales with file count, bundling helps. If you ship to browsers, obviously. For a long-running server process, it buys you nothing.
what is still missing#
Decorators. They are not erasable — they generate runtime code. If your framework depends on decorator-based dependency injection or routing, you need a transform. Several major frameworks are in this category, and it is the single most common blocker for adopting the no-build workflow.
Path aliases. @/components/foo requires resolution mapping. Node supports imports in package.json with the # prefix, which works and requires changing your import style.
Anything in your build that is not TypeScript. CSS modules, GraphQL documents, asset imports. If your build does more than strip types, you still have a build.
the migration advice#
If you are on Node 22 or earlier, plan the move. Check:
- Base image compatibility — minimum glibc and macOS versions have moved.
- Deprecated APIs that now throw rather than warn.
- Native modules, which need a rebuild.
Run the full suite on the new version in CI before switching the default. The upgrade is usually uneventful and "usually" is load-bearing.
the broader observation#
Node's response to competitive pressure from Deno and Bun was to absorb their best ideas rather than to argue about philosophy.
That was correct, it took about four years, and the ecosystem is meaningfully better for it. Competition in developer tooling works, and the incumbent that responds by improving rather than by defending is the one that keeps the position.
— Dom, April 3, 2026