Skip to main content
  1. Blog/

Node.js 20 Drops — Permission Model, Test Runner, and the Maturity Arc

·936 words·5 mins
Osmond van Hemert
Author
Osmond van Hemert
JavaScript & Node.js - This article is part of a series.
Part : This Article

Node.js 20 was released on April 18, and as someone who’s been building on Node since the early days (version 0.10, if you’re keeping score), this release tells an interesting story about where the runtime is heading. There are no headline-grabbing features that’ll set Twitter on fire, but the additions reflect a platform that’s growing up in exactly the right ways.

The Permission Model: Finally
#

The feature I’m most excited about is the experimental permission model. Launched behind the --experimental-permission flag, it allows you to restrict what a Node.js process can access: file system reads and writes, child process spawning, and worker thread creation.

node --experimental-permission --allow-fs-read=/app/data --allow-fs-write=/app/logs app.js

This is a significant security improvement. For years, one of the legitimate criticisms of Node.js (and npm in particular) has been the supply chain risk: a single malicious dependency can read your filesystem, spawn processes, or exfiltrate data. With the permission model, you can constrain what the runtime itself is allowed to do, adding a layer of defense-in-depth that doesn’t depend on trusting every transitive dependency.

It’s still experimental, and the granularity isn’t as fine as Deno’s permission system (which has had this from day one — credit where it’s due). But the fact that Node.js is adopting this pattern validates what Ryan Dahl identified as a design flaw in his famous “10 Things I Regret About Node.js” talk. Better late than never, and the Node team’s implementation benefits from learning what works in practice.

Test Runner Goes Stable
#

The built-in test runner, introduced experimentally in Node.js 18, is now marked as stable. This is a bigger deal than it might seem. For years, the Node.js testing story has been “pick a framework” — Mocha, Jest, Vitest, Ava, Tap — each with its own configuration, assertion style, and ecosystem. Having a zero-dependency test runner built into the runtime reduces friction significantly, especially for smaller projects and libraries.

import { describe, it } from 'node:test';
import assert from 'node:assert/strict';

describe('Array', () => {
  it('should return -1 when value is not present', () => {
    assert.equal([1, 2, 3].indexOf(4), -1);
  });
});

The API is clean and familiar. It supports describe/it blocks, beforeEach/afterEach hooks, subtests, mocking, code coverage via --experimental-test-coverage, and watch mode. It’s not going to replace Jest for complex frontend testing setups anytime soon, but for backend services and libraries, it’s increasingly compelling.

I’ve started using it on a few internal tools, and the startup time alone — no Jest configuration parsing, no Babel transforms — makes it noticeably faster for small test suites.

V8 11.3 and Performance
#

Node.js 20 ships with V8 11.3, which brings several quality-of-life improvements:

  • String.prototype.isWellFormed() and toWellFormed() — Useful for handling strings that might contain lone surrogates, a common source of subtle bugs in text processing.
  • Array.prototype changes — Methods like Array.fromAsync continue to modernize the standard library.
  • Improved regular expression performance — V8’s regex engine continues to get faster, which matters for any application doing significant text parsing.

The V8 update also brings Maglev, V8’s new mid-tier optimizing compiler, which sits between Sparkplug (the baseline compiler) and TurboFan (the full optimizing compiler). The practical impact is faster startup times and improved performance for code that runs moderately often — not hot enough for TurboFan to kick in, but frequent enough to benefit from optimization beyond the baseline.

The Bigger Picture: Node.js in 2023
#

Stepping back, Node.js 20 reflects a runtime that’s shifted from “move fast and add features” to “mature, harden, and refine.” The permission model addresses security. The stable test runner reduces external dependencies. Performance improvements are incremental but consistent.

This is exactly what I want from a platform I’m running in production. The days of major breaking changes and the drama around --harmony flags feel like ancient history. Node.js has become the kind of boring, reliable infrastructure that you build businesses on — and I mean “boring” as the highest compliment.

The LTS schedule continues to be one of the best things about Node.js governance. Version 20 will enter Active LTS in October 2023, and teams can plan their upgrade paths with confidence. After the chaos of the early npm years, this kind of predictability is valuable.

What I’m Watching
#

Two things I’ll be monitoring as Node.js 20 matures:

  1. Permission model adoption — Will the ecosystem embrace it? Will frameworks like Express or Fastify start documenting the permissions they need? This could become the foundation of a much stronger security story for Node.js.

  2. The single-executable application feature — Also experimental in v20, the ability to bundle a Node.js app into a single executable (using the --experimental-sea-config flag) could change how we distribute Node.js tools. Think of it as Node’s answer to Go’s static binaries.

My Take
#

Node.js 20 isn’t exciting in the way that async/await in Node 8 was exciting. It’s exciting in the way that a well-maintained codebase is exciting — everything works a little better, the rough edges are getting smoothed out, and the platform is clearly being stewarded by people who care about the long-term.

If you’re starting a new Node.js project today, target version 20. If you’re on Node 18 LTS, plan your upgrade for when 20 hits LTS in October. And if you’re still on Node 16 — its end-of-life is September 2023, so start planning now.

The JavaScript runtime landscape has never been more competitive, with Deno and Bun both pushing interesting ideas. But Node.js remains the pragmatic choice for production workloads, and releases like this show why.

This post is part of my Developer Landscape series, covering the tools and platforms that shape how we build software.

JavaScript & Node.js - This article is part of a series.
Part : This Article