Build1 publisher2 min readPublished
Stability labels decide which npm packages Node's built-ins can replace in production
Flavio Copes matched twelve Node.js built-ins to the npm packages they replace and graded each one by its stability in the current LTS line. Node 22 still labels its SQLite driver experimental, so a server on that line can see the API change in a minor release.
The Engineer · Build desk
Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

What happened
- Copes checked each claim against the Node 24.21.0 API docs, the Active LTS line as of September 2026, and ran every example on Node 26.0.0.
- Node 20 reached end of life on April 30, 2026, Node 22 stays in maintenance until April 2027, and Node 26 becomes the next LTS on October 28, 2026.
- The built-in test runner's coverage and module mocking still sit behind experimental flags, while function mocks and spies through t.mock.fn() are stable.
- Global fetch is built on the undici HTTP/1.1 client and ships Request, Response, Headers and FormData as globals alongside it.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- decision With node:test stable on every supported Node line, whether a given suite can drop Jest comes down to whether it needs coverage or whole-module mocks.
- exposure Node 22 servers that adopted node:sqlite depend on an API that can change in any minor release, on a line that stays in maintenance until April 2027.
- cost Leaving better-sqlite3 means writing BEGIN, COMMIT and ROLLBACK by hand in try/catch wherever code relied on its db.transaction(fn) helper.
Node grades each API on a stability scale, and the grade is published in the docs [4]. Stability 2 means stable. Stability 1 means experimental, with 1.0 for early development, 1.1 for active development and 1.2 for a release candidate [4]. "Anything experimental can change or disappear in a minor release, so the number matters if you're putting it in production," Copes wrote [5].
On Node 22, the --experimental-sqlite flag was removed in 22.13, but node:sqlite kept its 1.1 label [9]. Removing the flag also removed the only opt-in step, so code can import the module without ever marking it as experimental [2]. The docs describe that level as "nearing minimum viability" [9].
The SQLite swap takes a native build out of every install [6]. The better-sqlite3 package is a native addon that gets compiled, or fetched as a prebuilt binary, on each install [6]. node:sqlite arrived in Node 22.5 and is synchronous like the package it replaces, so query code keeps its top-to-bottom shape with no await [6]. One porting trap is small. The module exists only under the node: prefix, and a plain import 'sqlite' fails [7].
The test runner has been Stability 2 since Node 20.0.0, and node --test finds files named like math.test.js with no configuration [11]. Copes wrote a post on Jest in 2018 and now runs every test script on his own site through node --test [14]. He draws the line at one feature. "If your suite leans on mocking whole modules, keep Jest for now," he wrote [13].
Copes's list also names dotenv, nodemon and uuid among the packages Node now covers [1]. The entries cited here do not include their stability levels, so for those three the replacement claim rests on his list alone.
I think adoption should follow the labels. A plain unit-test suite can move to node:test on any supported line [1]. A production SQLite path belongs on Node 24.15.0 or later, where node:sqlite is a 1.2 release candidate and prints no warning [8].
What to watch
- Whether a later Node 24 or 26 release promotes node:sqlite from a 1.2 release candidate to Stability 2.
- Whether test coverage and mock.module() reach stable status, the remaining reason Copes gives for keeping Jest.
- The Node 26 API docs at the October 28 LTS promotion, for stability labels that differ from the Node 24 line.