Published 4 mrt. 2025
Picking The Perfect JS Runtime
Or why benchmarks are misleading

Picking JavaScript runtimes have real consequences. We've tracked performance metrics, revealing striking differences between Node.js, Deno, and Bun. Memory consumption patterns in production tell a compelling story that synthetic benchmarks miss entirely.
The Node.js Era
Introduced in 2009 (approximately 15 years ago), Node.js is a JavaScript runtime built on Chrome’s V8 JavaScript engine. Node.js extended JavaScript beyond the browser, enabling developers to use the same language for both client and server side, popularizing full-stack JavaScript.
Since then, we’ve navigated through numerous challenges including (but not limited to):
- Callback hell with deeply nested functions
- Gulp build system issues
- A significant community fork
- Confusion between CommonJS and ES Modules
- The infamous left-pad incident where a single dependency removal broke builds at major companies like Facebook, PayPal, Netflix, and Spotify
Thankfully, the JavaScript ecosystem has evolved significantly since then.
Server Side Rendering
At brainhive, we’ve built our backend infrastructure with Rust from the beginning, leveraging its (memory) safety, concurrency, and exceptional performance capabilities that align with our engineering philosophy.
However, our projects often use NextJS—a React-based framework—which requires server-side rendering to optimize performance. Despite avoiding JavaScript for our primary backend services, we still need a JavaScript runtime to support server-side rendering, making runtime selection relevant in our architecture.
JavaScript Runtimes
Aside from Node.js, there are two competing JavaScript runtimes: Deno and Bun.
Bun
Built in Zig, Bun is a fast, Node-compatible, JavaScript runtime that aims to provide a better alternative to Node.js. Bun aims for 100% Node.js compatibility and is designed for speed, complete with a bundler, test runner and package manager.

Express benchmark shown on the website
Deno
Built in Rust, Deno is a secure, high-performance JavaScript runtime that aims to provide a better alternative to Node.js. Deno is designed for security, complete with a package manager, Node.js compatibility, and native TypeScript support.

Deno benchmark shown on the website
Bun In Production
Since we’re using Next.js we originally picked Bun as our JavaScript runtime due to its speed and compatibility with Node.js (at the time Deno simply wasn’t compatible). It was definitely an improvement but we ran into two major issues:
- Even post 1.0, there have been several releases with sudden breaking changes.
- We experienced increased memory usage and out-of-memory errors, suggesting that Bun’s memory management was sub-par.
We can work around ‘unstable releases’ by testing thoroughly and adopting a more conservative approach to updates. But, we couldn’t get a grip on Bun’s memory management issues.

Bun average memory usage
Switching To Deno
Due to the memory management issues in Bun we needed to find an alternative (or switch back to Node.js). Thankfully, with the release of Deno 2.0, we were finally able to run our application (without making any changes to our code) with Deno. And so we did.

Deno average memory usage

Switching from Bun to Deno
Peak memory usage went down from 700 MiB to 400 MiB and most importantly, memory usage was (way) more stable. This also resulted in a significant reduction in the amount of out-of-memory errors. Additionally, since adopting Deno we haven’t experienced breaking changes in new releases.
Conclusion
Moral of the story? Benchmarks are interesting but do not accurately reflect real-world usage. What good is a fast runtime if it’s not stable? In our case, we’ve found Deno to be a great alternative to Bun. It’s fast, more stable, and uses less memory.
Latest Posts
Discover more articles in our blog