Deno 2.0: Is Node Toast Now?

javascript9 min

byLucas Santos

This page was machine translated. Read original / Suggest a fix

In 2020 I went on one of the Hipsters.tech episodes where we talked a lot about what this Deno thing was, the new tool that was popping up and promising to replace Node.

Deno, o novo Node? - Hipsters #203 - Hipsters Ponto TechAdmin

Four years later, Deno finally hit version 2.0, and this time I think it actually has what it takes to replace Node.js. Let me tell you why I think that.

What is Deno?#

Before we get into it, what is Deno? A year ago Roz and I gave a talk about it at Iugu Talks4Devs. It’s worth checking out:

Play

In short, Deno is another runtime created by Ryan Dahl, the same creator of Node.js. The idea is that Deno showed up to fix the problems he thought were hurting Node.js, including having a private package manager like NPM.

Deno is written in Rust and runs TypeScript natively, so you don’t need a build step or anything like that to run your code. It’s as if it automatically understands everything you want to do with TypeScript and interprets the code straight away as JavaScript. But I won’t go on about that here, we have an article on the blog that’s just about Deno:

Let’s talk about Deno

Deno 2.0#

Deno 2.0 still stays true to the features it originally set out to provide, and the problems it originally set out to solve. Even though it lost its way a bit along the road (we’ll get to that), it’s stayed pretty consistent with what it promises and what it delivers.

💡

A fun fact: Deno is the second most popular Rust project on GitHub, only behind the Rust language repository itself.

Deno’s main pitches were:

  • It would have native TypeScript support, meaning you wouldn’t need to install TypeScript or any other library like TSX to run TS in the runtime
  • Built using Web Standards, meaning it would have implementations that strictly followed TC39 and IETF proposals. This is so true that Deno was the first runtime to implement Temporal, but not just that, Promises, Fetch, ESM and many others are already implemented natively
  • It would have all the tools needed to build and maintain a project long term, like: a linter, a formatter, a type checker, a testing framework, the ability to compile to an executable, and more
  • Security first, with permission systems and so on

And that’s still true, which is impressive, because usually when runtimes like this grow (as is the case with Node), compromises end up getting made, and the original pitches sort of get lost in community requests and ideas.

Deno 2.0’s features are pretty interesting, I’d even say they’re quite bold:

  • Full Node and NPM support, so you can run any Node.js application on Deno with zero extra configuration
    • This includes native support for package.json and node_modules
  • The addition of a package manager you can call with deno add, deno install, and remove packages with deno remove
  • The standard library (stdlib) is now officially stabilized and at version 1.0
  • Support for private NPM registries
  • Support for workspaces and monorepos
  • Deno will now have a release calendar and LTS support
  • The creation of JSR, a package registry similar to NPM that supports multiple runtimes

On top of that, other features that already existed are now better:

  • deno fmt now also formats HTML, CSS, and YAML
  • deno lint now has Node-specific rules and can auto-fix errors with deno lint --fix
  • deno test supports the Node.js Test Runner
  • deno task also runs package.json scripts
  • The docs generated by deno doc now look nicer with a better layout
  • deno compile supports signing packages and icons on Windows
  • deno serve now supports running the server using multiple CPU cores in parallel
  • deno init can scaffold projects, so you can have templates for libs and servers
  • deno jupyter, for people using jupyter notebooks, now outputs images, charts, and HTML
  • deno bench, for running benchmarks, has more accurate measurements
  • deno coverage has HTML test coverage output

I’m not going to go through every single change, that’s not what this article is about. The idea here is to talk about what’s new and what it means for Deno and for the runtime ecosystem as a whole. But if you want me to make more content about this, hit me up on my social media and let me know!

Opinions#

Overall, I liked this new version of Deno quite a lot, but there are a few things I found a bit strange. The first one is the support for package.json and node_modules, things that were explicitly called out as bad about Node and that wouldn’t be implemented in this new runtime. And yet, here we are with full Node support.

While I get that, without this, there’s no way Deno could compete with Node at all, because the Node.js ecosystem is simply way bigger than Deno’s, and so is the community. So it was kind of necessary for the Deno folks to implement compatibility so people already using Node could use Deno directly, with zero configuration, and that includes having to support Node’s environment.

Nobody likes node_modules, that much is clear from all the memes about it.

Heaviest Objects In The Universe : r/ProgrammerHumor

So the solution here was to actually support both package.json and node_modules, but not make them mandatory. That was a smart move. If you want to use NPM packages but don’t want node_modules installed, you can just replace all your import statements like:

import Express from 'express' // express is a dep in package.json

With the npm: prefix instead:

import Express from 'npm:express'

Now you don’t need Express in package.json anymore, or to install anything in node_modules, because Deno takes control of that package and installs it in its cache. In bigger projects you can use import maps (a web standard) to map the package name to something else (which is essentially another kind of package.json, but it helps you know what’s installed):

// deno.json
{
"imports": {
"express": "npm:express"
}
}

And that brings me to another topic: deno.json itself, which was one of the original pitches, not having a JSON file that defines the whole project. While it’s not needed or mandatory (unless you want to publish something on JSR), it’s kind of become a best practice to have that file around. And that is, basically, another package.json.

JSR and Deno PM#

Deno followed a bit in Bun’s footsteps by bundling a package manager into the binary, which I think goes completely against what they originally proposed (there’s even a blog post from Ryan Dahl himself about it). Personally, I really like the direct URL import model we had before (and, I believe, still have in Deno), which is a more decentralized way to manage your packages, one that doesn’t hand control over to private organizations that could simply remove, change, or even take over a public package.

This is so true that there are several examples of it happening, where companies have full control over public packages, like the recent case of WordPress taking over (almost hijacking) one of the most used plugins in the ecosystem. Swap WordPress for NPM, Deno, JSR, or anything else, and you get the same result. Sure, it all depends on how these companies behave, and the WordPress case really is a separate one because there’s a key person who controls most of it, but it’s still a possibility.

On the other hand, centralizing packages like this makes the ecosystem safer, and it prevents problems like what happened with Faker and Left-pad (which shows a completely different and much more serious problem, but let’s stick to this story). In both cases, NPM managed to step in and forcibly take control of the packages, republishing and/or restoring previous versions. That’s an example where having full control is a good thing, but only if the company in control has good intentions. But the question that remains is:

Which company has good intentions?

Back to Deno, creating JSR and using Deno as a package manager isn’t necessarily a bad thing, because it basically connects to a bunch of registries and acts as a client for each one to download packages from multiple different places. Deno kind of became the yarn here, it even compares itself to them:

The idea is solid. For example, if you run deno install:

  • If you have a package.json, it behaves like npm install, creating a node_modules and installing all the packages inside it
  • If you don’t have a package.json, it caches all the dependencies in the global cache, the same thing we used to do with deno cache

This means you can literally use any kind of package from any source you want. You can use packages from JSR with import x from 'jsr:package@version' alongside import x from 'https://url.com' and import x from 'npm:package@version'. As interesting as that is, I can already imagine the mess this is going to be in big projects.

Is This the End of Node.js?#

For the first time, I have to say I honestly don’t know. With Deno’s adoption growing bigger every day, and Node.js’s inability to implement the same features at the same speed, I do see a lot of people ending up migrating to Deno, but I don’t think it’s the end of Node.js.

Even though it’s slower, Node.js has been pulling off some big feats and is implementing some really interesting things, like support for running TypeScript files with --experimental-strip-types and --experimental-transform-types. The addition of the Node Test Runner, improvements to these packages, and plenty of other things being added as we speak.

But I don’t think all these additions are enough to keep the project alive. Node is a project with a lot of dependencies, a lot of people involved, and a lot of politics too. Plenty of companies depend on this project and are trying to steer it their own way through committee members. Eventually, Deno (or Bun, or whatever runtime comes next) is going to show up with a feature we can’t deny, for example, in Deno’s case, being able to compile everything into a single binary is something incredible to me, and a lot of people feel the same way, but that’s rarely going to make it into Node.

Conclusion#

Deno 2.0 comes with a lot of good stuff and it’s genuinely worth trying out, maybe even rolling it out on a project and letting it run for a while. I’m going to run some tests with Node.js projects I have, and who knows, maybe I’ll post the results here, or in my newsletter!

If there’s anything you want to see here, don’t forget to hit me up!