Node.js: The Single-Threaded Lie That Became a Multi-Core Monster
Listen up, kids. I’ve been slinging code since the 1980s when men were men and memory was measured in kilobytes. Back then, if you wanted…
Node.js: The Single-Threaded Lie That Became a Multi-Core Monster
Listen up, kids. I’ve been slinging code since the 1980s when men were men and memory was measured in kilobytes. Back then, if you wanted parallelism you bought a Cray and sacrificed a goat. Today? I reach for Node.js and laugh at your “Java is faster” nonsense while my little green engine runs circles around your bloated thread pools. Yes, I said it!
Node.js is the most unfairly maligned powerhouse in our industry. Everyone parrots the same tired line: “bUt It’S sInGlE-tHrEaDeD!” like a religious mantra. Meanwhile I’m over here running 64 Node processes across 32 cores, sipping coffee, and watching my app chew through work that would make a Java Spring Boot cluster weep blood.
Let me show you why Node.js is secretly the most scalable, elegant, and downright vicious runtime when you actually understand how to use it like a grown-up.
The Event Loop Is Steroids, Not a Limitation
First, get this through your skull: the event loop isn’t a bug, it’s a feature so good that every other runtime has been copying it for the last decade.
While your traditional server is busy blocking threads waiting for database results (like a deer in headlights), Node is juggling 10,000 connections with a single thread because it never waits. It fires off the request, registers a callback, and moves on with its life. When the I/O finishes, the callback runs. Zero blocking. Pure sex.
This is why Netflix, LinkedIn, Uber, PayPal, and every company that actually moves real money chose Node. Not because they’re stupid. Because it’s faster than anything else for real-world workloads.
CPU-Bound Work? Just Spawn More Nodes, You Coward!
“Ah but what about CPU-intensive tasks?” cry the pedants.
Simple: stop trying to make one process do everything. That’s like trying to win Le Mans with a Prius. Instead, spawn a fleet of them.
Node gives you three weapons of mass parallelism:
- cluster module — the OG
- child_process.fork() — the surgical strike
- worker_threads — when you want threads but don’t hate yourself
Cluster: The “I’m Not Mad, I’m Just Disappointed You Didn’t Know This Existed” Option

Node is awesome!
Boom. You now have one Node process per CPU core, all sharing the same port thanks to the primary process doing round-robin load balancing at the OS level. Zero configuration. Works on Windows, Linux, macOS. Try doing that cleanly in Java without wanting to murder someone.
child_process.fork(): When You Want Surgical Parallelism
Need to offload image processing? Cryptocurrency mining? Machine learning inference? Don’t pollute your main process.

NodeJS is still awesome
This is message-passing nirvana. No shared memory corruption, no race conditions, no locking. Just pure, clean, actor-model style communication. Erlang wishes it was this easy.
worker_threads: When You Want Threads But Still Want to Sleep at Night
Sometimes you do want shared memory (ArrayBuffers, SharedArrayBuffers). Node gives you real threads with worker_threads. Post messages, transfer ArrayBuffers at zero copy, profit.

Events + Modules + Messaging = The Holy Trinity
Node’s true genius is how these three primitives combine:
- EventEmitter — your asynchronous glue
- CommonJS modules & ESM, (
module.exports) — zero-boilerplate code organization - Message passing — bulletproof inter-process communication
You end up with systems that are:
- Insanely composable
- Ridiculously resilient (processes crash? just respawn)
- Horizontally scalable (throw more machines, run more processes)
- Easier to reason about than any threaded mess I’ve ever seen
I’ve built trading systems that process millions of WebSocket messages per second using exactly this pattern. I’ve built video transcoding farms that scale to hundreds of cores with PM2 cluster mode. I’ve built real-time analytics engines that make Spark look like it’s running on a potato.
And every time, Node.js with proper process spawning wins.
The Truth They Don’t Want You to Know
Node.js isn’t single-threaded. It’s single-threaded by default for people who don’t know what they’re doing.
When you actually architect like someone who’s been doing this since before you were born, Node.js becomes the most powerful, flexible, and performant runtime on the planet.
Go ahead. Keep writing your “Node bad” blog posts while I’m over here making money with a runtime that scales from a Raspberry Pi to a 128-core beast without changing a single line of code.
Node.js isn’t just powerful, it’s the final boss of runtimes! It’s been “Hell yes, do it with Node.js!” It supports ESM — full-throated, no-asterisks, no — “experimental”-flag-bullshit support since Node 12 (2019), and it’s been rock-f*ng-solid since Node 14.
If you’re still writing require() in 2025, you’re not a “seasoned CommonJS veteran” — you’re just old and scared. I’ve been writing modules since before ES modules even had a spec, and I switched the day Node let me. Zero regrets.
Current reality (Node 20/22/24 as of late 2025):
- Just name your file .mjs → instant ESM
- Or slap “type”: “module” in package.json → your whole project is ESM
- Or name files .cjs if you want to stay in 1998 with CommonJS
- Top-level await? Works.
- import.meta.url? Works.
- Dynamic import()? Works (and it’s the official way to mix CommonJS/ESM now).
- dirname / filename in ESM? import.meta.dirname and import.meta.filename landed in Node 20.10+ (you’re welcome).
- JSON imports? import data from ‘./data.json’ assert { type: ‘json’ } (or just import data from ‘./data.json’ with { type: ‘json’ } in newer versions).
- Interop with CommonJS? Smoother than ever. You can await import() a CommonJS module from ESM, or createRequire() from ESM to load CommonJS if you’re into pain.
Every major framework (Express → Fastify, NestJS, Next.js, Angular Universal, etc.) either fully supports ESM or is actively deprecating CommonJS.
Even the Node.js docs now say: “ESM is the preferred module system.”
The last remaining excuses are dead:
- “But loaders and flags!” → Gone years ago.
- “But __dirname!” → Fixed.
- “But some ancient package only has CommonJS!” → await import(‘that-package’) or just fork it and add “type”: “module” like the rest of us.
I’ve been running 100% ESM production services since Node 16 without a single hiccup. Zero. None. My Angular + Native Federation + web3 micro-frontends are all ESM. My Neo4j drivers are ESM. My message queues between worker processes are ESM. Everything.
So yes. Node.js supports ESM. It supports it better than most people deserve.
Stop coping. Convert your codebase. You’ll thank me when your bundle sizes drop and your brain stops hurting from module.exports = { … module.exports } nonsense.
ESM won. Get on the train or get left in 2012 with the rest of the dinosaurs.laughing at your thread pools for over a decade.
Now if you’ll excuse me, I have some processes to spawn.
— An old man who’s been coding since 1980
메타데이터
- post_id
- 9bb4caab4a7a
- slug
- node-js-the-single-threaded-lie-that-became-a-multi-core-monster-9bb4caab4a7a
- url
- https://medium.com/@hobojizz/node-js-the-single-threaded-lie-that-became-a-multi-core-monster-9bb4caab4a7a
- canonical_url
- https://medium.com/@hobojizz/node-js-the-single-threaded-lie-that-became-a-multi-core-monster-9bb4caab4a7a
- author_url
- https://medium.com/@hobojizz
- status
- ok
- fetched_at
- 2026-06-24 11:06:28