Node.js Just Released Version 20! WTH?!

Let’s see what’s new with Node v20!

Fernando Doglio
Bits and Pieces
Published in
8 min readApr 24, 2023

--

Let me first start by stating that I started using Node.js when it was on version 0.10 (yes, you read that right!).

Writing this article kinda makes me feel old.

With this new release, some of my suspicions are starting to be confirmed. Back when I wrote about version 19 of Node.js, I suggested the team might be “getting inspiration” from what Deno is doing with their CLI, and lo and behold, I was not wrong!

Don’t get me wrong, this is by no means a bad thing, in fact, it shows the team behind Node is capable of adapting and recognizing where the industry is going and what the users want, even if it’s not something they were thinking about before.

That said, let’s see what’s new with this new version.

💡 As an aside, have you ever found yourself duplicating code across multiple Node.js projects, or struggling to manage dependencies and versions? Bit can help.

Learn more:

We now have a permissions model

Even though this feature is still experimental and you have to use a special flag to enable it (i.e the --experimental-permissions flag), this feature is the beginning of a more secure Node sandbox.

And this is what I mean by having the Node team “getting inspired” by the Deno team. Deno has had this from day 1, and lots of Node developers bashed it at first.

As Node devs, we’re used to simply writing to disk if we feel like it, or reading a file if we need to. Who cares if we actually should be able to do it or not, right?

Wrong.

In fact, others have taken advantage of that lack of permissions in the past many times (here is one example of that).

Having inherent permissions on the entire system to perform things like writing or reading from disk, or even spawning new child processes can be a huge security issue. If your code is not secure enough, you could be opening a gate for hackers to attack the system your code is running on.

Through these permission flags, even if you’re not careful enough and your code can be exploited, the Node runtime won’t let it get out of hand.

And that’s fantastic! If you think about it, it’s kinda crazy we’re now using the insecure version of Node in so many production systems. I foresee this being a mandatory update once the feature is marked as stable.

With that said, the currently available flags are:

  • --allow-fs-read & --allow-fs-write which clearly enable reading and writing to and from the OS. You can use them to enable full access or specify a path and control what can be read and written (more secure that way).
  • --allow-child-process which as the name suggests, lets you create a brand new child process from Node.
  • --allow-workers which enables access to worker threads.

All those features are automatically disabled the minute you use the --experimental-permissions flag, so you’ll have to specify them when executing your script if you want to have access to them.

On a personal note, I’m still finding this permission model a bit lacking, since other potentially dangerous actions, such as accessing network resources, are still not part of it. I’m hoping that changes with future versions.

You’ll also have access to the process.permissions.has() method to check, programmatically, if you can actually perform the actions you want. That way you can gracefully react to the lack of permissions instead of letting the entire script crash.

This is a major update and one of the most relevant new features of this release. With a little bit of luck, it becomes stable sooner rather than later and the entire Node ecosystem becomes more secure automatically.

We finally have a stable, native, test runner

I mean, sure, if you’re already using Jest or Mocha, why would you care about this?

However, if you’re starting something new, taking a look at this new test runner might be a good idea. Mainly because you’re reducing dependencies.

OK, I’ll admit it, the current version seems a bit barebones compared to other, more mature libraries like Mocha.

You only get basic methods to write your tests, the ability to mock methods and watch for file changes to re-test the code.

Then again, this is the first time this test runner is marked as stable, so expect more features to be added in the future. This is, after all, another great idea that other languages and *cough* runtimes like Deno *cough* have implemented in the past.

If I had to guess, I would say this is not the last time we hear about the test runner on the change logs of future Node versions.

Did you like what you read? Consider subscribing to my FREE newsletter where I share my 2 decades’ worth of wisdom in the IT industry with everyone. Join “The Rambling of an old developer” !

With a new version of Node comes a new version of V8

During version 19 of Node, we saw the upgrade of V8 from 10.2 to 10.7, however, this time around we’re jumping to version 11.3 which comes with some interesting benefits.

Out of them, the most relevant and interesting ones are:

Our strings gained 2 new methods: isWellFormed and toWellFormed

If you’ve been working heavily on integrating Node with other external applications or doing some low-level FileSystem activities, you might’ve run into some trouble processing UTF16 strings.

These two methods allow you to check if those strings are properly formed (through the isWellFormed method) and to fix them if they’re not ( toWellFormed ).

Granted, many of you (me included), aren’t likely to take advantage of them, but if you’re in the group that will, I’m sure you’re happy about this change!

You now get extra Array methods in case you’re one of those zero-mutations developers

If you’re one of those freaks (j/k I love you all) that want to avoid mutations at all costs, then you probably hate arrays.

Methods like sort , splice and reverse affect the original Array, effectively mutating it.

Now with this new version of V8 and Node, you get new methods to replace those you hated, that actually keep the original array intact:

  • Array.prototype.toReversed() -> Array
  • Array.prototype.toSorted(compareFn) -> Array
  • Array.prototype.toSpliced(start, deleteCount, ...items) -> Array
  • Array.prototype.with(index, value) -> Array

Rejoice in your mutation-free utopia, you’re now happy.

You get more powerful RegExps

If you’re a fan of Regular Expressions, then you’ll love this update.

The newly added “v” flag allows you to perform some interesting operations.

You can use it to match Unicode multi-code point characters (like Emojis):

const re = /^\p{RGI_Emoji}$/v;

// Match an emoji that consists of just 1 code point:
re.test('⚽'); // '\u26BD'
// → true ✅

// Match an emoji that consists of multiple code points:
re.test('👨🏾‍⚕️'); // '\u{1F468}\u{1F3FE}\u200D\u2695\uFE0F'
// → true ✅

Without the “v” flag this would not be possible, the second test fails on previous versions of Node.

You can also do difference or group subtraction (i.e match anything that’s on A and not on B ):

/[\p{Script_Extensions=Greek}--[αβγ]]/v.test('α'); // → false

That example would match (thanks to the -- operator) any Greek letter except the alpha character.

You can also do intersections (matching all characters from both A and B ) with the &&:

const re = /[\p{Script_Extensions=Mongolian}&&\p{Number}]/v;
// U+1817 MONGOLIAN DIGIT SEVEN
re.test('᠗'); // → true
// U+1834 MONGOLIAN LETTER CHA
re.test('ᠴ'); // → false

The above code matches all Mongolian numbers, but not their letters.

If you want to know what else adds the new “v” flag, continue reading here.

Web Assembly gets tail call optimization

If you are into WASM, then this is probably a great update for you.

Normally, when a function calls itself recursively, a new stack frame is created for each call, which can quickly use up memory and slow down the program.

With tail call optimization, however, the compiler or interpreter recognizes when a recursive call is the last operation in a function and can replace it with a jump to the beginning of the function, rather than creating a new stack frame. This allows the program to reuse the same stack frame for each recursive call, saving memory and making the function faster and more efficient.

In simpler terms, tail call optimization is a way to make recursive functions faster and use less memory by avoiding the creation of new stack frames.

Other, less notable updates

While the above points are the ones that make me want to update my current version of Node immediately, there are some other improvements that are well worth a mention:

  • A dedicated performance team was formed to improve, well, performance, on the Node runtime and apparently they’ve already made some great progress regarding the parsing of URLs and the fetch function. Keep an eye on that if you’re making heavy use of those features.
  • There was some more work done on the capability of creating a single executable application (sort of a compilation into binary, kind of). You can read more about that here. If you ask me, that sounds an awful lot like Deno’s compile command, but hey, that’s just me!
  • There was some work done to improve the compatibility of the Web Crypto API implemented in Node with the actual standards that everyone uses (yay for standards!).
  • There is now native support for ARM64Windows architectures for the NodeCLI.

And that’s it!

If you ask me, these updates are really exciting and some of them show progress toward a more secure and stable platform.

Will these changes bridge the gap between Deno and Node? I don’t think so, but it’s great to see how the team is taking a cue from others in the same space.

If I’m not mistaken, the list of similarities now includes:

  • A native watch mode added on version 19
  • The stable test runner on version 20
  • The experimental permissions model on version 20
  • A potential compile command to create Single Executable Apps from your code coming soon.

And of course, all the other updates are fantastic as well. I especially liked the Regular Expressions one, but that’s because I’m a bit fan of them.

What about you? What’s the most exciting part of this new version?!

Build Apps with reusable components, just like Lego

Bit’s open-source tool help 250,000+ devs to build apps with components.

Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.

Learn more

Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:

Micro-Frontends

Design System

Code-Sharing and reuse

Monorepo

--

--

I write about technology, freelancing and more. Check out my FREE newsletter if you’re into Software Development: https://fernandodoglio.substack.com/