Modernizing Node.js with idiomatic JavaScript

This blog post was written by Peter Marshall, Software Engineer at Google, V8 and Arunesh Chandra, Sr. Program Manager at Microsoft, Chakra

Until recently, it was hard to write JavaScript that worked across all browsers. The different JavaScript virtual machines found in each browser varied so much in their behavior that developers often put explicit checks in their code for each browser, and entire frameworks came and went that were devoted to allowing developers to write their code without these workarounds. These days, thanks to a massive investment in web standards, writing the same code and being able to run it in all major modern browsers has never been easier. Today on the web, we have more or less achieved uniformity across implementations in terms of the behavior of the JavaScript language, which is a huge win for developers and users. However, in terms of techniques for optimizing JavaScript performance, developers have had a harder time, which is where we still have some work to do.

Performance in JavaScript is a huge area of focus for browser implementers and developers alike. JavaScript is faster than ever, and performance work continues to deliver huge wins to end users and the web in general. There is still one question that remains unanswered: How can we decide which JavaScript to make fast? In other words, if developers want to improve the performance of their JavaScript, what sort of code should they write?

In the past, performance advice was often presented as anti-patterns, for example “Don’t use for-each” or “Don’t use try-catch”. But developers want to use the latest and greatest features of their language. This leads to the same sort of special-casing that we saw 10 years ago, but for performance reasons instead of behavioral reasons. One place where we really see this ‘specialized’ code appear is in code written for Node.js.

Node.js is a server-side JavaScript runtime that has become extremely popular over the years. For much of its life, Node.js embedded the V8 engine as the only option to run JavaScript code. This meant that to squeeze the best performance out of their applications, developers often tailored their code to the specific version of V8 in a given release. This is not only true for applications written for Node.js, but for Node.js core itself, which implements a lot of the features of Node.js using JavaScript. This led to a lot of code being written and be heavily optimized to run on a previous generation of V8’s optimizing compiler, Crankshaft, to generate fast machine code on-the-fly.

Fast-forward to 2018 — Node.js can now also be powered by the ChakraCore JavaScript engine and V8 has a new optimizing compiler Turbofan, both of which have a very different performance profile to Crankshaft. The problem for Node.js is that the codebase can sometimes lag behind the advancements in JavaScript compilers.

So now the question becomes what should we do about this problem? The answer is straightforward: JavaScript developers should focus on writing idiomatic, readable and maintainable code and not focus on the compilers underneath. As an example, consider this method for detecting whether a string contains any disallowed characters:

This method avoids regular expressions, splits the first few iterations of a loop, and deliberately stays under a particular character count so that Crankshaft would inline it. Ideally, developers should not have to worry about any of those micro-optimizations, instead they should be able to write something much simpler like the following, and trust that the compiler will do the right thing for performance:

We believe that instead of developers specializing their JavaScript code for the compilers, compiler implementers should focus on optimizing common, real-world code patterns that developers are using. Specifically for the Node.js codebase, we encourage developers to go ahead and identify Crankshaft optimizations and submit pull requests with idiomatic replacements. If for some reason the idiomatic replacement causes performance to regress, go ahead and open an issue on V8 or ChakraCore or both. We are committed to fixing these issues. The following PRs are examples of such changes already happening in the community, it will be great to see more of these.

This is the modern way of writing JavaScript that will ultimately create conditions for Node.js to take advantage of the innovations coming out of the compilers and in the process make the Node.js codebase more readable, maintainable and more approachable for new contributors. This benefits the entire Node.js ecosystem, including its underlying JS engines V8 and ChakraCore.

Modernizing Node.js with idiomatic JavaScript was originally published in Node.js Collection on Medium, where people are continuing the conversation by highlighting and responding to this story.

Please follow and like us: