Node.js 14 dropped on April 21st, right on schedule. As someone who’s been building with Node since the early days — back when we were still arguing about whether JavaScript on the server was a terrible idea — each major release is a chance to take stock of where the platform is heading. And Node 14 tells an interesting story: the platform is growing up, focusing less on flashy new syntax and more on the plumbing that makes production systems reliable.
The Headline Features#
Let’s start with what’s new. The V8 engine bumps to version 8.1, which brings Optional Chaining (?.) and Nullish Coalescing (??) out of the flag zone and into stable territory. If you’ve been using TypeScript, you’ve had these for a while, but having them natively in Node without a compilation step is welcome. They’re the kind of small ergonomic improvements that reduce boilerplate without introducing complexity.
More interesting to me is the experimental support for WASI — the WebAssembly System Interface. This isn’t just “run WebAssembly in Node” (we’ve had that since Node 8 with the WebAssembly global). WASI is about giving WebAssembly modules a standardized way to interact with the operating system: reading files, accessing environment variables, working with clocks. It’s sandboxed by design, capability-based, and it points toward a future where you can run untrusted code with fine-grained permissions.
The --experimental-wasi-unstable-preview1 flag doesn’t exactly roll off the tongue, and the API is explicitly unstable. But the direction is clear. Imagine being able to load a third-party computation module — written in Rust, C, or any language that compiles to Wasm — and run it in your Node process with confidence that it can only access the resources you explicitly grant. For plugin architectures, serverless runtimes, and edge computing, this is compelling.
Diagnostics Get Serious#
The feature I’m most excited about is the improvements to the diagnostics tooling. Node 14 introduces experimental Async Local Storage in the async_hooks module, which finally provides a clean way to propagate context across async boundaries without manually threading it through every function call.
If you’ve ever tried to implement request tracing in a Node.js HTTP server, you know the pain. You get a request, you want to tag every log line with the request ID, but your code calls into libraries that call into other libraries, all with async/await or callbacks, and suddenly your nice request context is lost. The common workarounds — using cls-hooked, monkey-patching, or explicitly passing context objects — range from fragile to ugly.
AsyncLocalStorage is the platform’s answer. It’s conceptually similar to thread-local storage in languages with real threads. You create a store, run a function within it, and any async operations spawned from that function can access the store. It works across setTimeout, Promises, async/await, and even event emitters. This is genuinely useful infrastructure for anyone building production Node services.
The diagnostic reports feature, which was experimental since Node 12, has also been promoted to stable. These reports give you a JSON snapshot of the process state — JavaScript and native stacks, heap statistics, resource usage, loaded libraries — that you can trigger programmatically or on specific signals. Think of it as a lightweight alternative to full core dumps that’s actually practical to collect in production.
The module situation#
The ECMAScript modules story continues its slow march toward stability. In Node 14, ESM support is no longer behind a flag — import/export syntax works out of the box (with the usual .mjs extension or "type": "module" in package.json). But “unflagged” doesn’t mean “settled.” The dual CJS/ESM ecosystem remains messy.
The practical reality is that most of the npm ecosystem is still CommonJS. If you’re writing a library, you need to either ship dual CJS/ESM builds or accept that some consumers will have issues. The conditional exports feature in package.json (stable in Node 14) helps — you can specify different entry points for require() and import — but it adds complexity to package configuration that many library authors would rather not deal with.
I’ve been gradually migrating some of my projects to ESM, and my honest assessment is: it’s fine for applications, it’s still painful for libraries. Give it another year. By the time Node 14 reaches end-of-life, I expect the tooling and ecosystem will have caught up. For now, if you’re starting a new application (not a library), ESM is worth considering. For libraries, keep shipping CJS with optional ESM wrappers.
What’s Coming in October#
Node 14 enters Active LTS in October 2020, replacing Node 12 as the recommended production version. This is the real milestone. Right now, Node 14 is in “Current” status, which means it’ll get new features until October, at which point it freezes for stability. If you’re on Node 12 in production, start testing against 14 now — you have six months to identify breaking changes and update dependencies.
The breaking changes are relatively mild this cycle. The major one is the removal of some deprecated crypto APIs and the switch to OpenSSL 1.1.1, which means TLS 1.3 support by default. There are also some changes to fs.promises and stream APIs that might bite you if you were relying on undocumented behavior.
My Take#
Node.js 14 isn’t a release that makes you rewrite your applications. It’s a release that makes your existing applications easier to debug, easier to monitor, and slightly more pleasant to write. The WASI support is the most forward-looking feature, but it won’t be practical for production use for at least a year.
What I appreciate most about Node’s release cadence is the predictability. Every six months, a new major version. Every October, the even-numbered version goes LTS. You can plan for it. After thirty years of watching platforms come and go, I’ve learned that boring and predictable beats exciting and unpredictable every single time.
If you’re running Node in production, start your Node 14 compatibility testing now. Update your CI matrices, run your test suites, check your native addons. October will be here faster than you think, and you don’t want to be scrambling to upgrade when 12 starts winding down.
