What would Node look like if it was written today? In one word: Deno. The JS runtime has Typescript build in and simplifies module resolution. On top, it takes security to the next level and closes the gap between how we write javascript on the backend and the browser.
Not so long ago ...
Released in 2009 node took over the world incredibly fast. Despite the initial skepticism about running javascript in the backend, the community backup was unrivaled. Soon, sophisticated tooling appeared, and years later(2014), Microsoft released Typescript, double betting on Javascript.
Today, Node is one of the most popular choices for backend development. The event-based server philosophy ensures a high performance/throughput ratio. Running Javascript makes is an accessible tool for many developers. In a way, one can say, Node democratized backend development by lowering the barrier of entry. I have been happily using Node in the last five years, but at the same time, I wonder what does the future awaits?
The new kid around the block: Deno
Started in 2018, the Deno project, as the website states, provides a secure runtime for Javascript and Typescript. It is composed of basically two parts: a typescript frontend and a Rust backend. The communication between the two happens by messaging with TypedArrays
.
Deno is a JavaScript/TypeScript runtime with secure defaults and a great developer experience. — The Deno Website
Under the hood, we find a snapshotted version of the Typescript compiler, the V8 engine, and the Tokio event loop. Altogether, shipped as one binary of less than ten MB or as a Rust crate.
Ageing API's
Removing promises from Node back in 2010, helped the community at is early stage. But as javascript started to move faster and faster and introducing the await and async functionalities, Node's APIs started to age.
A considerable effort is made today to bring them up to speed and keep consistent versioning at the same time. Many of the API calls must still be wrapped in constructors like promisify
to be used with the Promise
syntax. This extra step adds overhead to development and increases boilerplate in applications.
In contrast, Promises are Deno's native bindings for async behavior. The Rust backend mirrors the promise objects received from the Typescript frontend with Rust Futures. Async actions in Deno always return a Promise
.
Another noticeable thing about Node is that it relies on Buffer
objects to read and write data. In a step to bring uniformity with browser interfaces, Deno uses TypedArrays
everywhere. Being consistent when reading and writing files across the backend and front end is much easier when using the same data structures.
Typescript with Zero Setup
If you use Typescript, you know it is a remarkable tool. It introduces a type system that can be enforced as applications grow. This reduces the overhead of conventional static typing by providing flexibility. A project can be partially typed in the begging, and type coverage can be extended as the application grows.
In Node, Typescript can be used directly with ts-node
, although one must be careful in production. The safest and most performant choice is to use ts-node
for development. Then compile to javascript for production. The setup for development can be complicated, especially together with other features like hot code reloading.
On the other hand, Deno is all about Typescript. It uses a snapshotted version of the compiler and catches unchanged files. Do you want to run Typescript code? Just run the Deno binary. No config. No hustle. Is that easy, and of course it supports javascript too.
Browser Like Package Resolution
The current resolution scheme of Node overcomplicates module resolution. The algorithm provides flexibility in file location and naming with a considerable tradeoff in complexity.
A require
call would first search for a file with the same name and a .js
, .json
, or .node
extension. If the path specified does not include a leading '/'
, './'
, or '../'
node assumes the module is a core module or a dependency in the node_modules
folder. If the name does not match, a core module node will check the node_modules at that location. If nothing is found, it will get to the parent directory and continue to do so until it reaches the root of the file system.
Additionally, folders can be specified as modules in the package.json
file. The require
function is also aware of the package.json
file of all the folder begins checked. Once a folder is found, Node will look for anindex.js
or index.node
file inside it. The freedom of not having to provide a file extension and the flexibility of package.json
comes at a considerable increase in complexity and decrease in performance.
Deno simplifies the algorithm by providing two types of module resolution, relative and URL based:
import * from "https://deno.land/std/testing/asserts.ts";
In addition, the resolution algorithm does not use package.json
file or the node_modules
folder. Instead of require
, it uses ES Modules imports. This allows us to use a modern approach to code management without the need of a pre-compiler and brings us again closer to how Javascript is used in the browser.
Distributed Package Management
Server-less adoption is at this moment doubling every year. Developers use to split monoliths into microservices. Now we are splitting micro-services into functions. Why? Well, on the one hand, nobody wants to deal with orchestration unless we have too. On the other hand, distributed systems are more flexible and can be changed faster. The bottom line is, applications are becoming systems of smaller and separated parts.
A typical javascript backend application represents 0.3% of the code is using. The rest is made up of packages in the node_modules
folder. And many are hardly used at runtime. At the same time, the whole ecosystem depends on a centralized package manager: npm
.
Deno brings a distributed approach to package management. Packages can be resolved by URL and catched afterward. Applications are lighter and less dependent on a single and centralized package registry.
On Security
When doing backend development, I expect security to work outside the box. The last thing I want to think about is a linter file or node module accessing the network or the file system.
In Deno, internal functions cannot call V8 API's arbitrarily as they do in Node. The communication between Deno's APIs and the JS engine is centralized and unified with messaging based on typed arrays.
Unless specifically allowed, scripts can't access files, the environment, or the network. — deno.land
Scripts executed with Deno can access the file system and network only if the user explicitly specifies it. And even better, permission can be given at file, folder level, or network path level with the —allow flag. This offers developers granular control of read and write actions that happen at runtime.
$ deno --allow-net https://deno.land/std/examples/echo_server.ts
Security by default is a significant upgrade compared to the "trust" policy applied to dependencies one pulls from npn
. With Deno you can run and develop applications with the confidence that they will do what they are expected to.
Summing up
Deno is how Node would look like if it would be built today. It improves security, simplifies module resolution, and runs Typescript.
As I am writing this article, we are still at version 0.33 and growing fast. I am sure if you are here is because you are to some degree using Node or Javascript. If you are like me, you probably love it. But as they say, to love something truly means to let it go.
I am looking forward to seeing Deno grow beyond merely a scripting runtime and to hearing about the first experiences in production. As long as developers continue disrupting ourselves, we can always expect faster, more straightforward, and more reliable software.
Originally published on bogdanned.com.
Top comments (27)
I heard a talk about deno and looked into it a bit. The first thing that stand out to me was there import system. Using urls for import doesn't seem to me a great idea. I cannot imagine anything other than the bad dev experience it would bring. They are using rust for there backend which have a powerful package manager and is one the reason why people prefer rust over any other systems level lang.
I think they have to introduce a package manager sometime in the future and as the ecosystem/community will grow around it, a package manager would play very important role in supporting that.
The idea behind this is that we are already using URLs as imports in the browser all the time. Why not in backend applications? There is also the case of simplifying build and compile processes by eliminating dependencies.
When looking at Deno we must keep in mind it is designed to replace bash scripts with js. That was the original problem to solve. As the community picked it up it seems like is growing to be much more than that.
But aren't we moving away from that by using frontend frameworks? Today at most we use urls for importing images or css. That too we do mostly for internet reasons. At backend it is unnecessary and doesn't make any sense.
I listened the talk by one of the founders and he was kind of clear that they were trying to make a better node And even if it was designed to replace bash script then why compare with node?
"At backend it is unnecessary and doesn't make any sense" -- I would say as things are today, you are probably right. However, with serverless and services getting smaller and smaller I see a strong case for URL imports.
The comparison with Node is natural as they are bot JS runtimes. And both written by the same persona. Ryan Dahl based Deno on what he would like Node to be/have back in 2018.
I think you have a strong point but I am trying to imagine how things will be in five to ten years. And there I see a strong tendency towards decentralized and smaller but independent applications. Some of them which can perfectly run on Deno.
Well in five to ten years it will be used for more than just serverless and services. That's where the problem will start.
Deno is in early stages so I am pretty much sure that they will change some things for good.
Using Deno does not mean you can't use tools like NPM, it means that you are not restricted by it. Currently all code used in Node must be in this central repo, with Deno, no more.
This imports can be manual if you want to, the reality is, we should know what are we importing and not just blindly installing cause the tutorial say so.
EDIT: Something like NPM will probably be implemented by the community after v1.0, just hoping it moves towards Linux package management(repos), its cleaner this way.
Thanks for the writeup. I really admire Ryan Dahl and am rooting for him and for Deno. It is hard for me to see how it will gain mainstream adoption without a "killer feature". While all of these features are good (maybe even great) incremental improvements I am afraid they won't be enough.
The article is well written and polished but looks like you forgot to add a source that you wanted to add:
Spot on Isaac: Deno is still missing that one "killer feature" that would make up for the cost of switching. At the same time, it does offer a glimpse of what the future will look like.
Maybe it will just end up as a "research" space fo the community in order to improve Node.
Thx for the tip adding the source. Cheers!
Deno Compile, that's probably the feature that will pay off the investment.
Admittedly I haven't looked into Deno a great deal yet, but I'm having trouble imagining production level software at this point given that a lot of proven and tested open source frameworks and tools such as express, create-react-app, electron, webpack, and their hundreds of dependencies, assume the use of npm or CJS modules require syntax. Would these packages and their dependencies have to be rewritten?
I hope not, but if so I do feel like we'd be seeing yet another split in the javascript ecosystem and a whole new world of compatibility concerns that would not be a "great developer experience." I've already noticed a number of people creating their own versions of popular packages for Deno and I can't help but feel they might not get the same support as the originals. I wonder if Deno could perhaps add some kind of compatability layer.
A node polyfill is currently under development in the Deno std library. You can see the docs and progress here: github.com/denoland/deno/tree/mast.... Still in it's infancy but growing. Supporting node packages would certainly ease the transition for developers.
Looks like the way to go. Thanks Chris!
That's a really good point. The code amount of code relying on Node is right now huge and the added features do not pay off for now(my opinion).
"I wonder if Deno could perhaps add some kind of compatibility layer." -- I really wonder how that would work. If one could translate the calls to the Node APIs to Deno APIs that might just work as long as we keep performance in mind.
Maybe that's even a project worth pursuing.
In your section "Typescript with Zero Setup": Didn't you want to say that you can execute typescript directly with ts-node?
Since node-ts is something related to TeamSpeak...
Although you can execute typescript code out of the box
ts-node
you still need config files for a proper dev to production flow.On the other hand with
deno
is out of the box. How ists-node
related to TeamSpeak?I just tried to give you a hint regarding a "typo"/error in your post.
node-ts
is not about typescript at all, butts-node
is.ts-node
is not related to TeamSpeak, butnode-ts
is.if you search for
node-ts
in your article and replace all occurrences withnode-ts
the error would be fixed.Thx a lot for spotting that and for the feedback!!!!
I'm not sure that the lack of a package manager is something I consider a feature or benefit. Npm is one of the single greatest things to come out of node. I'm sure living without it in Deno has its advantages, but I could definitely imagine some disadvantages that I'm not sure why I would have to live with
Npm standardized package management for Node. But that itself is the problem.
I see your point, however. As applications grow one wants to be able to understand dependencies easy and for the better or worse package.json was fulfilling that function really well.
Personally I think URL imports can become really important when one builds a service containing several serverless functions. In the case of stateless services, you do not want to write a REST API. So they can just be replaced with a script hosted on S3. Publishing to npm would be in that case too much of an overhead.
Node.js never had promises, not until native came in with V8.
What was called promises in few old versions (last one was v0.1.29) was purely a simple extension to event emitter, nothing more (no
then
, no nested resolution, it had nothing to do with promises which we work with now)You may confirm that at still available documentation: nodejs.org/docs/v0.1.29/api.html#_...
Still, it's true, core Node.js members didn't believe in promise interface even when Promise A+ started to be really popular. It's only after it became a part of a standard, things changed. async/await was delivered in Node.js on par with browsers, soon after we also got
fs.promises
.Anyway to this day (as lighter, and not introducing artificial ticks) callbacks are faster than promises, but for most of real world scenarios difference can be safely ignored.
Thanks for the suggestion. And thanks a lot for the reference to the
v0.1
docs. I will take a closer look and update where appropriate. Much appreciated!You mention
"The setup for development [with node + typescript] can be complicated, especially together with other features like hot code reloading."
Last I checked, Deno has no answer for hot reloading yet. Has that changed?
Great remark Alex! I just run into this file watcher but never tested it. A file watcher should be enough to reproduce what we on
node
withnodemon
.This seems cool. However, I'm looking forward to NodeJS deprecating old APIs and growing. If it's all JS and TS for either runtime, I'd rather stick with something mature.
Deno will be future after Node.
Thanks for the article.
you are welcome Nitin!
Newer dev here looking to future proof his skills by paying attention to Deno. Mind ELI5 some of the benefits? Some of the terminology is confusing. Super interested in the future of this!