JavaScript has grown far beyond the browser. It now powers millions of back-end systems, APIs, and cloud services through Node.js, which is one of the most widely deployed runtimes on the planet. Keeping such a critical piece of infrastructure fast, secure, and stable is a massive engineering challenge, and the work behind it is often invisible. Rafael Gonzaga is a principal open-source engineer at NodeSource and a member of the Node.js Technical Steering Committee. He's spent years digging into the performance and security layers of Node's core, helping shape the direction of the runtime itself. Rafael joins the show to talk about the state of Node.js performance.
how benchmarking really works, the balance between speed and stability, and what it means to contribute to one of the world's most important open source projects. This episode is hosted by Josh Goldberg, an independent full-time open-source developer. Josh works on projects in the TypeScript ecosystem, most notably TypeScript ESLint, a powerful static analysis tool set for JavaScript and TypeScript. He is also the author of the O'Reilly Learning TypeScript book, a Microsoft MVP for developer technologies, and a co-founder of SquiggleConf, a conference for excellent web developer tooling. Find Josh on Blue Sky, Fostadon, and .com as Joshua K. Goldberg.
With me today is Rafael Gonzaga, Principal Open Source Engineer at NodeSource. Rafael, welcome to Software Engineering Daily. Hello. Thank you. I'm happy to be here. We're excited to have you. You do a lot of great stuff with Node.js and performance and user-lens libraries. But before we get into all that, how did you get into coding? Well, it started since I was a teenager. I've always been in computers since I was young. I played a lot of games. But my father is blind. And back in the time, he couldn't make a few things on the computer because of the disability. And then I started creating a visual or kind of talkback.
I tried to do that, at least. And then my journey in the computer science started. I tried to make it happen with Python, Python 2 back in the time, I don't remember. I couldn't make it work, but then I learned, okay, this is how I could make a calculator. This is how I could make things to work. So I started there, but until now, I couldn't make it. But there are plenty of good apps nowadays that he uses, and I'm still helping with some plugins and things like that. So do you have experience then with accessibility technology and writing things for folks who are, for example, blind?
To be honest, my focus is on backend. So accessibility on web browsers is not my thing, but I know exactly the problem that it causes because very often my father calls me, okay, Facebook is not working anymore. They changed something. Can you help me? And then I need to write a Chrome extension that will replace the element to the old way that he's used to get and make that happen. But on the front end, I'm not that person.
But you do work in an area that sometimes folks erroneously call front-end. So I think we should take a moment here to recognize that JavaScript is not a purely front-end technology. It's very possible to spend much of your career in, say, Node.js and JavaScript in the entirely back-end. What do you think of the areas that your code has touched or the stuff that people use your areas for? Well, I always loved low-level programming. So I started with Python, but then I suddenly jumped to C and C++. I learned most of the things outside of university. By the way, I haven't completed my degree, so I don't have a degree nowadays.
And then during the process, I learned many, many languages. I came from Elixir, Erlang, I went to PHP, C Sharp, but I still learned, I have still working on personal projects with C++ and C. And then I found that, okay, there's JavaScript, people use JavaScript on Frontend, but I don't like Frontend too much. Then I saw, okay, people were running JavaScript in backend. I tried, okay, let's see how it works. And my first attempt with Node.js was in a hackathon a long, long time ago.
And then I could make an HTTP server. And then I said, OK, that's nice. Let me see how it was great. And then I saw the GitHub repository. And then, OK, they use C++, so I could help in that part. Then I started looking to it, but I didn't make the contribution back in the time. But yeah. So how did you go from starting to look at Node to becoming a member of the Node Technical Steering Committee? OK. I have started on Fastify, an HTTP framework. When I was working as a soft engineer in a company that was about to switch all their PHP projects to Node.js, they were looking to a microservice approach. And then I was responsible to make that migration.
So I was investigating, okay, we are about to use Node.js, but let me make some tests to see how well we scale this new platform. Then I saw some HTTP frameworks like Express, and a lot of people were advocating about Express, and all the resources you find on the internet, they use Express. But I made some benchmarks, and Express was not fast as I thought it could be. And then I was looking, okay, let me see if there are other HTTP frameworks, because Express is not official from the Node.js team, right? It's a library as well as Fastify. And then I tried Fastify, and I saw the benchmarks page from that.
And I said, okay, this is nice. The results is exactly what I was expecting. But I found a bug during the process. And I decided, okay, let me create that request. That shouldn't be a chief code. I wrote the request. It was accepted. And then I saw, okay, the community was very welcome. And I decided to keep contributing to Fastify. Then I met Matteo, Matteo Colina, one of the Fastify creators during the process that told me, do you want to apply to a position at Neo4? I said, yes. I made the test, which is very curious because...
something got wrong in the process. And then I did the test for a senior front-end engineer and I passed, but I haven't worked as a front-end engineer at all, but I passed anyway. Then after passing, I got migrated to Mateo's team and then I got some tickets to work on Node.js 4. And one of my first requests to Node.js was fixing a memory leak on a Windows 2012 server, 32 bits. And I spent, I don't know, two weeks or three weeks working on that.
And it was very hard because I had to SSH into a machine which was far away from me. And I was testing, debugging memory leaks, and I could fix that bug. After that, I learned a lot about Node.js Core. And then I started doing a lot of stuff. And I got nominated to Node.js Core. And then almost three years ago, I got nominated to Node.js TSC. What does it mean for someone to be on the TSC for Node? It's very important to be part or to have a voice in technical discussions. But to be honest...
If you are a Node.js collaborator or if you are outside of Node.js team, you can still advocate, you can still share your opinion. It's more about being more proactive on helping new members or discussing new features and having more context about Node.js in general. So if someone attempts to include, okay, let's reshape Node.js, so let's remove C++ and move that to Rust. Normally, the TST team are the set of people that will say if that's possible or not. And also in case of...
discussions, they have a vote if we should do it or not. So it's important, but it shouldn't be something that should guide people to get that role, to be honest. It's more about having a voice. I think voice is not the correct term here because technically the TSE was created just to guide the project, but we shouldn't have more power than any other contributor. That's what I'm trying to say. A TSE member shouldn't be anything different from an OGS core collaborator. Anything that a core collaborator does, a TSE can do and vice versa. So I don't know if I could explain that well, but yeah, that's the idea.
It's kind of amorphous being a technical steering committee member on a project that doesn't have one single company or one single charter. But your area has to believe in performance, and you've written quite a lot about Node performance. How do you see that evolving these days? Currently, I work on Node.js security and performance. Performance, I normally do it because all my studies, my research are in performance. However, I've been paid to work full-time on Node.js to focus on security. So I have these two fields. For performance, I have been monitoring Node.js performance for quite a while. I have a lot of projects around that.
And recently, I'm leading the performance initiative of Node.js. However, due to a restricted bandwidth, I don't have much time to work on that because I'm not paid full-time on performance button security. I normally write some reports like the state of Node.js performance. And the Node.js have been evolving a lot, mostly due to V8 improvements. So V8 is doing a very good job in JavaScript. So some performance improvement came from V8, per se. And some of them is because Node.js is migrating most of its API from JavaScript to C++. In the past...
we had a lot of discussions if we should write APIs in C++ or JavaScript. And the reason that some of them were writing in JavaScript is because it's way easier to get contributors for JavaScript than C++. So, okay, some API, let's write it in JavaScript because it's easy to get maintenance. But then we saw, okay, for these specific APIs, JavaScript will be a bottleneck. So let's move that to the C++ part of Node.js. Then we moved and then we saw, okay, we got a significant performance improvement. And if you compare...
The Node.js startup, for instance, we got a significant improvement and that applies to Lambda providers. If you are using Lambda, AWS, or VeriCell, even Cloudflare workers, you see that it's very important for them to have a very fast cool start. But I would say that those new runtimes like Ban and Dino, they have moved Node.js to get a different point of view in terms of performance. We were more stable in the past, not releasing many features and being more assertive. But now we have switched a bit our focus to hear more about the community. If community wants some modules like Escalite, let's do that. It doesn't need to be so sacrifying.
for maintainers. So we have that. And we have a more strict focus on performance. So we are actively monitoring benchmarks, micro-benchmarks of Node.js. And to be honest, it's very hard to write benchmarks in JavaScript in general because of VAT. VAT can be very tricky. Most of the times it's smarter than you. And if you are writing a micro-benchmark, it's possibly you are looking to the wrong metric. So it's very hard to make some comparisons because some API might be fast on your machine or in your workload.
And with a different workload or running that for more time, it will cause more de-optimizations, optimizations or garbage collectors, and then you'll see a different performance. But yeah, I would say that Node.js is way faster than two years ago. I want to walk through a couple of scenarios with you just to help flesh out what you're saying. Do you recall a performance optimization you attempted at Node that did not work out after running all those optimizations and benchmarks? Yes, there are plenty of them, to be honest. At Node, there is a working group called Node.js Performance, which stores most of the issues that people find about regressions in Node.js in general. Some of them we were investigating.
I believe it was from Node.js 18 to Node.js 20, or on a specific version of Node.js 20, which McLev, a compiler inside V8, was introduced and enabled by default. After that, we have received some issues saying, okay, my microbenchmark is telling me that my code is 50% is lower than in a previous version of Node.js. Then we came up to investigate, and the reason is that McLev was, if you run that piece of microbenchmark for a short period of time, McLev was being introduced, and this was causing... some slow operations. But it turns out that if you run that with a prediction workload and running with a reasonable amount of time, MacLev was being called, but then it was moved to the TurboFan, which is the optimized bytecode of V8. And then the result for the performance of the code was similar. So for short scripts, that might be a performance bottleneck, but for long live ATP servers, the performance was the same. So it's very tricky to measure micro benchmarks in JavaScript because you might be measuring a no operation.
because V8 optimized your code away. You might be measuring a non-production or an unrealistic workload because you will never run that function many times as you are measuring. And some of them, for instance, in a specific version of V8, if you are converting a string to an integer using parseInt in comparison to a plussino, the plussino was, I don't know, 10 times faster than parseInt in some V8 versions. I have shared that on Twitter. I have a repository that monitors all those micro operations. And I have also wrote about that. And it was fixed by V8.
And it turns out that most of the cases, if you attempt to switch all parsing into plus signal, it will only give you 0.0005 milliseconds of performance improvement. So most of the time, it doesn't work for the change. So it's tricky. One of the open source projects I work on, someone spent, I think it was about 2,500 words trying to explain to us why we needed to switch from one library to another. That would save about, don't quote me on this, about 104 bytes of the module size and about a fraction of a hundredth of a hundredth of a hundredth of a second of runtime improvement at the beginning. But at the same time,
As I'm sure you're going to say within the next 10 minutes, sometimes these optimizations really do result in user-line improvements, where something that's a fraction of a second faster compounded over time really is better. So how do we know when we're looking at, say, optimizations in Node or in user-line libraries, when is it worth it to make that performance investment? Okay. It's very important to have baseline. So if we're trying to optimize your code, you need to have a benchmark. So if you have most of the people that use Node.js, they use it as an HTTP server. So measure how your routes are...
or how they are performing. If you are getting, I don't know, 10,000 requests per second, if you are getting 100,000 requests per second, measure CPU utilization, measure memory during workload, during a benchmark tool, when a benchmark tool runs, like WRK2 or AutoCannon or Apache benchmark, it doesn't really matter. Actually, it matters, and I can explain a bit more later. But create a baseline. This is the state of art of my HTTP server. Then you start measuring. You can't optimize things without measuring things first. Otherwise, you fall into a trick role where you are spending a significant amount of time.
to get just a small milliseconds of improvement, as you said. But there are a lot of improvements you can do, mainly when you are comparing some HTTP frameworks. Like most of you use Express because Express is spread everywhere. But what most of you don't know is that if you switch from Express to Fastify, you get a lot of performance. I'm not saying that because I'm part of the Fastify team. I'm also part of the Express performance team. And what I can say that Fastify in the last versions, it's way faster than in the, I don't know, a year ago. So always measure.
Fastify is still, as far as I know, the fastest framework in terms of stability and performance out there for Node.js. And more importantly, make sure that everything that you write to the logs, for instance, logs is a huge problem. A lot of people lose a lot of performance by just choosing the wrong log library. People are using console.log, which is a huge problem. If you are doing that, please stop. People are using, for instance, Winscun. I wrote an article called The Cost of Logging, I believe, in 2022. And it is still very popular and it's still applicable.
As far as I know, the fastest logging library out there is Pinot.js. The reason for that is that Pinot was writing in a way that it can create a QE of messages and will not block the event loop when writing to the terminal. I wrote in depth about that in that article, so I suggest people to check that. It's very easy to find. And Pinot.js also uses Sonic Boom as a dependency, which is very important to make Pinot.js fast. So by choosing the right HTTP framework and the right logging library, I'm pretty sure your app will be way faster than nowadays.
One of the advantages, as I understand it, of writing things in user land, like, for example, first Express and then Fastify, is that allows users to iterate an experiment. But at the end of the day, some of the things you've alluded to, such as console.log being slow, are part of Node core, and therefore the default that a lot of people go with. What would it take for Node itself to have an equivalent to an Express or Fastify or Bino.js in core? OK. The Bino.js in core is due. We are just using that, to be honest. So that might happen.
Yes, a lot of people ask me why simply Node.js folks can rewrite the HTTP module to make it similar to Fastify and Fast as Fastify. And the reason for that is first one is maintenance. So Fastify, they have a huge thing maintaining that. And for Node.js, if you move things to Node.js, it's expected that you get less contributors working on a specific module because it's way hard to understand Node.js source code than Fastify. The second one is that we can't break the world.
Node.js is used by tons of devices and even releasing things as major, server major, which is expected to break, we can't touch very hard on NTP modules because this will migrate people away from Node.js. If you look to the recent Node.js downloads dashboard, you see that a lot of people are stuck on Node.js 12 just because of breaking change. They don't know how to migrate. It's very hard for them to migrate. And those breaking changes are very crucial for them to stick on an end-of-life version.
And this is a problem because when you stay in an end-of-life version of Node.js, you are not safe. There are plenty of vulnerabilities that affect you. And I also wrote a talk. I have delivered a talk called Five Ways You Could Have a Hack at Node.js, where I exposed some vulnerabilities that Node.js have fixed. And if you are using an outdated version of Node.js, possibly this will happen to you. And that's it. I mean, we can make some improvements to HTTP module, but we can't change it too much. How it was designed is crucial and its legacy. First, it's very hard to change. And second, changing that means that you are prone to break a lot of people.
So you have two conflicting desires and needs here. One is you want to keep things stable because breaking changes hurt the community's ability to migrate, but you also want to have modern APIs and improvements. So how do you strike that balance? How do you know what's a valid or correct breaking change to make? Most of the features, they are behind flags, behind enabling flags. For instance, we brought the permission model. Permission model is a feature where when you enable that, the Node.js will restrict access to file system, to network, to V8, to V8 inspector protocol, child process worker threads. So all of these will be restricted whenever you pass dash dash permission when you use Node.js. This is a secure measure.
And people ask me, why not enable that by default? The reason is that, first, it will break all the scripts out there so people will never be able to migrate. People do not read changelogs. They just don't want to read that. Or for some of them, it's hard. Some of them use Node.js behind the scenes. So for instance, if you are using Next.js, you are using Node.js behind the scenes. And in some situations, you don't have the Node.js command out there. It's behind Next.run or something like that. So this will break frameworks. And frameworks will need to upgrade. And then users will need to upgrade their frameworks versions to fix that.
it creates a kind of chain of breakage that will reach out to the end users, and this will be very difficult for them to migrate. So frameworks will be mad with us, users will be mad with us, and most of the cases, if we enable the permission model by default, a lot of people will just pass disabled permission because they don't care about it. So we decide, okay, if people, they care about performance, they will opt in. It would be much easier if we rewrite Node.js from scratch. So let's say that we are releasing Node.js 2.0.
possibly those features will be enabled by default because we will teach people this is how this Node.js was designed. If you want to use Node.js, that's how you go. But since Node.js is still the same, people are just using NVM to upgrade and they don't expect this new design, this new structure. They don't want to learn Node.js again. We can't do that. Yeah, it's also on, what's the latest major version at time of recording? 24.10 or so? We're not quite at the 2.0 level anymore. Yes, that's correct. I'm working on Node.js 25.0 that will go out in two days. Oh, that's exciting. What are the major changes or features for 25? So one thing important about several major releases.
A lot of people believe that server major are the most exciting releases of packages or runtimes platforms like Node.js, but turns out that it's very, very boring. Server minors goes to server minors releases. Performance improvements, they most of the time are categorized as server minor or server patch, which means that it's very likely that Node.js 24.10 will be way more exciting than 25.0, because 25.0 only contains breaking changes. And for Node.js... 25.0, we will have the upgrade version of V8 to the version 14.1, which will bring the major JSON string 5 performance improvement. So if you look to the V8.dev, they have created a blog post about how they have made JSON string 5 fast. And with this version of Node.js, it will bring the V8 version that includes that performance improvement.
We will have a new built-in new signed integer 8 array. It's for base 64 or hexadecimal configuration. WebAssembly and GIT pipeline optimizations. We are also enabling adding a new feature to the permission model, which is the dash dash allow net. So network will be restricted whenever you use the permission model on Node.js 25. But other than that, we don't have a lot of features coming in. Most of them, they are breaking changes or considered breaking changes. So if you are using the permission model on Node.js 24, it's likely that you get affected by Node.js 25 with this new network restriction.
We are flagging the experimental web storage. So web storage will be enabled by default on Node.js. And we are deprecating and removing a lot of APIs. APIs that was runtime deprecated, they were removed on Node.js 25. And some of them will be runtime deprecated on Node.js 25. So if your console, whenever you run Node.js and some API is emitting a warning to you that this will be sent removed, possibly Node.js 25 will break you. So upgrade that. That is exciting. Removal of that code. No performance, download size, and so on at all.
Yes, I mean, we will remove code that needs maintainers. So for us, maintainers, it's very good to remove code. I want to take us a bit back to the second of two scenarios. We talked about a performance optimization attempt that ended up showing, well, not always optimal. Is there an upcoming one or a recent change you've made that you're excited about for a performance that did work out? There are one that the request is to open. I wrote that with Robert. We'll bring a significant performance improvement to the HTTP module of Node.js. We talked about that, and then I remembered. But it's an opt-in feature, so we are releasing that to not break people.
A new flag, whenever you create an HTTP server, there is an optimize empty request option. Basically, if you are relying on the rest pattern, which is get request should not have a body. Head request should not have a body. If you enable that option, your HTTP server will be faster because we will clean up the string faster. We will dump the string.
However, there are people that still read body from get and head requests. So if we enable that by default, we will break those people. So I believe that we will have that option. We will have some discussions to enable that by default, but this is a separate discussion. For now, let's break that option. So frameworks like Fast 5 or Express, they can enable that if they want to. So they will have this gate. If Express enabled that by default and people were not mad with it, it means that Node.js are likely to enable that by default as well. So this is a request.
Before you move on, just looking at pull59778, Optimize Empty Requests, you've got a screenshot in the body of the description showing requests per second. And it looks like you're going from, in one of the cases, 32,000 to 69,500. That seems like a significant increase in throughput. Yes, that benchmark we have run for 30 seconds in a dedicated machine. So without this option, enable, we got 1,000 requests, let's say, or 32,000 requests per second in one percentile.
to 69,000 in one percentile requests per second. So we are more than doubling down the performance of HTTP. But this is specific to the get and head requests. It's pretty incredible. You can see how the decision is not easy to make of whether you and when you might want to make the breaking change and turn this on by default. You're doubling the request per second, but also breaking so many people. Surely that's got to be a long, slow, deliberating process to turn on. That's correct, yes. And I just saw that they need to rerun that CI and land that. This is one of the problems. We run so many CIs in different environments because Node.js runs everywhere.
So we have a smart OS, we have Mac OS, we have Windows different platforms, we have Linux different platforms, and we need the CI to be green in all those. I believe that we have more than 60,000 of assertions on Node.js for tests. So it takes a significant amount of time. So to get a green CI, it usually gets, say, six hours. If you get a flaky test, you need to rerun that. So it takes a while. Well, okay. You are about to look up some more exciting improvements that you're looking forward to with the next versions of Node. There are some improvements that we did to the Node.js core, but we don't have...
it optimizes a specific internal code of Node.js. So it doesn't translate to an API where people will experience that in real-world applications. So there are improvements to the assert partial, deep, strict, equal. There are improvements to how Node.js is benchmarked. So for instance, people always ask me, if Node.js has so many benchmark files, we have benchmarks for every feature we release, why regressions still exist? Why you don't measure benchmark? Why you don't run benchmarks after every release or before every release of Node.js to find regressions? The reason is that.
Nowadays, to run a full Node.js benchmark CI, it takes 84 hours. Eight to four hours. Wow. The reason for that is that because measuring JavaScript code is tricky, we can't run a benchmark just once. We rely on an algorithm called new hypothesis, which tends to be the student test approach, which is we run each benchmark for each configuration 30 times before the change and 30 times after the change. So imagine the situation where in our benchmarks, we have configurations. So let's say that we want to create a benchmark file for the U2 inspect or console.log better.
So we create a configuration where we call console.log with a long string, with a short string, with an integer, with an object, with other options like a long object or a small object. So all those are configurations. So we run the benchmark 30 times for each configuration before the change. So 30 times for integer, 30 times for short strings, 30 times for long strings or big strings. And then we run that 30 times again after the change. Then we produce that statistical analysis to prove the variance or if the change, if the performance is statistically significant or not. Because sometimes...
You run the benchmark before the change just once and run that after the change just once. And then you compare, okay, I got more requests per second or I got more operations per second, so my code is faster. But that's not the reality. Your machine is doing a lot of things in background. So if you are in a Zoom call when you are running a benchmark, it's very likely that your code will produce a lot of variance that will impact in the result. And then the operations per second you get in the end is not true. It doesn't show the reality. Brendan Gregg once did an experiment in 2018, I believe, where he shoot in the data center and then he experienced that the slow disk IO operations increased.
just because he went right in the front end of the data center and should. So this proves the variance. So to calculate if your benchmark produce high statistical significance, which means that if the B value is greater than 0.05, it means your code don't belong to the same group, which means your performance improvement is valid or your regression is valid. Otherwise, they are just in the same sense of the standard variation. So that's why people normally plot data into a normal distribution. So they measure that. And that's why it takes so long to run benchmarks. But we are improving that. We are creating more machines. We are sharing that. But it will take a while. That's an incredibly statistical, mathematical way to look at this. That must have taken a long time to get to. I love that part, to be honest. Most of my research and my studies are about that. So if you look to my blog posts, to my blog in general, I talk a lot about how to prove if your benchmark is valid or not and how to not get tricky. So I wrote talks about that.
There's a talk where I created with the help of my team. It's called Lies, Them Lies, and Benchmark, which basically shows you why most of the time your benchmark is being lying to you. And this also points to when I was talking about HTTP loading tools. I mentioned that there's no difference between Apache Benchmark or Dat Work or AutoCannon, but the reality is there is. There's a problem when you are using HTTP load frameworks called Coordinate the Mission. So G10 wants there's an HTTP load tool called Work, WRKey.
And G10 work created a second version, a fork of it, WRK2, which fixes the coordinate omission. Coordinate omission is simple, if you try to explain. Imagine that you send a request to your server. Once the server replies back, it will send another request, and then it measures the latency. But this doesn't show the reality. Once you send a request, you should send another request after X seconds.
So if you show how real-world applications behave, then you get a long latency on the second request because the server is waiting for the second response. So there's a long explanation on the WRK2 repository. I suggest you to test it. And it's valid. That points out a very good solution. So I normally use that for benchmarks in Node.js. Let's talk about your blog a little bit. You are the author of a series, The State of Node.js Performance. What is The State of Node.js Performance? What does that blog post talk about? In 2023, I've been doing Node.js major release for quite a while. I believe since Node.js 18 or Node.js 17. I don't remember.
I did all Node.js major releases. So Node.js 17, 18, 20, 21, 22, 23, 24, and in two days, 25. Hopefully. And I have always wondered, okay, we are releasing a new major version of Node.js, but what this means to performance enthusiasts. It means that if I migrate from Node.js 18 to Node.js 20, my code will run faster. A lot of people don't know that and they create issues on Node.js core repo. After migrating from Node.js 16 to Node.js 18, my code, my application is behaving worse. And because benchmarks.
takes a while, we don't run that very often. And we don't have machines or capacity to run that. So Node.js for developers, they had to investigate that. They need to replicate the environment of the issue. So some of them are only replicable on Windows. Some of them are Mac OS. And it was very hard. So I decided, OK, after releasing Node.js 20, I want to know exactly which
workload or which APIs will be affected, either by regression or by performance improvement. We can't do that as part of Node.js release process because this takes a significant amount of time. And on Node.js 2023, I did using my on time. And 2024, I got sponsored by the company I'm working on to Node.js. So they give me machines to test that. They dedicated machines time so I could do that. And I'm planning to do the 2025 version. So on this report, I utilized three versions of Node.js. For Node.js 2024, I have used Node.js.
20.17.0 and 22.9.0. And also comparing to the 18 version of Node.js. So I split the benchmark setup using the Node.js internal benchmark suite, which takes 84 hours. And Node.js bench operations, which is a repository where I maintain, where I also monitor small operations like converting strings using parse int versus plus sino. Is that faster on this specific environment? So this repository where I store all those benchmarks.
Specific APIs, for instance, HP server, Fastify, and Express, it got worse or better with this new version. Because in all major versions of Node.js, we upgrade the V8 version. By upgrading the V8 version, we never know exactly which APIs will be affected, because it's JavaScript in the end. If they change something in the JIT compilation, this might affect the whole ecosystem, or this might affect just specific operations in Node.js. So, for instance, the V8 update.
got the parsing performance upper that matches the plus signal. So this report is a more comprehensive study about how the Node.js benchmarks are evaluated, how we guarantee we are not measuring non-operations, and how it will affect you. So for instance, by upgrading from Node.js 18 to Node.js 20, or better, from Node.js 16 to Node.js 20, if you were relying on Node.js crypto operations, your code will be very slow now. because of OpenSSL. So then we tracked down all Node.js dependencies. Node.js depends on LibUV, depends on OpenSSL, many other dependencies. Just to no-process.versions, you will see all the dependencies of Node.js. And it got worse because of OpenSSL. Now it's fixed because we have improved, we have upgraded Node.js to the OpenSSL version 5.2, if I'm not wrong. And this report contains all the information and all the...
the resources you need to know before upgrading or even to diagnose your Grafana dashboard. Because when people upgrade Node.js version, they will say, okay, my app is now consuming 25% more memory, but is using 20% less CPU. Is that bad or do you? And then on this report, normally there's an explanation and why this is good or why this is bad. Do you find that?
People understand the metrics as put forth. Are there ways to kind of convey the nuances and complexities of them that the common node user would understand? The reports, I normally use percentages. So it's easy for them to say, okay, this API is 15% faster or 15% slow. I always create two kinds of sections. One for people that just want to get the value and one for people that wants to understand how the benchmark was executed. That makes sense. Before we move on to the last area of technical topics, I do want to ask about your company, NodeSource, your employer. They sponsored this work. What is it that NodeSource does?
NodeSource is a consulting company, but they also create products. They have an APM which measures the performance, all the metrics of your Node.js application without sacrificing the performance. For instance, it's very weird for me as a performance enthusiast that if I want to deploy my application in production and I want to monitor that, I want to measure the performance of my app, this will reduce the performance of my app by 30%. It's weird. It's just bad. Any solid product from NodeSource exists. It's an APM.
Basically, we have forked Node.js. We are still rebasing on top of Node.js, but we have added or changes that makes APM fast. So now, if you don't use APM or use NSolid, the performance will be the same. So we have no direct impact in your app. So you'll be able to see in the console, CPU utilization, event loop utilization, memory utilization, and even creating CPU and heap profiles without losing performance. So you can do all of that in production. So that's why NSolid exists, and that's why I like it. Is this something that could be upstream to Node as an option?
Yes, we do a lot of upstrings to Node.js, but we also understand that the process of moving things to Node.js is slower because it expects consensus from all members to make some changes. And some of them are too business-oriented that doesn't go to the Node.js, but go to a product like Unsolid. But yeah, we do a lot of upstrings patches. Great. I'd love to hear that. It's good to see people in the community, companies in the community that not just work around Node, but on Node and in itself to make a better version. Yeah.
Let's talk at the end of this section about user land libraries. Let's say that I am writing an app and I'm using Nsolid for logging and APM. I'm using Fastify rather than, say, Express or Core HTTP. What are some of the tips and tricks you'd use or you would give me to make sure my app is fast, let's say? It really depends on the SLO or on the expected requests per second you want. I know that a lot of people will say that I want my app to be the fast as possible.
but some of them don't need to spend too much time on fine-tuning as other companies. So I would say that if you choose a good ATP framework like Fastify, it makes a good usage of Fastify because it's not just using Fastify, it's creating Fastify routes with the correct expected request objects and expected response objects so Fastify can optimize that. It's creating a good pattern of projects. Most of the bottlenecks is in the business code, right? If you receive a request, you need to connect to the database. So make sure you have a pool of connections. Measure your code using traceOpt and traceDopt to check if you are making use of V8 optimizations. Some people, they don't understand how hiding classes on V8 classes makes their code faster. So they are creating an object and using the delete property. For instance, I have an object and I want to delete one property from this object. And they use the delete keyword for that.
What they don't know is that whenever you use the lead keyword, the hidden class will go away and these objects will be way slower than it should be. So instead of using delete, assign the value to undefined so you don't delete the hidden classes. So creating objects using the correct or the expected object tree. All of this, you can get information by using trace deopt. If you get too many deoptimizations during your code, something is wrong. Make use of a good login library. Whenever you call an API too much, you need to make sure that this API is as fast as possible. So Vino.js is a good login library. When you create a database connection, make sure that inspect the network. Check if you are creating too many sockets. Check if the connection is being up in the pool of connections. So you don't need to create a new connection with database whenever you send a query for that.
So there are more generic performance tips than Node.js tips, I would say. So measuring the errors of your machine. So check the events, check sys holes, check if you are using the correct version of Node.js, please. There is a package I wrote. It's called isMyNodeVulnerable. So just run your terminal. npx isMyNodeVulnerable, is-my- blah, blah, blah. And it will tell you if you are using a server version of Node.js or not.
I think upgrading the dependencies is very important. I would suggest to use less dependency as possible. So for instance, if you use uchu.styletext, this replaces some usage of chalk. If Node.js has now TypeScript, Node.js has Escalade, we have a lot of built-in modules that I strongly suggest you to use that because we can make it faster. What are your thoughts on log files as applications and as servers? I don't work on products like business products for quite a long time. I have been working with open source or libraries for the last five years. So I don't have a strong opinion on that part. I know as a maintainer that we normally don't use the log files so people can upgrade that. We don't release log files because people can upgrade that. At least this is how we handle that on Fastify and I believe Express as well. So I don't have a strong opinion. What a rare and beautiful statement from someone who works deeply in tech. I don't have a strong opinion.
Yeah. All right. We're heading towards the end of the interview. Are there any topics you wanted to bring up when it comes to Node or security or performance? Well, if you are interested on Node.js core and you want to make your contribution, I have been doing live streams on Twitch, YouTube, and also Axe, teaching people how to contribute to Node.js. So I've been planning to create a live stream during the Node.js 25 release to show people how I release a major version of Node.js. And I normally announce the live streams on the Node.js mentoring channel, either on Node.js OpenJS Foundation Slack or on the Node.js Discord server.
Have you found that a lot of people have started contributing to Node through these live streams? Well, I know at least 10 people that got their first contribution to Node.js with some guidance from the live stream. So it's a very good number for me. In a sense, you've become a 10x developer. Yeah, kind of. Let's say that I'm somewhat interested. I've never contributed to a major open source project before. I would love to do it. I don't really know what that entails or what the benefits and drawbacks are. How would you pitch this to me? I would say that my career would be way boring if I didn't contribute to open source because it opened so many challenges for me that I could learn a lot. I'm pretty sure that if I...
I haven't started contributing to Node.js. I would have stopped my C++ studies. I would have stopped a lot of performance improvements research I did. So this helps me to find good offers. I have received many, many offers because of my open source work. So I could travel through the world. I'm from Brazil. And for me to go to Europe is like 12 hours flight. And it's very far. I have been in China delivering talks. I have been in the whole Europe delivering talks. And all of these were not possible without my contributions to the open source. That's lovely.
So you've become a member of a global community of people? Yes. I mean, I have more people that I know by GitHub handles than in real life. That's lovely. To close out the interview, I'd like to ask something explicitly non-tech related. And that's a great transition. Let's talk about friendships in sports. You used to play tennis and you've recently switched what you've kindly been calling behind the scenes soccer and what others may call football. Can you tell us about that? I have recorded some fire chats at Node.com, I don't know, two years ago, three years ago, where I told people I have started playing tennis because finding people.
Finding friends to play soccer is way difficult because for tennis, I just need to find one person. But for soccer, I need to find a whole team. And now, after three years, I could find a good amount of people. And now I'm playing more soccer than tennis. So yeah, I've been playing soccer, I don't know, three times per week as a good Brazilian. So we love soccer. So we do that. It's in my blood. So yeah. Do you root for any clubs or players still? I support Santos. It's the Neymar team in Brazil. But I mean, we are not good.
We are not good now. We are very good, to be honest, but we are not good now. I also like a lot Manchester United, but still, it's in the same situation as Santos. A person who must have hope in this world. Well, great. Javier, you've talked about a lot of incredibly exciting and interesting things. We talked about security in Node, talked about performance, how Node's internal performance works, is improved, and has been benchmarked and optimized. We talked about tips for using libraries, and also, of course, your regular streams, where one can learn how to contribute themselves. If we wanted to learn more, if we wanted to reach out to you on the internet, where would you direct us?
Join the Node.js mentoring channel. I'm mostly available on X and also Slack. So if you want to send me a message on Slack or Discord, I'm happy to guide you to your first contribution. Also on X, my DMs are open and I'm happy to help. For Software Engineering Daily, this has been Rafael Gonzaga and Josh Goldberg. Thank you for listening, everyone. Have a great day.