Backpressure
Backpressure is the mechanism that prevents a fast producer from overwhelming a slow consumer by signaling the producer to pause. In Node.js streams, writable.write() returns false when the consumer's buffer is full.
Backpressure = a toilet that says 'STOP FLUSHING, I'm full!' If you ignore the signal (write() returning false) and keep flushing, the bathroom floods (OOM crash). pipe() is the auto-flush handle that listens for you.
When writable.write() returns false, the producer should stop writing and wait for the 'drain' event before resuming. Failing to respect backpressure causes the internal buffer to grow unboundedly, eventually exhausting heap memory. pipe() handles backpressure automatically—it pauses the readable when write() returns false and resumes it on 'drain'. The highWaterMark option (default 16KB for byte streams, 16 objects for object mode) controls when write() returns false.
In practice, backpressure failures are common in code that uses async iteration over readable streams without careful attention to the writable side. When writing: `for await (const chunk of readable) { writable.write(chunk); }` the loop doesn't wait for 'drain', bypassing backpressure entirely. The correct pattern is to await a promisified version of 'drain' when write() returns false. stream.pipeline() (Node.js ≥10) handles this correctly and also propagates errors and cleans up streams on failure—unlike the original pipe() which doesn't forward errors and can leave streams open on failure.
Backpressure prevents producer-consumer imbalance. writable.write() returns false when the internal buffer exceeds highWaterMark; producers must stop and wait for 'drain'. pipe() and stream.pipeline() handle this automatically. Manual stream consumption via for-await or direct write() calls requires explicit backpressure handling to avoid OOM crashes.
pipe() does not propagate errors—if the readable or writable emits an error, the other stream is not automatically destroyed, causing resource leaks (open file handles, sockets). Always use stream.pipeline() in production code, which handles error propagation and cleanup.