From Node.js to Deno
1) Small standard library. There is only a very small standard library included with Node.js. It includes modules to do only the most essential tasks, such as interactions with the file system and the operating system. This has lead to a huge number of third-party modules being created for things that, in my opinion, should've been included in the standard library. A lot of these modules are also very compact, sometimes containing just a few lines of moderately complicated code. That, in turn, has caused a dependency explosion: every third-party module you install depends on dozens (often even hundreds) of other small dependencies. This is of course how it is supposed to work (also in other languages), but the sheer number of indirect dependencies that a node.js module might import is a huge security risk. Bad actors can take over a module (as has happened several times before) and deploy malicious code that is then automatically picked up by your continuous integration system.
Deno works out-of-the-box with Typescript, meaning that no transpiling has to be done as is the case with Node.js. Many other things such as debugging also work without the need for any kind of setup. This alone is already a major selling point for Deno in my opinion.
Another big improvement over Node.js is the standard library, as mentioned a few paragraphs back. While it would be unfair to say that node has no standard library (it has some utilities to interact with the operating system and to replace browser APIs), it definitely leaves quite some room for improvement. Take for example the python standard library; it contains pretty much anything that a developer will need for a reasonably sized application. Python's standard library enables a developer to work on actual features, without having to worry about basic stuff such as command line argument parsing or unit testing. It also allows for safer applications, because there's less third-party packages that can become abandoned, break or contain malicious code. That is exactly what a standard library should aim for.
Finally, Deno also comes with batteries included. It comes with linting, a language server, application bundling, unit testing and formatting all built in. This makes for a very smooth development experience. You can just get started with a Deno project, no configuration needed (reasonable defaults are already provided).
While deno itself works without issues, the surrounding ecosystem is not completely ready yet for prime time. There is no official docker image yet, for example. Building your own deno base image is rather simple however:
FROM debian:10-slim # Install deno RUN apt update -y \ && apt install -y --no-install-recommends curl ca-certificates unzip \ && curl -fsSL https://deno.land/x/install/install.sh | sh \ && mv "$HOME/.deno/bin/deno" /usr/bin/deno \ && apt remove -y curl ca-certificates unzip \ && apt autoremove -y \ && rm -rf /var/lib/apt/lists/*
The end result is an image of about 150 MB.
Setting up the development environment is a smooth process. The installation of deno itself is quite easy, you basically just copy a bash command from the deno website and the install script does everything else. On linux there are still some manual operations needed, which are explained by the install script though. The extension for the VSCode editor works adequately, but it still has some rough edges here and there. I still had to set the path for the deno executable myself, and sometimes the editor wouldn't properly propagate typing changes to the language server.
Deno itself works like a dream, as mentioned before. There's no need for any setup, you can just start developing. The deno workflow is basically: 1) create an
index.ts file, 2) fill it with some typescript code and 3) run it with
deno run index.ts.
Most of the typescript code from the node project could just be copy-pasted without issues. I even managed to remove some dependencies, such as the logger (which was already included in the deno standard library). On the other hand, for some external dependencies I did have to write my own replacements as I could not find any good third-party packages. In the old project I used convict (a configuration library), and a kubernetes API library, both which had no good replacements. Writing the drop-in replacements for those libraries took some extra time, which wasn't an issue originally while developing the node project.
One significant difference between node and deno is that deno has implemented a coarse permission system. In order to allow a deno program to access a file or connect to a website you need to specify permissions in the
deno run command. I didn't have much trouble with this system, since I only encountered the file system permission and the network permission. It seems like a good system from the limited experience I had with it, but I will refrain from any more opinions for the time being until I've learned more.
In the end, I've come to the following conclusions regarding deno, and when you should use it. Deno, as it is right now, is very useful for small greenfield projects that don't use much (if any) dependencies. The standard library and the third-party modules have not matured enough to use it for larger projects. For those kinds of projects one should still use Node with Typescript, as the benefits still outweight the security risks and the lack of standard library.
However, in a year or two, once Deno has done some maturing and there are more stable third-party packages available, then it might be a good idea to give Deno a good look again when starting a new large software project (where previously you would've used Node).
Small sidenote: Converting old node projects to Deno might not be worth it even then, since the third-party modules cannot just be ported from one runtime to the other. Also, you will have to switch all Node-specific code to Deno-specific code.