Deno 2.0: Is Node.js Already a Thing of the Past?
In 2020, I participated in an episode of Hipsters.tech where we talked extensively about Deno, the new tool that was emerging at the time and promised to be a Node replacement.
Deno, o novo Node? - Hipsters #203 - Hipsters Ponto Tech…AdminFour years later, Deno finally reached version 2.0, and this time I believe it really has the capacity to be a Node.js replacement. But let me tell you why I think so.
What is Deno?#
Before we start, what is Deno? A year ago, Roz and I gave a talk about it at Iugu Talks4Devs. It’s worth checking out:
In summary, Deno is another runtime created by Ryan Dahl, the same creator of Node.js. The idea is that Deno came to fix the problems he thought could be harmful to Node.js, including the presence of a private package manager like NPM.
Deno is written in Rust and runs TypeScript natively, so you don’t need a compilation step or anything like that to run your code. It’s as if, automatically, it understands everything you want to do with TypeScript and interprets the code as JavaScript directly. But I won’t dwell on that here, we have an article on the blog that talks only about Deno:
Deno 2.0#
Deno 2.0 still remains faithful to the functionalities it initially came to provide and the problems it initially came to solve. Although it got a bit lost along the way (we’ll talk about that soon), it has remained quite consistent in what it promises and what it delivers.
A funny fact is that Deno is the second most popular project on GitHub written in Rust, only behind the language’s own repository.
Deno’s main propositions were:
- It would have native support for TypeScript, which means you wouldn’t have to install TypeScript or any other library like TSX to run TS on 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 only that, Promises, Fetch, ESM and many others are already implemented natively
- It would have all the necessary tools to create and maintain a project long-term such as: Linter, Formatter, Type Checker, a testing framework, the ability to compile to an executable, and more
- Security first with permission systems and more
And that remains true, which is impressive because typically when runtimes like these grow (as is the case with Node), compromises end up being made so that the original proposals get somewhat lost in community requests and ideas.
Deno 2.0’s features are quite interesting, I’d say they’re even pretty bold:
- Full support for Node and NPM, so you can run any Node.js application on Deno without any extra configuration
- This includes native support for
package.jsonandnode_modules
- This includes native support for
- The addition of a package manager that you can call with
deno add,deno install, and remove packages withdeno remove - The standard library (stdlib) is officially stabilized and at version 1.0
- Support for private NPM registries
- Support for workspaces and monorepo
- Deno now will have a release calendar and LTS support
- The creation of JSR, which is a package registry similar to NPM that supports multiple runtimes
Beyond that, other existing features are now better:
deno fmtnow also formats HTML, CSS, and YAMLdeno lintnow has Node-specific rules and can fix errors automatically withdeno lint --fixdeno testsupports the Node.js Test Runnerdeno taskalso runs scripts frompackage.json- Documentation generated by
deno docis now prettier with a better layout deno compilesupports signing packages and icons on Windowsdeno servenow supports running servers using multiple CPU cores in paralleldeno inithas scaffolding capability, so you can have templates for libs and serversdeno jupyterfor those using jupyter notebooks now has output for images, charts, and HTMLdeno benchfor running benchmarks has more precise measurementsdeno coveragehas HTML test coverage output
I’m not going to go through each change one by one, this article doesn’t have that focus. The idea here is for us to discuss these updates and what they mean for Deno and the broader runtime ecosystem. But if you want me to create more content about this, reach out to me on my social networks and let me know!
Opinions#
Overall, I quite liked this new version of Deno, but there are some things I found a bit strange. The first is support for package.json and node_modules, which were things explicitly said to be bad in Node and wouldn’t be implemented in this new runtime. Yet here we are with full Node support.
While I understand that without this, Deno can’t compete with Node in any way because the Node.js ecosystem is simply much larger than the Deno ecosystem, and so is the community. So it was somewhat necessary for the Deno team to implement compatibility so that people already using Node could use Deno directly without any configuration, and that includes supporting the Node environment.
Nobody likes node_modules, that’s clear from various memes.

So the solution here was to really support both package.json and node_modules, but not force them to be mandatory. That was a smart move. If you want to use NPM packages but don’t want node_modules installed, you can simply replace all import declarations like:
import Express from 'express' // express é uma dep no package.jsonTo use the npm: prefix:
import Express from 'npm:express'Now it’s no longer necessary to have Express in package.json or install anything in node_modules because Deno will take control of that package and install it in cache. In larger projects, you can use import maps (which are a web standard) to map a package name to another name (which is essentially another type of package.json, but helps you know what’s installed):
// deno.json{ "imports": { "express": "npm:express" }}And that brings me to another subject: deno.json itself, which was one of the original proposals to not have a JSON file that defines the entire project. While it’s not necessary or mandatory (unless you want to publish something to JSR), it has kind of become a good practice to have that file there. And that’s basically another package.json.
JSR and Deno PM#
Deno followed Bun somewhat by including a package manager inside the binary, which I think completely goes against what they initially proposed (there’s even a blog post from Ryan Dahl himself about it). Personally, I really like the model of direct URL imports that we had before (and I believe we still have in Deno), which is a more decentralized way to maintain your packages and not hand over control to private organizations that can simply remove, change, or take over a public package.
This is so true that there are several examples of this happening, where companies have total control of public packages, like the recent case of WordPress that took over (almost seized) one of the most used plugins in the ecosystem. Replace WordPress with NPM, Deno, JSR, or anything else and you get the same result. Of course, it all depends on how these companies behave, and the WordPress case is indeed a separate case because it has a key person who controls most things, but it’s still a possibility.
On the other hand, centralizing packages this way makes the environment more secure and avoids 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 was able to intervene and forcibly take control of packages, republishing and/or restoring previous versions. This is an example where having total control is a good thing, but only if the company that has it has good intentions. But the question that remains is:
What company has good intentions?
Getting back to Deno, the creation of JSR and using Deno as a package manager isn’t necessarily a bad thing because it basically connects to many managers and is used as a client for each one to download packages from multiple different places. Deno kind of became yarn, and it even compares itself to them:

The idea is good, for example, if you run deno install:
- If you have a
package.json, it will behave likenpm install, creating anode_modulesand installing all packages inside it - If you don’t have a
package.json, it will cache all dependencies in the global cache, the same thing we did withdeno cache
This means you can use literally any type of package you want from any source you want, meaning you can use packages in JSR with import x from 'jsr:pacote@versao' along with import x from 'https://url.com' and import x from 'npm:pacote@versao'. Despite being interesting, I imagine the mess this will be in large projects.
Will it be the end of Node.js?#
For the first time, I have to say I really don’t know. With Deno’s adoption increasing every day and Node.js’s inability to implement the same features at the same pace, I see that many people will end up migrating to Deno, but I don’t think it’s the end of Node.js.
Despite being slower, Node.js has done great things and is implementing very interesting features like support for running TypeScript files with --experimental-strip-types and --experimental-transform-types. The addition of Node Test Runner, improvements to these packages, and many things being added as we speak.
But I don’t think all these additions are enough to keep the project afloat. Node is a project with many dependencies, many people involved, and a lot of politics too. Many companies depend on this project and are trying to guide it for their own benefit through committee members. Eventually, Deno (or Bun, or the next runtime) will show up with a feature we can’t deny, for example, in the case of Deno, the ability to compile everything to a binary is something incredible to me, and many people think the same, but that will rarely reach Node.
Conclusion#
Deno 2.0 comes with many good things and is really worth testing, maybe even implementing in a project and letting it run for a while. I’ll do some testing with Node.js projects I have, and maybe I’ll post the future results here or in my newsletter!
If you want to see something here, don’t forget to reach out to me!