Node.js in 2020

I’ve been using Node.js for a while now, and I never really liked it much. After re-evaluating it my opinion changed. Let’s take a look at what Node.js has to offer in 2020.

In the Olden Days

If you’ve used Node.js in the past you might be familiar with most of its shortcomings. Callback hell, unstable packages, an ever-changing library hellscape that makes you question your every move since the tool you’re using might be obsolete the next day.

Apart from these, npm also had some problems that made it painful to use.

To top it all off, it uses Javascript that’s a controversial language. It lacks some of the basic constructs that other languages take for granted (a good example is Java’s collections framework that’s a superb example of how to make great data structures for your standard library) and it also has an arcane type coercion algorithm:

Javascript Type Coercion

As a fun fact let’s take a look at the definition of the word

Coercion: persuade (an unwilling person) to do something by using force or threats.

All in all it was a sub-par experience for someone who came from the enterprise world. A lot changed since then and now, that I took a long hard look at it I have to say that my opinion changed. Let’s see some of the improvements that were made.

ECMAScript 6 and Later

Back in the day all we had was the ECMAScript 5 specification that was considered a huge imrovement over ES4. Looking back this sounds funny, but it was true at that time. When I wrote my first Node.js program ES6 was still just a draft.

This led to a very upsetting experience including my pet peeve, the callback hell:

firstFunction(args, function() {
  secondFunction(args, function() {
    thirdFunction(args, function() {
      // ...

Promises & async/await

In ES6 they introduced Promises, that were later augmented with the async/await keywords. With these constructs the callback hell is all but eliminated. Now we can make these functions async and write this:

await firstFunction();
await secondFunction();
await thirdFunction();

Gone are the days of looking at callbacks nested 10 levels deep…


As most of you probably agree with me, hoisting is one of the best features of Javascript (just joking). In 2020 it is still there but now we have the option not to use it: let and const.

These keywords use block scoping and hoisting doesn’t apply to them. If we try to use a variable that’s not defined we get a proper Error now.

let x=5;

// oops, we get an Error in line 1

We just have to refrain from using var.


Another pain point was the usage of this. This was usually problematic within functions where we always had to give careful thought to what this (current context) refers to. This leads to a lot of mental pagefaults throughout the day which sours the developer experience.

With arrows this is less of a problem now, as this will stay bound to whatever it was bound to before. We don’t have to think about it within the body of an arrow.

Although arrows are no panacea, they still reduce the pain points significantly if we use them where appropriate:

[1, 2, 3].map(num => num * 2);


To me using require.js (and similar solutions) was an exercise in frustration. There were a lot of competing specs and it was hard to choose from them. Now we have a native way of using modules with proper import and export statements!

ES6, and later specs also come with a plethora of very useful features including but not limited to default parameters, destructuring, and proper data structures.

I observed a trend in Javascript’s evolution. There is no point in salvaging those things that were broken for a very long time. It would just lead to a lot of pain as people would scramble to fix age-old codebases to prevent them from failing when the deprecation cycle is complete.

What they did instead is introducing new means to do the same things (like var vs const + let). This accretion of features reminds me of Rich Hickey’s great talk (link here) where he talks about the importance of keeping APIs immutable.

This leads me to my next topic:


If you have a language that’s plagued by legacy problems and you successfully wall off the parts that don’t work well all you need is some tooling to enforce the exclusion of their use in the codebase.

Eslint can do this. It is a code linter tool that’s been around for a while and now it is possible to simply break the build whenever something funky is detected in the codebase.

Wait…break the what? Well, all of this we’ve talked before would be useless if there was no way of using modules and the rest of the newest features in any codebase. Enter Babel, the compiler for Javascript.


Babel is a tool that will consume your next gen Javascript code and spit out any code you desire. You can make it generate ES3 compliant code if you really want to. What makes this tool really great is that it also supports plugins.

You can enable features of Javascript, the module system, minification, JSX and much more. We can also combine Babel with Eslint to help us write better and cleaner code.

This will also work for backend and frontent projects, so now it is much easier to write isomorphic Javascript projects with shared codebases.

Even though Eslint helps with making Javascript less error-prone, now we have an alternative language as well, that’s a superset of Javascript: Typescript.


To me the advent of Typescript was the best part of this whole process. This language adds proper types to Javascript which makes the whole platform enterprise-ready in my opinion. With types it is much easier to understand a piece of code as they restrict some of the funky hacks that exist in Javascript and also enable developers (and tools!) to understand the code just by looking at it. Sometimes it is enough to just look at the API. Prior to this, it was rather difficult.

Moreover there is the @types namespace on npm that adds Typescript headers to a lot of the libraries out there. The interoperability with existing code is superb, thus adopting this language in a Javascript project is simple.

We’ll explore this Typescript migration in depth in a later article.

Typescript also adds some features to the mix apart from the types: interfaces, structural typing, union & intersection types, type aliases, generics and null safety. The list goes on.

Typescript is very similar to Kotlin. Both languages are hosted and an upgrade over the old language that runs on it (Java and Javascript respectively). They solve problems that programmers are struggling with every day thus leading to a much better developer experience.

Honorable Mentions

I overheard a conversation a few days ago. A programmer was complaining about the lack of new libraries in the Javascript ecosystem. They thought that it is alarming that they have been using the old tools for too long and there is no innovation.

I also observed that there are some tools that have been there for a long time and a status quo started to emerge. To me this is a sign of maturity. I can add all these tools above to my project and I can rest assured that they will be present a year from now. Libraries have started to stabilize and this is great news.

Another thing that helps the ecosystem is Visual Studio Code. I’ve used a lot of IDEs throughout the years including Eclipse, NetBeans, IDEA and so on. I’m using IDEA and VS Code almost every day and I can say that they are almost on par when it comes to features. VS Code enhances the developer experience for me so much that I’ve started to enjoy using it.


When Node.js came out it was not something that I could have compared to the web development tools I was using. More than 10 years have passed since then. The platform received so many upgrades that I think it is time to re-evaluate it for all those people who dismissed it a long time ago when it was indeed a sub-par experience to work with it. Right now it is in the sweet spot even for enterprise projects with the help of Typescript. In the upcoming articles we’ll look at how to set up Node.js projects with Typescript and how to migrate Javascript code. Until then…

Go forth and kode on!