Eight years in, I have shipped backends in several stacks. I have nothing against Go, Java, or Rust — they are excellent tools for the right problem. But when I need a high-throughput, I/O-heavy service with a small team and fast iteration, I still reach for Node.js.
That is not nostalgia. It is a calculated choice based on what actually happens under load.
Where Node.js genuinely shines
Most production backends I have owned are not CPU-bound. They wait: on databases, caches, HTTP calls, message queues, object storage. Node's event loop is built for exactly that shape of work.
In a telecommunications environment serving 10M+ subscribers, our critical path was a chain of I/O operations — ingest, enrich, query, respond. Node handled thousands of concurrent connections per instance without spawning a thread per request. That efficiency translated directly into lower infra cost and simpler horizontal scaling.
The TypeScript layer on top gave us contract safety without sacrificing the runtime model. Strict typing at the boundary between services caught integration mistakes before they reached staging.
Where I would not use it
I am honest about the limits. Heavy CPU work — video transcoding, complex cryptographic operations, large in-memory transformations — does not belong on the main event loop. I have seen teams melt production because someone ran a synchronous JSON parse on a 40 MB payload inside a request handler.
The fix is not abandoning Node. It is architectural discipline:
- Push CPU-heavy work to worker threads or dedicated services
- Keep request handlers thin and async end-to-end
- Measure event loop lag, not just response time
What I look for before committing to the stack
Before choosing Node for a new service, I ask four questions:
- Is the workload primarily I/O-bound?
- Can the team maintain strict TypeScript contracts across service boundaries?
- Do we have observability on event loop delay and GC pauses?
- Is there a clear escape hatch for CPU-intensive paths?
If the answer to the first three is yes, Node remains a strong default. If the workload is compute-heavy with tight tail-latency requirements on every request, I evaluate alternatives without ego.
Closing thought
Frameworks come and go. Runtimes get faster. What endures is matching the tool to the shape of the work. For I/O-heavy backends at scale, Node.js still earns its place in my toolbox — not because it is fashionable, but because it keeps delivering when the latency budget is measured in milliseconds.