— Why Some Benchmarks Miss the Point, and What We Should Really Be Comparing
Recently, an article titled “We Threw 1 Million Concurrent Users at Go, Rust, and Node.js” made waves in the webdev world.
Its conclusion? Node.js is obsolete. Go and Rust crushed it in a massive load test, with Node dropping connections left and right.
At first glance, the results seem compelling: Node.js lagged behind, struggled with memory usage, and had higher response times.
But dig deeper, and you’ll realize:
This isn’t a case of Node.js being bad — it’s a case of bad testing design.
Let’s break it down 👇
📊 The “Benchmark” That Beat Node.js — and Why It’s Flawed
The author created a backend route /order
, wired up to PostgreSQL, Redis, and an external API, then implemented it using Node.js, Go, and Rust.
The Node version struggled the most under heavy concurrent requests. But here’s the kicker — the implementation was deeply flawed:
- A new HTTP client was instantiated on every request instead of reusing a global pool.
- Redis calls weren’t properly error-handled, which broke the entire request pipeline.
- Axios requests lacked timeout settings, allowing infinite hangs and amplifying failures.
Meanwhile, the Go and Rust implementations were production-optimized, even skipping some error handling to avoid latency.
That’s not a fair fight.
It’s like racing a Formula 1 car against a bicycle with flat tires and declaring bikes obsolete.
🔍 So Is Node.js Really That Bad? Let’s Look at the Criticisms
1. “Node is single-threaded — it can’t handle concurrency!”
Yes, Node.js is single-threaded — but event-driven and non-blocking, which is exactly what you want for high I/O concurrency like:
- Webhooks
- BFF APIs
- Logging systems
- File uploads
Node avoids race conditions and locking bugs entirely by design. Not bad for a language that's supposedly “dead.”
2. “It sucks at CPU-bound tasks.”
True. Node isn't built for image processing or cryptographic operations. But:
Do you really need CPU-heavy logic in your web app?
Most apps are bottlenecked by database queries, I/O waits, or misconfigured servers, not by language-level computation. And for the rare CPU spikes? Offload to microservices or use native modules.
3. “npm is insecure.”
The npm ecosystem is large — which makes it a bigger target, not necessarily a worse one.
With tools like:
npm audit
Snyk
- Semantic version locks (
^
,~
) - CI-based pre-publish checks
…you can keep npm packages just as secure as any other ecosystem. Go and Rust also have supply-chain vulnerabilities — they’re just smaller fish in the sea.
4. “Node isn't for ‘serious’ systems.”
This one’s funny. Some production systems run on PHP. Others on Python with duck typing and no types at all.
Node with TypeScript + ESLint + proper architecture can absolutely power critical systems. Don’t blame the language — blame poor engineering.
5. “It’s bloated and hard to deploy.”
Admittedly, node_modules
has a reputation — but modern tools like pnpm, Vite, and ESM have made huge progress.
And for local development and deployment consistency?
I personally use ServBay, which gives me an instant, fully integrated Node.js + PostgreSQL + Redis + PHP stack — no Docker files, no global installs, just click and code.
Bonus: it now supports Windows, which is great for fullstack developers on all platforms.
🆚 But What About Go and Rust?
No question — they’re amazing.
- Rust is the performance king. But its learning curve is steep, and dev time can balloon.
- Go is pragmatic, fast, and great for microservices — but its type system lacks generics (at least until recently), and large-scale maintainability depends heavily on discipline.
You can write a blazing-fast Rust WebSocket server — but what happens when you need to refactor it weekly for changing business logic?
Real-world engineering isn’t just about QPS. It’s about:
- Team productivity
- Feature velocity
- Maintainability
Sometimes a slightly slower Node service gets the job done faster, cheaper, and safer.
🧠 Are We Being Too Harsh on Node.js?
Most Node criticisms come from comparing its worst usage to other languages’ best practices.
But in real-world teams, you don’t always get optimal codebases.
Here’s the truth:
If your site handles a few thousand users per day, Node.js is more than enough.
If you're aiming for millions of concurrent users, you don't just switch languages — you switch architectures.
You’ll need:
- Load balancers
- CDNs
- Caching layers
- Clusters
- Event queues
At that point, yes — Go or Rust might be a better fit.
But don’t treat a hypothetical bottleneck like a universal problem.
📐 How to Choose the Right Language (Hint: It’s Not Always Rust)
Let’s be practical:
Use Case | Best Option |
---|---|
🧪 Rapid prototyping, BFF layers, API gateways | Node.js + TypeScript |
📈 High QPS core services | Go |
🔬 Performance-critical modules | Rust or C++ |
🚀 Teams with heavy JS expertise | Stick with Node.js |
🧰 CI/CD + Dev Environment Simplicity | Node + tools like ServBay |
✅ Conclusion: It’s Not the Language. It’s How You Use It.
Every tool has tradeoffs.
- Rust has a steep learning curve.
- Go is clean but sometimes too minimal.
- Node has performance ceilings — but excels at developer velocity and flexibility.
If you use Node.js for what it does best — I/O-bound APIs, serverless handlers, real-time apps — it’s still one of the top choices today.
🙌 Final Thoughts
Instead of debating which language is "dead," let’s ask:
What does our project need? What does our team know? What gets us to production fastest — and safest?
That’s what real engineering looks like.
💬 What’s your take? Have you run into benchmarks like this that misled your team or client?
Let’s talk in the comments — and if you’re using ServBay or trying alternatives, I’d love to hear your workflow.
Top comments (0)