Node.js
Server-side JavaScript with Node.js — when it fits, streams and backpressure, multi-core concurrency, and graceful shutdown.
5 questions
MiddleTheoryCommonWhat are the Node.js stream types, and what problem do streams solve?
What are the Node.js stream types, and what problem do streams solve?
Streams process data in chunks instead of loading it all into memory, keeping usage flat for large or unbounded data. There are four types: Readable (source, e.g. a file read), Writable (sink, e.g. a file write), Duplex (both, e.g. a TCP socket), and Transform (a Duplex that modifies chunks, e.g. gzip). pipe connects them and propagates backpressure so a fast source can't overwhelm a slow sink.
Common mistakes
- ✗Reading a whole large file into memory instead of streaming it in chunks
- ✗Ignoring backpressure, letting a fast source overrun a slow writable
- ✗Confusing Transform with Duplex — Transform modifies chunks as they pass through
Follow-up questions
- →How does
pipepropagate backpressure between a readable and a writable? - →When would you choose a Transform stream over buffering and transforming manually?
JuniorTheoryOccasionalWhen is Node.js a good fit, and when is it a poor one?
When is Node.js a good fit, and when is it a poor one?
Node.js shines for I/O-bound workloads — APIs, proxies, real-time apps — because its single-threaded event loop handles thousands of concurrent connections without blocking while it waits on the network or disk. It is a poor fit for CPU-bound work (heavy computation, image/video processing): a long synchronous task blocks the event loop and stalls all other requests until it finishes.
Common mistakes
- ✗Running CPU-heavy synchronous work on the main thread, blocking the event loop
- ✗Thinking Node is multi-threaded for application code by default
- ✗Assuming concurrency requires one OS thread per connection
Follow-up questions
- →How can you keep a CPU-heavy task from blocking the event loop?
- →Why does one slow synchronous function affect all pending requests?
MiddleTheoryOccasionalIn Node.js, how do worker_threads and child_process differ for using many cores?
In Node.js, how do worker_threads and child_process differ for using many cores?
Both add parallelism beyond the single main thread. worker_threads run JS on threads inside the same process, sharing memory cheaply via SharedArrayBuffer and communicating with low overhead — ideal for CPU-bound work. child_process (spawn/fork/exec) launches separate OS processes with isolated memory, communicating over IPC or pipes — better for running other programs or full isolation. Use a cluster to spread an HTTP server across cores.
Common mistakes
- ✗Expecting
worker_threadsto share ordinary variables withoutSharedArrayBufferor messaging - ✗Reaching for
child_processfor CPU work where in-process threads have lower overhead - ✗Thinking Node can use multiple cores with no workers, cluster, or child processes
Follow-up questions
- →When is the isolation of a child process worth its higher communication cost?
- →How does the cluster module spread incoming connections across cores?
MiddleTheoryOccasionalWhat are the phases of the Node.js event loop, and where do setImmediate and process.nextTick run?
What are the phases of the Node.js event loop, and where do setImmediate and process.nextTick run?
The libuv event loop cycles through ordered phases: timers (setTimeout), pending callbacks, poll (I/O), check (setImmediate), and close callbacks. Between every callback it drains two microtask queues: process.nextTick first, then promise callbacks.
Common mistakes
- ✗Treating
setImmediateas identical tosetTimeout(fn, 0) - ✗Thinking promise microtasks run before
process.nextTick - ✗Expecting
setTimeout(0)vssetImmediateorder to be deterministic at the top level
Follow-up questions
- →Inside an I/O callback, why does
setImmediatereliably beatsetTimeout(0)? - →How can a recursive
process.nextTickstarve the I/O (poll) phase?
MiddleTheoryRareWhat is graceful shutdown in Node.js, and why does it matter?
What is graceful shutdown in Node.js, and why does it matter?
Graceful shutdown means that on a termination signal (SIGTERM/SIGINT) the process stops accepting new work, finishes in-flight requests, closes resources (DB pools, sockets, the HTTP server), then exits. It matters because an abrupt exit drops live requests, leaks connections, and can corrupt partial writes. A timeout forces exit if cleanup hangs, so a stuck connection can't block the shutdown forever.
Common mistakes
- ✗Exiting on
SIGTERMimmediately, dropping requests still being served - ✗Forgetting to close DB pools and sockets, leaking connections on redeploys
- ✗Omitting a force-exit timeout, so a hung connection blocks shutdown forever
Follow-up questions
- →Why is a force-exit timeout needed alongside the clean shutdown path?
- →Which signal does an orchestrator like Kubernetes send before killing a pod?