> ## Documentation Index
> Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Node.js

> Runtime, Events, Streams, and Backend Scaling

# Node.js Interview Questions (55+ Detailed Q\&A)

## 1. Runtime & Core Architecture

<AccordionGroup>
  <Accordion title="1. What is Node.js? Is it a framework?">
    **Answer**:
    No, Node.js is a **Runtime Environment** for executing JavaScript outside the browser -- it is not a framework, not a language, and not a web server (though it can create one).

    **Architecture stack**:

    * **Chrome V8 Engine**: Compiles JS directly to machine code (no interpreter step). Uses JIT (Just-In-Time) compilation with TurboFan optimizing compiler and Sparkplug baseline compiler.
    * **Libuv**: A C library that provides the event loop, async I/O (file system, DNS, network), a thread pool (default 4 threads), and cross-platform abstractions over OS-level async primitives (`epoll` on Linux, `kqueue` on macOS, `IOCP` on Windows).
    * **C++ bindings**: Bridge between JS and native OS APIs (crypto, zlib, http\_parser).

    **Key mental model**: Node is a single-threaded orchestrator. Your JS runs on one thread, but the actual I/O work is delegated to the OS kernel or Libuv's thread pool. This is what makes it non-blocking.

    **What interviewers are really testing**: Do you understand that Node is not "just JavaScript"? Can you articulate the relationship between V8, Libuv, and the event loop? Candidates who say "Node.js is a JavaScript framework" are an instant red flag.

    **Red flag answer**: "Node.js is a server-side framework for building web apps." This conflates the runtime with Express/Koa frameworks and shows surface-level understanding.

    **Follow-up**:

    * "If V8 compiles JS to machine code, where does the bytecode step fit in? What is Ignition vs TurboFan?"
    * "Can you explain what Libuv's thread pool is used for vs what goes directly to the OS kernel?"
    * "Why would Node choose to be single-threaded instead of multi-threaded like Java's Tomcat?"
  </Accordion>

  <Accordion title="2. Single Threaded but Scalable? How?">
    **Answer**:
    Node uses a **Single Main Thread** for JS execution (the Event Loop), but this is only half the story.

    **How it actually scales**:

    1. **Event Loop** (single thread): Orchestrates callbacks, runs your JS code, and dispatches I/O requests.
    2. **OS Kernel Async Primitives**: Network I/O (TCP sockets, HTTP) goes directly to the kernel via `epoll`/`kqueue`/`IOCP` -- zero threads needed. This is how Node handles 10k+ concurrent connections on a single process.
    3. **Libuv Thread Pool** (default 4 threads, max 1024 via `UV_THREADPOOL_SIZE`): Used for operations the OS cannot do asynchronously -- file system operations, DNS lookups (`dns.lookup`, not `dns.resolve`), and some crypto operations.

    **The C10K advantage**: Traditional thread-per-request models (Apache) need \~2MB per thread. 10k connections = 20GB RAM just for threads. Node handles 10k connections with a single thread + kernel event notifications, using \~50-100MB total.

    **Real-world example**: A chat server handling 50k concurrent WebSocket connections. Each connection is just a file descriptor in the kernel's epoll set -- no thread allocation per connection.

    **What interviewers are really testing**: Can you distinguish between "single-threaded JS" and "Node uses threads behind the scenes"? Strong candidates know about the Libuv thread pool, `UV_THREADPOOL_SIZE`, and which operations use threads vs kernel async.

    **Red flag answer**: "Node is single-threaded so it can only do one thing at a time." This misunderstands the delegation model entirely.

    **Follow-up**:

    * "If the thread pool default is 4, what happens when you have 100 concurrent `fs.readFile` calls? Does the 101st block?"
    * "When would you increase `UV_THREADPOOL_SIZE` and what's the cost of making it too large?"
    * "How does the Cluster module change this picture? What about Worker Threads?"
  </Accordion>

  <Accordion title="3. Event Loop Phases (Deep Dive)">
    **Answer**:
    The event loop is not a single queue -- it is a **series of phases**, each with its own FIFO queue. Understanding this is critical for debugging timing issues.

    **The 6 Phases** (in order):

    1. **Timers**: Execute callbacks from `setTimeout` and `setInterval` whose threshold has elapsed. Note: timers are not guaranteed to fire at the exact time -- they fire as soon as possible *after* the threshold, in this phase.
    2. **Pending Callbacks**: Execute I/O callbacks deferred from the previous loop iteration (e.g., TCP errors like `ECONNREFUSED`).
    3. **Idle/Prepare**: Internal use only by Libuv. You cannot interact with this phase.
    4. **Poll**: This is the **heart** of the event loop. It retrieves new I/O events from the kernel, executes their callbacks (file read complete, socket data received, etc.). If the poll queue is empty, it will block here waiting for new events (up to a calculated timeout based on pending timers).
    5. **Check**: `setImmediate` callbacks execute here. This is specifically designed to run callbacks after the poll phase completes.
    6. **Close Callbacks**: `socket.on('close')`, cleanup handlers.

    **Microtask Queues** (run between *every* phase transition):

    * `process.nextTick` queue (higher priority -- runs first)
    * Promise microtask queue (`.then`, `.catch`, `.finally`)

    ```mermaid theme={null}
    graph TD
        Start([Event Loop Start])
        Timers["1. Timers Phase<br/>(setTimeout, setInterval)"]
        Pending["2. Pending Callbacks<br/>(I/O errors)"]
        Poll["3. Poll Phase<br/>(I/O events)"]
        Check["4. Check Phase<br/>(setImmediate)"]
        Close["5. Close Callbacks<br/>(socket.close)"]
        Micro["Microtasks<br/>(nextTick, Promises)"]
        
        Start --> Timers
        Timers --> Micro
        Micro --> Pending
        Pending --> Micro
        Micro --> Poll
        Poll --> Micro
        Micro --> Check
        Check --> Micro
        Micro --> Close
        Close --> Micro
        Micro --> Timers
    ```

    **Execution Order Example**:

    ```javascript theme={null}
    setTimeout(() => console.log('timeout'), 0);
    setImmediate(() => console.log('immediate'));
    process.nextTick(() => console.log('nextTick'));
    Promise.resolve().then(() => console.log('promise'));

    console.log('sync');

    // Output:
    // sync
    // nextTick
    // promise
    // timeout (or immediate, depends on timing)
    // immediate (or timeout)
    ```

    **Why Order Varies**: `setTimeout(fn, 0)` actually means `setTimeout(fn, 1)` internally (minimum 1ms). If the event loop enters the timers phase in under 1ms, the timer hasn't elapsed yet, so `setImmediate` (check phase) fires first. If it takes longer than 1ms to enter timers, `setTimeout` fires first. This is **non-deterministic** in the main module but **deterministic inside an I/O callback** (setImmediate always fires first there, because after I/O you're in the poll phase, and check phase comes next).

    ```javascript theme={null}
    // DETERMINISTIC: Inside I/O callback, setImmediate always first
    const fs = require('fs');
    fs.readFile(__filename, () => {
        setTimeout(() => console.log('timeout'), 0);
        setImmediate(() => console.log('immediate'));
    });
    // Always: immediate, timeout
    ```

    **What interviewers are really testing**: Can you explain the phases without just listing names? Do you know *why* the poll phase blocks, what microtasks are, and the `setTimeout(0)` vs `setImmediate` indeterminacy? This question separates candidates who have read the docs from those who have debugged timing bugs.

    **Red flag answer**: "The event loop just processes callbacks in order" or listing phases without understanding what the poll phase actually does.

    **Follow-up**:

    * "What happens if you schedule a `process.nextTick` inside a `process.nextTick` recursively? How does that differ from recursive `setImmediate`?"
    * "If I have a `setTimeout(fn, 5)` and the poll phase is blocked waiting for I/O for 10ms, does the timer fire late?"
    * "In Node 11+, what changed about microtask execution order between phases vs inside a single phase?"
  </Accordion>

  <Accordion title="4. `process.nextTick` vs `setImmediate`">
    **Answer**:
    These are two fundamentally different scheduling mechanisms with different positions in the event loop.

    **`process.nextTick`**:

    * Runs **immediately** after the current operation completes, **before** the event loop continues to the next phase.
    * Part of the **microtask queue** (along with Promise callbacks), but `nextTick` has higher priority than Promises.
    * **Starvation risk**: Recursive `nextTick` will starve I/O because the event loop never advances past the microtask checkpoint.
    * **Use case**: Emitting events after a constructor returns (so the caller can attach listeners), ensuring a callback fires before any I/O.

    **`setImmediate`**:

    * Runs in the **Check phase** of the event loop (after the poll phase).
    * **Cannot starve I/O**: Even recursive `setImmediate` allows the event loop to process I/O, timers, etc. between iterations.
    * **Use case**: Breaking up CPU work so I/O can still be processed; preferred for recursive scheduling.

    **Starvation Example**:

    ```javascript theme={null}
    // BAD: Infinite nextTick loop blocks everything!
    function recursiveNextTick() {
        process.nextTick(recursiveNextTick);
    }
    recursiveNextTick();
    // Event loop NEVER proceeds to other phases!
    // setTimeout callbacks never fire, I/O never processes.

    // GOOD: setImmediate allows other phases to run
    function recursiveImmediate() {
        setImmediate(recursiveImmediate);
    }
    recursiveImmediate();
    // Event loop can still handle I/O, timers, etc.
    ```

    **Real-world war story**: A production service had a middleware that called `process.nextTick` in a tight loop to "batch" database operations. Under load, this starved the HTTP server's ability to accept new connections. The process appeared alive (health check port was open) but returned zero responses. Switching to `setImmediate` fixed the issue instantly.

    **The naming is backwards**: `process.nextTick` fires *before* the next tick of the event loop, and `setImmediate` fires on the next tick (check phase). The Node.js team has acknowledged this is confusing but cannot change it due to backward compatibility.

    **What interviewers are really testing**: Do you understand microtask starvation? Can you articulate when each is appropriate? Have you ever been bitten by `nextTick` in production?

    **Red flag answer**: "They're basically the same thing" or "I always use `setTimeout(fn, 0)` instead."

    **Follow-up**:

    * "If you needed to guarantee a callback fires before any pending I/O but after the current synchronous code, which would you use and why?"
    * "How does `queueMicrotask()` relate to `process.nextTick`? Which runs first?"
    * "In a real application, give me a concrete example where using `nextTick` instead of `setImmediate` caused a bug."
  </Accordion>

  <Accordion title="5. V8 Engine Memory Limit">
    **Answer**:
    V8 divides its heap into **New Space** (short-lived objects, \~1-8MB) and **Old Space** (long-lived objects). The total heap limit defaults to approximately **1.5GB on 64-bit systems** and \~700MB on 32-bit systems.

    **Adjusting the limit**:

    ```bash theme={null}
    node --max-old-space-size=4096 app.js   # 4GB old space
    node --max-semi-space-size=64 app.js     # 64MB semi-space (new space half)
    ```

    **Why the limit exists**: V8's garbage collector must pause JS execution during major GC (mark-sweep-compact). Larger heaps mean longer GC pauses. At 4GB, you might see 500ms+ GC pauses, which is catastrophic for an HTTP server handling real-time requests.

    **Real-world considerations**:

    * **Container environments**: If your container has 2GB RAM and you set `--max-old-space-size=4096`, Node will be OOM-killed by the kernel. Always set the flag to \~75% of your container's memory limit (leave room for native memory, Buffers, thread stacks).
    * **Buffers and native memory**: `Buffer` allocations live **outside** the V8 heap. A process can use 500MB of Buffers while reporting only 100MB heap usage. `process.memoryUsage()` shows both `heapUsed` and `external` (C++ objects including Buffers).

    **What interviewers are really testing**: Do you know about memory regions beyond the heap? Can you reason about memory in containerized deployments?

    **Red flag answer**: "Just increase the memory flag if you run out" without considering GC pause impact or container limits.

    **Follow-up**:

    * "What's the difference between `heapUsed`, `heapTotal`, `rss`, and `external` in `process.memoryUsage()`?"
    * "How would you size the `--max-old-space-size` flag for a Node process running in a Kubernetes pod with a 1GB memory limit?"
    * "What happens to GC pause times as you increase heap size? How does this affect P99 latency?"
  </Accordion>

  <Accordion title="6. Garbage Collection in Node">
    **Answer**:
    V8 uses a **Generational Garbage Collector** based on the observation that most objects die young (the "generational hypothesis").

    **Heap Regions**:

    * **New Space** (Young Generation): Split into two semi-spaces (\~1-8MB each). New objects are allocated here.
    * **Old Space** (Old Generation): Objects that survive two GC cycles in New Space get promoted here.
    * **Large Object Space**: Objects too large for New Space go directly here.
    * **Code Space**: JIT-compiled code.
    * **Map Space**: Hidden class (Shape/Map) metadata.

    **GC Algorithms**:

    1. **Scavenge (Minor GC)**: Runs on New Space. Uses Cheney's algorithm -- copies live objects from one semi-space to the other, then clears the old semi-space. Very fast (\~1-5ms) because New Space is small. Objects surviving 2 scavenges get promoted to Old Space.
    2. **Mark-Sweep-Compact (Major GC)**: Runs on Old Space. Three stages -- **Mark** (walk the object graph from roots, mark reachable objects), **Sweep** (free unmarked objects), **Compact** (defragment memory). This is the expensive one (50-500ms+) and causes "stop-the-world" pauses.
    3. **Incremental Marking**: V8 breaks the marking phase into small chunks interleaved with JS execution (increments of \~5ms) to reduce max pause time.
    4. **Concurrent Sweeping/Compaction**: V8 runs sweeping and some compaction on background threads while JS continues executing.

    **Orinoco (V8's modern GC)**: Combines concurrent marking, parallel scavenging, and concurrent sweeping to minimize main-thread pauses. In practice, most GC pauses in modern Node (v18+) are under 10ms.

    **Monitoring GC in production**:

    ```bash theme={null}
    node --trace-gc app.js        # Log GC events
    node --expose-gc app.js       # Allow manual GC via global.gc()
    ```

    **What interviewers are really testing**: Can you explain *why* generational GC works (most objects die young)? Do you know the difference between minor and major GC? Can you reason about GC impact on latency?

    **Red flag answer**: "V8 handles garbage collection automatically, you don't need to worry about it." In production at scale, GC is one of the top causes of latency spikes.

    **Follow-up**:

    * "You see a Node process with 2GB heap and periodic 200ms latency spikes. How do you determine if GC is the cause?"
    * "What is an 'old space expansion' and how does it differ from a normal major GC?"
    * "How do closures and event listeners contribute to objects being unintentionally promoted to old space?"
  </Accordion>

  <Accordion title="7. Global Objects">
    **Answer**:
    Node.js provides several global objects and module-scoped variables (which look global but are not truly on the `global` object):

    **Truly global** (on the `global` object):

    * `global` itself (equivalent to `window` in browsers, or `globalThis` in ES2020+)
    * `process` -- the current Node process (PID, env vars, stdin/stdout, exit codes, memory usage)
    * `Buffer` -- binary data handling
    * `console` -- logging
    * `setTimeout`, `setInterval`, `setImmediate`, `queueMicrotask`

    **Module-scoped** (injected by the CommonJS module wrapper, NOT on `global`):

    * `__dirname` -- absolute path of the directory containing the current file
    * `__filename` -- absolute path of the current file
    * `require` -- the module loader function
    * `module` -- reference to the current module
    * `exports` -- shorthand for `module.exports`

    **The module wrapper**: Every CommonJS file is wrapped in:

    ```javascript theme={null}
    (function(exports, require, module, __filename, __dirname) {
        // Your code here
    });
    ```

    This is why `__dirname` is not available in ESM modules -- use `import.meta.url` with `fileURLToPath` instead.

    **ESM equivalents**:

    ```javascript theme={null}
    // CommonJS
    const dir = __dirname;

    // ESM
    import { fileURLToPath } from 'url';
    import { dirname } from 'path';
    const __filename = fileURLToPath(import.meta.url);
    const __dirname = dirname(__filename);
    ```

    **What interviewers are really testing**: Do you know the difference between truly global objects and module-scoped variables? Can you explain the CJS module wrapper?

    **Red flag answer**: Listing `__dirname` as a global object without knowing it's module-scoped, or not knowing the ESM equivalents.

    **Follow-up**:

    * "Why is `__dirname` not available in ES modules? How do you get the equivalent?"
    * "What is `globalThis` and why was it introduced?"
    * "If you set `global.myVar = 42` in one module, can another module access it? What are the dangers of this?"
  </Accordion>

  <Accordion title="8. CommonJS vs ESM">
    **Answer**:
    These are two fundamentally different module systems with different loading semantics.

    **CommonJS (CJS)**:

    * `require()` / `module.exports`
    * **Synchronous** loading -- `require` blocks until the module is loaded and evaluated.
    * **Dynamic** -- you can `require()` inside an `if` block or compute the module path at runtime.
    * **Loads a copy** (cached after first load in `require.cache`).
    * **Circular dependency handling**: Returns a partially filled `module.exports` (whatever was exported *so far* at the point of circular reference). This can cause subtle bugs where you get `undefined` for properties that haven't been assigned yet.
    * Default in Node (`.js` files without `"type": "module"` in `package.json`).

    **ES Modules (ESM)**:

    * `import` / `export`
    * **Asynchronous** loading -- parsed statically, loaded in parallel.
    * **Static** -- imports must be at the top level (enables tree-shaking and static analysis).
    * **Live bindings** -- importing a value gives you a *live reference* to the exporting module's binding. If the export changes, the import sees the new value.
    * **Circular dependency handling**: Due to live bindings, circular deps work more predictably (but you can still access uninitialized bindings if the exporting module hasn't run that code yet).
    * Enabled via `"type": "module"` in `package.json` or `.mjs` extension.

    **Interop gotchas**:

    * CJS can `require()` CJS. ESM can `import` ESM.
    * ESM can `import` CJS (the `module.exports` object becomes the default export).
    * CJS **cannot** `require()` ESM directly (must use dynamic `import()` which returns a Promise).
    * `__dirname`, `require.cache`, `module` are not available in ESM.

    **What interviewers are really testing**: Do you understand the *semantic* differences (sync vs async, copy vs live binding), not just the syntax? Can you navigate the interop challenges?

    **Red flag answer**: "CJS uses `require`, ESM uses `import`, they're basically the same."

    **Follow-up**:

    * "What is a 'live binding' in ESM and how does it differ from CJS's value copy?"
    * "You have a library that only ships CJS. How do you use it in an ESM project? What about the reverse?"
    * "What are the implications of CJS's synchronous loading for server startup time with hundreds of modules?"
  </Accordion>

  <Accordion title="9. `exports` vs `module.exports`">
    **Answer**:
    This trips up many developers. The key insight: `exports` is just a **convenience reference** that initially points to the same object as `module.exports`.

    ```javascript theme={null}
    // Internally, at module start:
    var module = { exports: {} };
    var exports = module.exports; // Same reference!
    ```

    **What works**:

    ```javascript theme={null}
    exports.a = 1;        // module.exports is { a: 1 } -- works!
    exports.b = 2;        // module.exports is { a: 1, b: 2 } -- works!
    ```

    **What breaks**:

    ```javascript theme={null}
    exports = { a: 1 };   // BREAKS! exports now points to a NEW object.
                           // module.exports is still {} (empty).
                           // The require() caller gets {}
    ```

    **Why it breaks**: `require()` always returns `module.exports`, never `exports`. When you reassign `exports`, you break the reference but `module.exports` is unchanged.

    **Rule**: If you are assigning an entirely new object, class, or function as the module's export, always use `module.exports`:

    ```javascript theme={null}
    // Exporting a class
    module.exports = class MyService { /* ... */ };

    // Exporting a function
    module.exports = function handler(req, res) { /* ... */ };
    ```

    **What interviewers are really testing**: Do you understand JavaScript reference semantics? This is really a question about how object references work.

    **Red flag answer**: "I just always use `module.exports` and never use `exports`" -- this works in practice but shows you don't understand *why*.

    **Follow-up**:

    * "Can you draw a diagram showing what happens to the reference when you do `exports = { a: 1 }` vs `exports.a = 1`?"
    * "In ESM, does this same confusion exist? Why or why not?"
  </Accordion>

  <Accordion title="10. Handling CPU Intensive Tasks">
    **Answer**:
    Node's Achilles' heel: CPU-bound work blocks the event loop, making the entire server unresponsive. A single 100ms CPU computation means every other request waits 100ms.

    **Solutions (ordered by preference for most cases)**:

    1. **Worker Threads** (`worker_threads` module):
       * True parallel JS execution with shared memory (`SharedArrayBuffer`) or message passing.
       * Same process, lighter than child processes. Shares the V8 isolate's native addons.
       * Best for: image processing, data parsing, cryptographic operations.
       * Gotcha: Each worker has its own V8 instance and event loop, so memory overhead is \~5-10MB per worker.

    2. **Child Process** (`child_process.fork`):
       * Spawns an entirely new Node.js process with its own V8 instance and memory space.
       * Communication via IPC (inter-process communication) channel.
       * Best for: isolating untrusted code, workloads that might crash (OOM), or when you need full process isolation.
       * Gotcha: Higher overhead (\~30MB per process), slower IPC than shared memory.

    3. **Offload to specialized service**:
       * Send CPU work to a Go/Rust microservice or a job queue (Bull/BullMQ with Redis, RabbitMQ).
       * Best for: video transcoding, ML inference, PDF generation. Let Node do what it's good at (I/O orchestration).

    4. **Native Addons (N-API/NAPI)**:
       * Write the CPU-intensive function in C/C++ or Rust (via neon) and call it from Node.
       * Best for: tight loops, numerical computation, when you need both speed and integration.

    **How to detect Event Loop blockage**:

    ```javascript theme={null}
    // Simple event loop lag monitor
    let lastCheck = Date.now();
    setInterval(() => {
        const now = Date.now();
        const lag = now - lastCheck - 1000; // Expected 1000ms interval
        if (lag > 50) console.warn(`Event loop lag: ${lag}ms`);
        lastCheck = now;
    }, 1000);
    ```

    **What interviewers are really testing**: Do you know *why* CPU tasks are problematic in Node, and can you pick the right solution based on the constraints? A staff-level answer includes trade-offs between the approaches.

    **Red flag answer**: "Just use async/await" -- async/await does NOT help with CPU-bound work. `await cpuIntensiveFunction()` still blocks the event loop if the function is synchronous.

    **Follow-up**:

    * "What's the difference between `worker_threads` and `child_process.fork` in terms of memory isolation, startup cost, and communication overhead?"
    * "If a CPU task takes 50ms, is that enough to worry about? How do you decide the threshold?"
    * "How would you implement a worker thread pool to process image thumbnails? What happens if all workers are busy?"
  </Accordion>
</AccordionGroup>

## 2. Streams, Buffers, I/O

<AccordionGroup>
  <Accordion title="11. What is a Buffer?">
    **Answer**:
    A Buffer is a fixed-size chunk of memory allocated **outside the V8 heap** (on the C++ side via Libuv). It represents raw binary data -- think of it as a byte array like `Uint8Array` (in fact, `Buffer` extends `Uint8Array` since Node 4).

    **Why Buffers exist**: JavaScript strings are UTF-16 encoded and immutable. When dealing with file I/O, network packets, or binary protocols (images, video, protobuf), you need raw byte manipulation -- that's what Buffers provide.

    **Key operations**:

    ```javascript theme={null}
    // Creation (multiple ways)
    const buf1 = Buffer.from('hello', 'utf8');     // From string
    const buf2 = Buffer.alloc(1024);                // Zero-filled (safe)
    const buf3 = Buffer.allocUnsafe(1024);          // Uninitialized (fast but dangerous)
    const buf4 = Buffer.from([0x48, 0x65, 0x6c]);  // From byte array

    // Encoding conversions
    buf1.toString('base64');     // 'aGVsbG8='
    buf1.toString('hex');        // '68656c6c6f'
    ```

    **`Buffer.alloc` vs `Buffer.allocUnsafe`**: `allocUnsafe` skips zero-filling the memory -- it's faster but may contain leftover data from previous allocations (a security risk if the Buffer is sent to a client without being fully written). Use `alloc` unless you are immediately overwriting every byte and need the performance.

    **Memory implications**: Buffers are tracked by V8's GC (the JS object is on the heap) but the actual data lives in native memory. This means `process.memoryUsage().external` increases, not `heapUsed`. A process can appear to have low heap usage while consuming gigabytes of native memory via Buffers.

    **What interviewers are really testing**: Do you understand where Buffers live in memory, why they exist separate from strings, and the security implications of `allocUnsafe`?

    **Red flag answer**: "Buffers are just arrays for storing data" -- misses the native memory, encoding, and security aspects.

    **Follow-up**:

    * "When would you use `Buffer.allocUnsafe` over `Buffer.alloc`? What's the risk?"
    * "If you have a Buffer containing a user's password, how do you securely clear it from memory? Can you even do that reliably in Node?"
    * "What's the relationship between `Buffer` and `Uint8Array`? Can you use a Buffer anywhere you'd use a typed array?"
  </Accordion>

  <Accordion title="12. Streams: 4 Types">
    **Answer**:
    Streams are Node's abstraction for handling **flowing data** -- they let you process data piece-by-piece instead of loading everything into memory at once.

    **The 4 Types**:

    1. **Readable**: Produces data. Examples: `fs.createReadStream`, `http.IncomingMessage` (request body), `process.stdin`. Has two modes: **flowing** (data events, automatic) and **paused** (must call `.read()` manually).

    2. **Writable**: Consumes data. Examples: `fs.createWriteStream`, `http.ServerResponse`, `process.stdout`. Returns `false` from `.write()` when its internal buffer is full (backpressure signal).

    3. **Duplex**: Both Readable and Writable, with **independent** buffers. Examples: TCP sockets (`net.Socket`), WebSocket connections. The read and write sides operate independently.

    4. **Transform**: A special Duplex where the output is a transformation of the input. The read and write sides are **connected**. Examples: `zlib.createGzip()` (compress), `crypto.createCipheriv()` (encrypt), CSV parsers. Data goes in one side, gets modified, comes out the other.

    **Object Mode**: By default, streams handle `Buffer`/`string` data. In object mode (`objectMode: true`), streams can handle any JS value. Used in database cursors, JSON parsers, etc.

    **The `pipeline` function** (preferred over `.pipe()`):

    ```javascript theme={null}
    const { pipeline } = require('stream/promises');

    await pipeline(
        fs.createReadStream('input.csv'),
        csvParser,
        transformStream,
        fs.createWriteStream('output.json')
    );
    // Automatically handles errors and cleanup!
    ```

    **What interviewers are really testing**: Can you explain when to use each type, the difference between Duplex and Transform, and do you know about flowing vs paused mode?

    **Red flag answer**: Only knowing Readable and Writable, or not knowing that Transform is a subtype of Duplex.

    **Follow-up**:

    * "What is the difference between Duplex and Transform? Can you give an example where you'd use Duplex but not Transform?"
    * "What are the two modes of a Readable stream? How do you switch between them?"
    * "Why is `stream.pipeline()` preferred over `.pipe()` chaining?"
  </Accordion>

  <Accordion title="13. Pipe & Backpressure">
    **Answer**:
    **Backpressure** is the mechanism that prevents a fast producer from overwhelming a slow consumer. Without it, data accumulates in memory and you get OOM crashes.

    **How it works mechanically**:

    1. Readable stream pushes data to the Writable stream via `.write()`.
    2. `.write()` returns `false` when the Writable's internal buffer exceeds `highWaterMark` (default 16KB for Buffer streams, 16 objects for object mode).
    3. The Readable must stop reading (`.pause()` in flowing mode, stop calling `.push()` in custom streams).
    4. When the Writable's buffer drains, it emits a `'drain'` event.
    5. The Readable resumes.

    **`.pipe()` handles this automatically**:

    ```javascript theme={null}
    readable.pipe(writable);
    // Internally: pauses readable when writable signals backpressure,
    // resumes on drain. Handles error propagation (partially).
    ```

    **The problem with `.pipe()`**: It does NOT properly propagate errors through the chain or clean up streams on failure. A broken stream in a pipe chain can cause memory leaks.

    **Solution -- use `pipeline`**:

    ```javascript theme={null}
    const { pipeline } = require('stream');

    pipeline(
        readableStream,
        transformStream,
        writableStream,
        (err) => {
            if (err) console.error('Pipeline failed:', err);
            else console.log('Pipeline succeeded');
        }
    );
    ```

    **Real-world war story**: A file upload service used `.pipe()` without backpressure awareness. Users uploading over a fast network to a slow disk caused Node's memory to spike to 8GB before the process was OOM-killed. The fix: switching to `pipeline` and setting appropriate `highWaterMark` values.

    **What interviewers are really testing**: Do you understand *why* backpressure exists, not just how to use `.pipe()`? Can you explain the `highWaterMark` and the `drain` event?

    **Red flag answer**: "Just use `.pipe()` and it handles everything." This ignores error handling and shows no understanding of the underlying mechanism.

    **Follow-up**:

    * "What happens if you ignore the `false` return value from `.write()` and keep writing?"
    * "How would you implement custom backpressure in a Transform stream?"
    * "What is `highWaterMark` and how do you decide what value to set?"
  </Accordion>

  <Accordion title="14. `fs.readFile` vs `fs.createReadStream`">
    **Answer**:
    This is the fundamental question of **buffering vs streaming** in Node.

    **`fs.readFile`**:

    * Reads the **entire file** into a single Buffer in memory.
    * Simple API: `const data = await fs.promises.readFile('file.txt')`.
    * Memory usage = file size. A 2GB file requires 2GB of RAM.
    * Use for: small files (config, JSON, templates) -- generally files under 10-50MB.

    **`fs.createReadStream`**:

    * Reads in **chunks** (default 64KB each, configurable via `highWaterMark`).
    * Memory usage is constant (\~64KB buffer) regardless of file size.
    * Must process data in streaming fashion (events or pipe).
    * Use for: large files, file uploads/downloads, anything where you're transforming and forwarding data.

    **The numbers that matter**:

    ```javascript theme={null}
    // Reading a 1GB file:
    // readFile: ~1GB RAM, ~2-3 seconds to start processing (must read entire file first)
    // createReadStream: ~64KB RAM, starts processing within milliseconds

    // Reading a 10KB config file:
    // readFile: simpler code, negligible memory
    // createReadStream: unnecessary complexity for this size
    ```

    **The streaming pipeline for file processing**:

    ```javascript theme={null}
    const { pipeline } = require('stream/promises');
    const fs = require('fs');
    const zlib = require('zlib');

    // Compress a 5GB log file with constant memory usage
    await pipeline(
        fs.createReadStream('huge.log'),
        zlib.createGzip(),
        fs.createWriteStream('huge.log.gz')
    );
    ```

    **What interviewers are really testing**: Do you default to streaming for large data? This reveals whether you've built systems that handle real-world file sizes.

    **Red flag answer**: "I always use `readFile` because it's simpler." This works until someone uploads a 500MB file and your server crashes.

    **Follow-up**:

    * "You're building an API that accepts CSV uploads and inserts rows into a database. Walk me through how you'd do this for a 2GB file."
    * "What is the `highWaterMark` option on `createReadStream` and when would you tune it?"
    * "How does `fs.promises.readFile` differ from `fs.readFileSync` in terms of event loop impact?"
  </Accordion>

  <Accordion title="15. Event Emitter Pattern">
    **Answer**:
    EventEmitter is the **pub/sub backbone** of Node.js. Almost every core module (Streams, HTTP Server, Process) inherits from it.

    ```javascript theme={null}
    const { EventEmitter } = require('events');
    const emitter = new EventEmitter();

    // Subscribe
    emitter.on('order:created', (order) => {
        console.log('Processing order:', order.id);
    });

    // Emit
    emitter.emit('order:created', { id: 123, total: 49.99 });
    ```

    **Key methods**:

    * `.on(event, listener)` -- subscribe (alias: `.addListener`)
    * `.once(event, listener)` -- subscribe, auto-remove after first call
    * `.off(event, listener)` -- unsubscribe (alias: `.removeListener`)
    * `.emit(event, ...args)` -- fire synchronously (listeners run in order of registration)
    * `.listenerCount(event)` -- how many listeners for an event
    * `.removeAllListeners(event?)` -- nuclear option

    **Critical detail -- synchronous execution**: `.emit()` calls all listeners **synchronously** in the order they were added. This means a slow listener blocks subsequent listeners and the caller.

    **Memory leak warning**: By default, Node warns if you add more than 10 listeners to a single event (`MaxListenersExceededWarning`). This usually indicates a bug (adding a listener on every request without removing it).

    ```javascript theme={null}
    // Increase limit (per emitter)
    emitter.setMaxListeners(50);

    // Set globally (use carefully)
    require('events').defaultMaxListeners = 20;
    ```

    **Production pattern -- typed events**:

    ```javascript theme={null}
    class OrderService extends EventEmitter {
        async create(data) {
            const order = await db.orders.create(data);
            this.emit('created', order);      // Decouple side effects
            return order;
        }
    }

    const service = new OrderService();
    service.on('created', sendConfirmationEmail);
    service.on('created', updateInventory);
    service.on('created', notifyWarehouse);
    ```

    **What interviewers are really testing**: Do you know that emit is synchronous? Can you articulate the memory leak risk? Do you use EventEmitter for application-level decoupling?

    **Red flag answer**: "EventEmitter is just for listening to clicks" -- conflating with browser events, or not knowing it's the foundation of Node I/O.

    **Follow-up**:

    * "If `emit` is synchronous, what happens if one listener throws an error? Do other listeners still run?"
    * "You see `MaxListenersExceededWarning` in your logs. What's your debugging approach?"
    * "When would you use EventEmitter vs a message queue like Redis Pub/Sub?"
  </Accordion>

  <Accordion title="16. Handling uncaught exceptions">
    **Answer**:
    Node provides two last-resort safety nets for unhandled errors. Getting this wrong can cause silent data corruption.

    **`process.on('uncaughtException')`**:

    * Fires when a synchronous error is thrown and not caught by any `try/catch`.
    * **Critical rule**: After an uncaught exception, the process is in an **undefined state**. Sockets may be half-written, database transactions may be uncommitted, in-memory state may be inconsistent.
    * **Best practice**: Log the error, flush logs, and **exit the process**. Let your process manager (PM2, systemd, Kubernetes) restart it.

    ```javascript theme={null}
    process.on('uncaughtException', (err, origin) => {
        logger.fatal({ err, origin }, 'Uncaught exception -- shutting down');
        // Give logger time to flush
        setTimeout(() => process.exit(1), 1000);
    });
    ```

    **`process.on('unhandledRejection')`**:

    * Fires when a Promise is rejected and no `.catch()` or `try/catch` (in async/await) handles it.
    * Since Node 15+, unhandled rejections **throw** by default (same as uncaught exceptions). In older versions, they just logged a warning.

    ```javascript theme={null}
    process.on('unhandledRejection', (reason, promise) => {
        logger.fatal({ reason }, 'Unhandled rejection -- shutting down');
        setTimeout(() => process.exit(1), 1000);
    });
    ```

    **The "just catch it and continue" anti-pattern**:

    ```javascript theme={null}
    // TERRIBLE: Swallowing the error and continuing
    process.on('uncaughtException', (err) => {
        console.error(err); // Log and... keep running? 
        // NO! State is corrupted. You might serve wrong data to users.
    });
    ```

    **Defense in depth** (what production services actually do):

    1. Wrap all async route handlers with error-catching middleware.
    2. Use `domain` (deprecated) or AsyncLocalStorage for request-scoped error context.
    3. Set `process.on('uncaughtException')` and `process.on('unhandledRejection')` as the last safety net -- log and exit.
    4. Run behind PM2/Kubernetes that auto-restarts crashed processes.

    **What interviewers are really testing**: Do you understand that `uncaughtException` means the process should die? Or do you treat it as a global catch-all?

    **Red flag answer**: "I use `process.on('uncaughtException')` to catch all errors so the server never goes down." This is actively dangerous -- it means you're serving requests with potentially corrupted state.

    **Follow-up**:

    * "Why is it dangerous to continue running after an uncaught exception? Give a concrete scenario."
    * "How does Express's error-handling middleware relate to `uncaughtException`? If an error is caught by Express, does `uncaughtException` fire?"
    * "What changed in Node 15 regarding unhandled Promise rejections?"
  </Accordion>

  <Accordion title="17. Synchronous vs Asynchronous API">
    **Answer**:
    Node provides both sync and async versions of most `fs` operations. Knowing when to use each is essential.

    **`fs.readFileSync` (blocking)**:

    * Blocks the event loop until the file is fully read.
    * No other request, timer, or I/O callback can execute during this time.
    * On a server handling 1000 req/s, a 10ms sync read means 10ms of total blockage -- \~10 requests delayed.

    **When sync is acceptable**:

    * **At startup** before the server starts listening: loading config files, reading TLS certificates, parsing environment.
    * **CLI tools** that are not serving concurrent requests.
    * **Build scripts** and tooling.

    **When sync is a bug**:

    * Inside any request handler, middleware, or event callback.
    * Inside any `setInterval` or recurring timer.
    * Anywhere after `server.listen()` is called.

    ```javascript theme={null}
    // GOOD: Sync at startup
    const config = JSON.parse(fs.readFileSync('config.json', 'utf8'));
    const server = http.createServer(handler);
    server.listen(3000);

    // BAD: Sync in a request handler
    app.get('/report', (req, res) => {
        const data = fs.readFileSync('report.csv', 'utf8'); // Blocks ALL requests!
        res.send(data);
    });
    ```

    **Sneaky sync operations people miss**:

    * `require()` is synchronous (file read + compile). Never call `require()` inside a hot path.
    * `JSON.parse()` on large strings (100MB+ JSON) is CPU-bound and blocks the loop.
    * `crypto.pbkdf2Sync` -- use the async version `crypto.pbkdf2` instead.
    * `zlib.gzipSync` -- use streaming `zlib.createGzip()` instead.

    **What interviewers are really testing**: Do you know which operations are secretly synchronous? Can you reason about the cascading impact on concurrency?

    **Red flag answer**: "I never use sync functions" (too absolute -- sync at startup is fine and simpler) or "I use sync everywhere because my app isn't high traffic" (will break the moment traffic increases).

    **Follow-up**:

    * "Is `require()` synchronous or asynchronous? What are the implications of calling `require` inside a request handler?"
    * "If `JSON.parse` is synchronous, how would you handle parsing a 500MB JSON file without blocking the event loop?"
    * "How can you detect synchronous operations that are blocking your event loop in production?"
  </Accordion>

  <Accordion title="18. Zlib and Compression">
    **Answer**:
    The `zlib` module provides streaming compression/decompression using Gzip, Deflate, and Brotli algorithms. It is a **Transform stream**, making it composable with pipes.

    **Streaming compression** (the right way):

    ```javascript theme={null}
    const { createGzip, createBrotliCompress } = require('zlib');
    const { pipeline } = require('stream/promises');
    const fs = require('fs');

    // Gzip compression with streaming
    await pipeline(
        fs.createReadStream('access.log'),       // 2GB file
        createGzip({ level: 6 }),                // Compression level 1-9
        fs.createWriteStream('access.log.gz')    // ~200MB output
    );
    // Peak memory: ~64KB (chunk size), NOT 2GB
    ```

    **HTTP compression** (Express):

    ```javascript theme={null}
    const compression = require('compression');

    app.use(compression({
        level: 6,                    // Balance speed vs ratio
        threshold: 1024,             // Don't compress responses under 1KB
        filter: (req, res) => {
            // Skip already-compressed content (images, videos)
            if (req.headers['x-no-compression']) return false;
            return compression.filter(req, res);
        }
    }));
    ```

    **Algorithm comparison**:

    * **Gzip**: Universal support, decent ratio. Standard for HTTP (`Accept-Encoding: gzip`).
    * **Brotli**: \~15-20% better compression than Gzip, but slower to compress. Most modern browsers support it (`Accept-Encoding: br`). Use for static assets pre-compressed at build time.
    * **Deflate**: Legacy. Avoid -- inconsistent implementations across clients.

    **What interviewers are really testing**: Do you think about compression as a streaming operation? Do you know when to use Gzip vs Brotli?

    **Red flag answer**: "I just add the compression middleware and it handles everything." Missing nuance about when NOT to compress (small responses, already-compressed content like images).

    **Follow-up**:

    * "When would you pre-compress assets at build time vs compress on-the-fly? What's the trade-off?"
    * "What happens if you Gzip an already-compressed PNG? Does the file get smaller?"
    * "How does Brotli compare to Gzip in terms of compression ratio, CPU cost, and browser support?"
  </Accordion>

  <Accordion title="19. REPL">
    **Answer**:
    **Read-Eval-Print-Loop** -- Node's interactive shell for rapid prototyping and debugging.

    ```bash theme={null}
    $ node
    > 2 + 2
    4
    > const http = require('http')
    undefined
    > .help    # Show REPL commands
    > .editor  # Enter multi-line mode
    > .exit    # Quit
    ```

    **Beyond basics -- the REPL is a debugging tool**:

    * **Inspect live objects**: Launch your app with `--inspect`, connect Chrome DevTools, use the console as a REPL against the running process.
    * **Custom REPL**: You can create a REPL that exposes your app's internals (database connections, caches) for live debugging in development:

    ```javascript theme={null}
    const repl = require('repl');
    const r = repl.start('app> ');
    r.context.db = require('./db');
    r.context.cache = require('./cache');
    // Now you can type: app> await db.users.findOne({ id: 1 })
    ```

    **What interviewers are really testing**: This is usually a throwaway question. The real test is whether you mention the custom REPL pattern for debugging.

    **Follow-up**:

    * "How would you expose a REPL in a running production service for emergency debugging? What are the security implications?"
  </Accordion>

  <Accordion title="20. OS Module">
    **Answer**:
    The `os` module provides operating system-related utility methods. Critical for Cluster setup, monitoring, and capacity planning.

    ```javascript theme={null}
    const os = require('os');

    os.cpus();                // Array of CPU core info (model, speed, times)
    os.cpus().length;         // Number of logical cores (for Cluster workers)
    os.freemem();             // Free memory in bytes
    os.totalmem();            // Total memory in bytes
    os.loadavg();             // 1, 5, 15 min load averages (Unix only)
    os.networkInterfaces();   // Network interfaces with IPs
    os.hostname();            // Machine hostname
    os.platform();            // 'linux', 'darwin', 'win32'
    os.type();                // 'Linux', 'Darwin', 'Windows_NT'
    os.uptime();              // System uptime in seconds
    ```

    **Production uses**:

    * **Cluster worker count**: `const workers = os.cpus().length` (common pattern, but in containers `os.cpus().length` may report the host's CPUs, not the container's limit -- use `process.env.NUM_WORKERS` or read from cgroup).
    * **Health checks**: Report `freemem()` / `totalmem()` ratio, `loadavg()`.
    * **Platform-specific behavior**: Conditional logic based on `os.platform()` for path separators, shell commands, etc.

    **Container gotcha**: In Docker/Kubernetes, `os.cpus().length` returns the **host machine's** CPU count, not the container's CPU limit. If the host has 64 cores but your pod has a 2-core limit, spawning 64 cluster workers wastes memory and causes excessive context switching.

    **What interviewers are really testing**: Do you know the container gotcha? This separates developers from operators.

    **Red flag answer**: Using `os.cpus().length` blindly for cluster workers in a containerized environment.

    **Follow-up**:

    * "In a Kubernetes pod with a 2-core CPU limit, `os.cpus().length` returns 64. How do you determine the correct number of worker processes?"
    * "How would you build a simple system health dashboard using only Node.js built-in modules?"
  </Accordion>
</AccordionGroup>

## 3. Web & Network (HTTP/Express)

<AccordionGroup>
  <Accordion title="21. Anatomy of HTTP Server">
    **Answer**:
    Node's `http` module provides a low-level HTTP server. Understanding it deeply is essential before using Express.

    ```javascript theme={null}
    const http = require('http');

    const server = http.createServer((req, res) => {
        // req is an http.IncomingMessage (Readable Stream)
        // res is an http.ServerResponse (Writable Stream)
        
        // Reading the request body (it's a stream!)
        let body = '';
        req.on('data', chunk => body += chunk);
        req.on('end', () => {
            res.writeHead(200, { 'Content-Type': 'application/json' });
            res.end(JSON.stringify({ received: body }));
        });
    });

    server.listen(3000);
    ```

    **Key insight**: `req` and `res` are **streams**. The request body is not immediately available -- you must consume the Readable stream. This is why Express needs `express.json()` middleware (it does this consumption for you).

    **What happens under the hood**:

    1. Libuv receives a TCP connection.
    2. `http_parser` (C library, now `llhttp` since Node 12) parses the raw bytes into HTTP headers.
    3. Node creates `IncomingMessage` and `ServerResponse` objects.
    4. Your callback fires with these objects.
    5. Response is written back through the socket.

    **Connection handling**: The server emits events -- `'request'` (most common), `'connection'` (raw TCP socket), `'upgrade'` (WebSocket handshake), `'close'`.

    **What interviewers are really testing**: Do you understand that req/res are streams? Most developers jump to Express without understanding the raw HTTP server.

    **Red flag answer**: "I've never used `http.createServer`, I just use Express." Express is built on top of this -- understanding the foundation matters.

    **Follow-up**:

    * "What is the `'upgrade'` event on an HTTP server and when would you use it?"
    * "How does `http_parser` (now `llhttp`) work, and why was it rewritten?"
    * "What's the difference between `res.end()` and `res.write()` followed by `res.end()`?"
  </Accordion>

  <Accordion title="22. Middleware (Express)">
    **Answer**:
    Middleware is Express's core abstraction -- a function with access to `(req, res, next)` that forms a **chain of responsibility** pattern.

    **Types of middleware**:

    1. **Application-level**: `app.use(fn)` -- runs on every request.
    2. **Route-level**: `router.use(fn)` or `app.get('/path', fn, handler)` -- scoped to routes.
    3. **Error-handling**: `(err, req, res, next)` -- 4-parameter signature, Express detects the arity.
    4. **Built-in**: `express.json()`, `express.static()`, `express.urlencoded()`.
    5. **Third-party**: `cors`, `helmet`, `morgan`, `compression`.

    **Execution flow**:

    ```
    Request --> [cors] --> [helmet] --> [json parser] --> [auth] --> [route handler] --> Response
                                                            |
                                                       [error handler] (if error thrown)
    ```

    **The `next()` contract**:

    * `next()` -- pass to next middleware.
    * `next('route')` -- skip remaining middleware in current route, go to next route.
    * `next(err)` -- skip to error-handling middleware.
    * Not calling `next()` and not sending a response = **request hangs** until client timeout.

    **Real-world middleware stack** (ordered):

    ```javascript theme={null}
    app.use(helmet());                    // Security headers (first!)
    app.use(cors(corsOptions));           // CORS (before routes)
    app.use(compression());              // Response compression
    app.use(express.json({ limit: '10mb' })); // Body parser with size limit
    app.use(morgan('combined'));          // Request logging
    app.use(authMiddleware);              // Authentication
    app.use('/api', rateLimiter);         // Rate limiting on API routes
    app.use('/api', apiRouter);           // Routes
    app.use(notFoundHandler);             // 404 handler
    app.use(errorHandler);                // Error handler (MUST be last)
    ```

    **What interviewers are really testing**: Do you understand middleware ordering matters? Can you reason about what happens when `next()` is not called?

    **Red flag answer**: "Middleware is just a function that runs before the route handler." This misses the chain-of-responsibility pattern, error middleware, and ordering significance.

    **Follow-up**:

    * "What happens if a middleware calls `next()` AND sends a response? What error do you get?"
    * "How does Express distinguish error-handling middleware from regular middleware?"
    * "You have 15 middleware functions and a request takes 500ms. How do you find which middleware is the bottleneck?"
  </Accordion>

  <Accordion title="23. Error Handling Middleware">
    **Answer**:
    Express error-handling middleware has a **4-parameter signature**: `(err, req, res, next)`. Express uses the function's `.length` property (arity) to detect it.

    ```javascript theme={null}
    // This is recognized as error middleware because it has 4 params
    app.use((err, req, res, next) => {
        // Log with context
        logger.error({
            err,
            url: req.originalUrl,
            method: req.method,
            userId: req.user?.id,
            requestId: req.headers['x-request-id']
        });
        
        // Don't leak internal errors to clients
        const statusCode = err.statusCode || 500;
        const message = statusCode === 500 ? 'Internal Server Error' : err.message;
        
        res.status(statusCode).json({
            error: message,
            ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
        });
    });
    ```

    **Critical placement rule**: Error middleware MUST be defined **after** all routes and other middleware. Express processes middleware in order; if the error handler is defined before a route, errors from that route won't reach it.

    **The async error problem**: Express 4 does NOT catch errors thrown in async route handlers. You need a wrapper:

    ```javascript theme={null}
    // Without wrapper: unhandled rejection, server hangs
    app.get('/data', async (req, res) => {
        const data = await db.query('SELECT ...'); // If this throws, Express never catches it
        res.json(data);
    });

    // With wrapper: error is passed to Express error middleware
    const asyncHandler = (fn) => (req, res, next) =>
        Promise.resolve(fn(req, res, next)).catch(next);

    app.get('/data', asyncHandler(async (req, res) => {
        const data = await db.query('SELECT ...');
        res.json(data);
    }));

    // Express 5 fixes this natively -- async errors auto-forward to error middleware
    ```

    **What interviewers are really testing**: Do you know about the async error gap in Express 4? This is one of the most common production bugs in Node web servers.

    **Red flag answer**: "I just add a `try/catch` in every route handler." While this works, it's verbose and error-prone -- missing one handler means an unhandled rejection.

    **Follow-up**:

    * "Why does Express check the function's arity to detect error middleware? What happens if you use arrow functions with destructuring that changes the apparent parameter count?"
    * "How does Express 5 handle async errors differently from Express 4?"
    * "How would you implement different error responses for API routes (JSON) vs web routes (HTML error page)?"
  </Accordion>

  <Accordion title="24. Body Parsing">
    **Answer**:
    HTTP request bodies are streams -- the data arrives in chunks. Body parsing middleware consumes the stream, buffers it, parses it, and attaches the result to `req.body`.

    **Native approach** (no Express):

    ```javascript theme={null}
    const server = http.createServer((req, res) => {
        const chunks = [];
        req.on('data', chunk => chunks.push(chunk));
        req.on('end', () => {
            const body = Buffer.concat(chunks).toString();
            const parsed = JSON.parse(body); // Manual parsing
            // Handle request...
        });
    });
    ```

    **Express built-in parsers**:

    ```javascript theme={null}
    // JSON bodies (Content-Type: application/json)
    app.use(express.json({ limit: '10mb' }));     // ALWAYS set a limit!

    // URL-encoded bodies (Content-Type: application/x-www-form-urlencoded)
    app.use(express.urlencoded({ extended: true })); // extended: true uses qs library (nested objects)

    // Raw binary (Content-Type: application/octet-stream)
    app.use(express.raw({ limit: '50mb' }));

    // Plain text (Content-Type: text/plain)
    app.use(express.text());
    ```

    **Security considerations**:

    * **Always set `limit`**: Without a limit, an attacker can send a 1GB JSON body and OOM your server. Default is `'100kb'`.
    * **`extended: true` vs `false`**: `extended: true` uses the `qs` library which supports nested objects (`user[name]=foo`). `extended: false` uses `querystring` which only supports flat key-value pairs. Nested object parsing can be exploited for prototype pollution -- validate inputs.
    * **Content-Type spoofing**: A request claiming `Content-Type: application/json` but sending garbage will cause `JSON.parse` to throw. The Express `json()` middleware handles this and returns 400.

    **What interviewers are really testing**: Do you set body size limits? Do you understand that body parsing is a streaming operation?

    **Red flag answer**: Using `express.json()` without a `limit` option, or not knowing that `req.body` requires middleware to be populated.

    **Follow-up**:

    * "What happens if `express.json()` is not in the middleware stack and you try to access `req.body`?"
    * "How would you handle a request that sends `Content-Type: application/json` but the body is invalid JSON?"
    * "Why is setting a body size limit a security measure? What specific attack does it prevent?"
  </Accordion>

  <Accordion title="25. CORS in Node">
    **Answer**:
    **Cross-Origin Resource Sharing** -- the browser's mechanism for controlling which origins can make requests to your API.

    **How CORS actually works**:

    1. **Simple requests** (GET, POST with form content types): Browser adds `Origin` header, server responds with `Access-Control-Allow-Origin`. If it doesn't match, browser blocks the *response* (the request still hits your server!).
    2. **Preflight requests** (PUT, DELETE, custom headers, JSON content type): Browser sends an `OPTIONS` request first with `Access-Control-Request-Method` and `Access-Control-Request-Headers`. Server must respond with allowed methods/headers. Only then does the actual request fire.

    **Key headers**:

    * `Access-Control-Allow-Origin`: Which origins can access (`*` or specific origin)
    * `Access-Control-Allow-Methods`: Allowed HTTP methods
    * `Access-Control-Allow-Headers`: Allowed custom headers
    * `Access-Control-Allow-Credentials`: Whether cookies/auth are allowed (`true`/omit)
    * `Access-Control-Max-Age`: How long to cache preflight results (seconds)

    **Critical gotcha**: `Access-Control-Allow-Origin: *` and `Access-Control-Allow-Credentials: true` are **mutually exclusive**. If you need cookies/auth, you must specify the exact origin.

    ```javascript theme={null}
    const cors = require('cors');

    // Production config (NOT `origin: '*'` with credentials)
    app.use(cors({
        origin: ['https://app.example.com', 'https://admin.example.com'],
        credentials: true,
        methods: ['GET', 'POST', 'PUT', 'DELETE'],
        allowedHeaders: ['Content-Type', 'Authorization'],
        maxAge: 86400   // Cache preflight for 24 hours
    }));
    ```

    **CORS is NOT a server-side security measure**: CORS is enforced by browsers. curl, Postman, and server-to-server requests completely bypass CORS. It protects *users* from malicious websites making requests on their behalf, not your API from all unauthorized access.

    **What interviewers are really testing**: Do you understand preflight requests? Do you know CORS is browser-enforced, not server-enforced?

    **Red flag answer**: "I just set `Access-Control-Allow-Origin: *` to fix CORS errors." This disables the protection entirely and breaks credentialed requests.

    **Follow-up**:

    * "A frontend developer says 'I'm getting a CORS error.' Where is the problem -- frontend or backend? How do you debug it?"
    * "Why can't you use `origin: '*'` with `credentials: true`? What would happen if you could?"
    * "If CORS is browser-only, how do you protect your API from unauthorized server-to-server access?"
  </Accordion>

  <Accordion title="26. JWT (JSON Web Token)">
    **Answer**:
    JWT is a **stateless authentication token** -- the server doesn't need to store session data. The token itself contains all the information needed to authenticate the user.

    **Structure** (three Base64URL-encoded parts separated by dots):

    1. **Header**: Algorithm and token type -- `{ "alg": "HS256", "typ": "JWT" }`
    2. **Payload**: Claims (data) -- `{ "sub": "user123", "role": "admin", "exp": 1700000000 }`
    3. **Signature**: `HMACSHA256(base64(header) + "." + base64(payload), secret)` -- proves the token hasn't been tampered with.

    **Critical security details**:

    * **The payload is NOT encrypted** -- it's Base64 encoded (anyone can decode it). Never put passwords, SSNs, or sensitive data in the payload.
    * **Storage options** (ranked by security):
      1. **HttpOnly + Secure + SameSite=Strict cookie** (best -- immune to XSS)
      2. **In-memory variable** (lost on page refresh, but very secure)
      3. **LocalStorage** (convenient but vulnerable to XSS attacks)
    * **HS256 vs RS256**: HS256 uses a shared secret (both sides know it). RS256 uses a public/private key pair (only the auth server has the private key -- better for microservices where you don't want to distribute secrets).

    **Token refresh pattern**:

    * Short-lived access token (15 min) + long-lived refresh token (7 days).
    * Refresh token stored in HttpOnly cookie, access token in memory.
    * When access token expires, use refresh token to get a new one.

    **What interviewers are really testing**: Do you know JWTs are not encrypted? Do you understand the storage trade-offs? Can you explain why refresh tokens exist?

    **Red flag answer**: "I store the JWT in localStorage" without acknowledging the XSS risk, or "JWTs are encrypted so they're secure."

    **Follow-up**:

    * "How do you revoke a JWT before it expires? Doesn't that defeat the purpose of stateless auth?"
    * "What's the difference between HS256 and RS256? When would you choose one over the other?"
    * "A JWT is stolen. What damage can the attacker do, and how do you limit the blast radius?"
  </Accordion>

  <Accordion title="27. Cookies vs Sessions">
    **Answer**:
    This is about **where state lives** -- client-side vs server-side.

    **Sessions (server-side state)**:

    * A random **session ID** is stored in a cookie on the client.
    * The actual session data (user info, cart, preferences) lives on the server in a **session store** (memory, Redis, database).
    * **Stateful**: The server must look up the session on every request.
    * **Pros**: Data isn't exposed to the client, easy to invalidate (delete server-side), no size limit.
    * **Cons**: Server must maintain state (scaling challenge), requires sticky sessions or shared store in clusters.

    **Cookies (client-side state)**:

    * Data is stored directly in the browser cookie.
    * Sent automatically with every request to the cookie's domain.
    * **Size limit**: \~4KB per cookie.
    * **Security flags**: `HttpOnly` (no JS access), `Secure` (HTTPS only), `SameSite` (CSRF protection), `Domain`, `Path`.

    **The scaling question**: Sessions in server memory break when you scale horizontally (load balancer sends request to different server). Solutions:

    1. **Redis session store**: All servers read/write sessions to shared Redis. Standard approach.
    2. **Sticky sessions**: Load balancer always routes a user to the same server. Fragile -- if that server dies, all its sessions are lost.
    3. **JWT (stateless)**: No server-side state needed. Trade-off: harder to revoke.

    **What interviewers are really testing**: Can you reason about state management in distributed systems? Do you know why "sessions in memory" breaks at scale?

    **Red flag answer**: "Cookies store data, sessions store data, they're basically the same." Misses the fundamental architectural distinction.

    **Follow-up**:

    * "You have 10 Node servers behind a load balancer. How do you handle sessions without sticky sessions?"
    * "What's the `SameSite` cookie attribute and how does it protect against CSRF?"
    * "When would you choose session-based auth over JWT, and vice versa?"
  </Accordion>

  <Accordion title="28. File Uploads">
    **Answer**:
    File uploads use `multipart/form-data` encoding -- the body contains multiple parts separated by a boundary string. You need specialized parsing.

    **Common approaches**:

    1. **Multer** (Express middleware): The standard choice. Handles `multipart/form-data` parsing, stores files to disk or memory.
    2. **Busboy/Busbody** (low-level stream parser): Direct stream access, more control, works with any Node HTTP server.
    3. **Formidable**: Alternative to Multer with streaming support.

    **Production-grade upload pattern** (stream to S3, never touch disk):

    ```javascript theme={null}
    const multer = require('multer');
    const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
    const { Upload } = require('@aws-sdk/lib-storage');

    // Use memory storage for small files, stream for large
    const upload = multer({
        storage: multer.memoryStorage(),
        limits: {
            fileSize: 10 * 1024 * 1024,  // 10MB limit
            files: 5                       // Max 5 files
        },
        fileFilter: (req, file, cb) => {
            const allowed = ['image/jpeg', 'image/png', 'application/pdf'];
            cb(null, allowed.includes(file.mimetype));
        }
    });
    ```

    **Security checklist**:

    * **Validate MIME type** on the server (don't trust `Content-Type` -- read magic bytes).
    * **Set file size limits** -- unbounded uploads = DoS vector.
    * **Generate random filenames** -- never use the original filename (path traversal attacks: `../../etc/passwd`).
    * **Don't serve uploads from the same domain** -- use a separate CDN/bucket to prevent XSS via uploaded HTML/SVG files.
    * **Virus scan** uploaded files in production (ClamAV or cloud-based scanning).

    **Serverless/container consideration**: Don't write files to local disk in ephemeral environments (Lambda, ECS Fargate). Stream directly to object storage (S3, GCS).

    **What interviewers are really testing**: Do you think about security, size limits, and storage strategy? Or do you just save to disk and serve from the same server?

    **Red flag answer**: "I save the file to the `uploads/` directory and serve it from Express." This has security, scaling, and reliability problems.

    **Follow-up**:

    * "How would you handle a 5GB video upload without running out of memory?"
    * "Why should you never serve user-uploaded files from the same origin as your application?"
    * "How do you validate that an uploaded file is actually a JPEG and not a renamed executable?"
  </Accordion>

  <Accordion title="29. WebSockets (`socket.io`)">
    **Answer**:
    WebSockets provide **persistent, full-duplex communication** over a single TCP connection. Unlike HTTP's request-response model, either side can send data at any time.

    **The handshake**:

    1. Client sends an HTTP request with `Upgrade: websocket` header.
    2. Server responds with `101 Switching Protocols`.
    3. The TCP connection is now a WebSocket -- no more HTTP framing.

    **Raw WebSocket (`ws` library)** vs **Socket.IO**:

    * **`ws`**: Thin wrapper over the WebSocket protocol. Lightweight, no fallbacks, no rooms, no auto-reconnection. \~40KB.
    * **Socket.IO**: Full-featured library with auto-reconnection, rooms/namespaces, binary support, fallback to HTTP long-polling, acknowledgements. \~300KB client bundle. Uses its own protocol on top of WebSocket.
    * **Key decision**: If all your clients support WebSocket (modern browsers, mobile apps), use `ws`. If you need reliability features and broadcasting, use Socket.IO.

    **Scaling WebSockets** (the hard part):

    * WebSocket connections are stateful -- each is tied to a specific server process.
    * With multiple servers, you need a **pub/sub adapter** (e.g., `@socket.io/redis-adapter`) so a message emitted on Server A reaches clients connected to Server B.
    * Load balancers must support WebSocket upgrades (sticky sessions or connection upgrade pass-through).
    * 10k connections per server process is reasonable; 100k+ requires tuning OS limits (`ulimit -n`).

    **What interviewers are really testing**: Do you know the difference between `ws` and Socket.IO? Can you reason about scaling stateful connections?

    **Red flag answer**: "Socket.IO is WebSocket." No -- Socket.IO is a library that *uses* WebSocket as one of its transports (with HTTP long-polling as fallback).

    **Follow-up**:

    * "You have a chat app on 10 servers. User A is connected to Server 3, User B to Server 7. How does a message get from A to B?"
    * "What happens to WebSocket connections during a deployment/restart? How do you handle this gracefully?"
    * "When would you choose Server-Sent Events (SSE) over WebSocket?"
  </Accordion>

  <Accordion title="30. Keep-Alive Agent">
    **Answer**:
    By default in older Node.js versions, **every outgoing HTTP request creates a new TCP connection** -- that's a 3-way TCP handshake + potential TLS handshake on every request. This is extremely wasteful.

    **The solution -- HTTP Agent with Keep-Alive**:

    ```javascript theme={null}
    const http = require('http');

    const agent = new http.Agent({
        keepAlive: true,         // Reuse connections
        maxSockets: 50,          // Max concurrent connections per host
        maxFreeSockets: 10,      // Max idle connections to keep
        timeout: 60000           // Socket timeout
    });

    // Use with requests
    http.get('http://api.example.com/data', { agent }, (res) => { /* ... */ });
    ```

    **Performance impact**: For a service making 1000 req/s to a downstream API:

    * **Without keep-alive**: 1000 TCP handshakes/sec (\~1ms each) = 1 second of cumulative handshake overhead, plus ephemeral port exhaustion risk.
    * **With keep-alive**: \~50 persistent connections handle all 1000 requests. Near-zero handshake overhead.

    **Node 19+ change**: `globalAgent` has `keepAlive: true` by default. Before Node 19, you had to explicitly enable it.

    **Common pitfall -- socket exhaustion**: If `maxSockets` is too low and request volume is high, requests queue behind the socket limit. If it's too high, you might exhaust the downstream server's connection limit.

    ```javascript theme={null}
    // For axios users
    const axios = require('axios');
    const https = require('https');

    const client = axios.create({
        httpsAgent: new https.Agent({
            keepAlive: true,
            maxSockets: 100
        })
    });
    ```

    **What interviewers are really testing**: Do you know about connection reuse and its impact on latency/throughput? Have you tuned socket pool settings?

    **Red flag answer**: "HTTP connections are handled automatically, you don't need to configure anything." True in modern Node, but this shows no understanding of what's happening underneath.

    **Follow-up**:

    * "What is ephemeral port exhaustion and how does keep-alive prevent it?"
    * "You see `ECONNRESET` errors from a downstream service. What's happening and how does the HTTP agent configuration relate?"
    * "How do you monitor the state of your connection pool (active, idle, pending connections)?"
  </Accordion>
</AccordionGroup>

## 4. Scaling & Performance

<AccordionGroup>
  <Accordion title="31. Cluster Module">
    **Answer**:
    The Cluster module lets you create child processes (workers) that share the same server port, utilizing multiple CPU cores.

    **How it works**:

    1. **Master process** calls `cluster.fork()` to create worker processes.
    2. Workers are full Node.js instances with their own V8 and event loop.
    3. The master accepts incoming connections and distributes them to workers.
    4. **Load balancing**: Round-robin (default on all platforms except Windows) or OS-level (let the kernel decide).

    ```javascript theme={null}
    const cluster = require('cluster');
    const os = require('os');

    if (cluster.isPrimary) {  // 'isPrimary' replaces deprecated 'isMaster' in Node 16+
        const numWorkers = os.cpus().length;
        console.log(`Master ${process.pid} starting ${numWorkers} workers`);
        
        for (let i = 0; i < numWorkers; i++) {
            cluster.fork();
        }
        
        cluster.on('exit', (worker, code, signal) => {
            console.log(`Worker ${worker.process.pid} died (${signal || code}). Restarting...`);
            cluster.fork(); // Auto-restart
        });
    } else {
        require('./server'); // Each worker runs the HTTP server
    }
    ```

    **What workers share**: The listening port (via file descriptor passing).
    **What workers do NOT share**: Memory. Each worker has its own heap, its own V8 instance. Global variables in one worker are invisible to others. In-memory caches are duplicated per worker.

    **Implications for state**:

    * Session data in memory? Each worker has its own copy -- requests to different workers get different sessions. Use Redis.
    * In-memory cache? Duplicated N times (N = workers). Use Redis.
    * Rate limiting in memory? Each worker tracks independently -- actual rate is N times the limit. Use Redis.

    **Worker count heuristic**: `os.cpus().length` for CPU-bound workloads, `os.cpus().length * 1.5 - 2` for I/O-bound workloads (workers spend time waiting on I/O so more can be useful).

    **What interviewers are really testing**: Do you understand the memory isolation between workers? Can you reason about what breaks when you naively cluster a stateful app?

    **Red flag answer**: "Cluster makes your app use all cores and everything just works." Missing the stateful implications.

    **Follow-up**:

    * "What happens to in-progress requests when a worker crashes? Are they lost?"
    * "How does the master process distribute connections to workers? What's the difference between round-robin and OS scheduling?"
    * "Why might PM2's cluster mode be preferred over using the cluster module directly?"
  </Accordion>

  <Accordion title="32. PM2 Process Manager">
    **Answer**:
    PM2 is a production process manager for Node.js that handles concerns you'd otherwise implement manually.

    **Key features**:

    * **Cluster mode**: `pm2 start app.js -i max` -- forks workers for all CPU cores.
    * **Auto-restart on crash**: Worker dies, PM2 restarts it within milliseconds. Configurable restart limits.
    * **Zero-downtime reload**: `pm2 reload app` -- restarts workers one at a time (new worker starts before old one stops). No dropped requests.
    * **Log management**: Aggregates stdout/stderr from all workers. `pm2 logs`, log rotation.
    * **Monitoring**: `pm2 monit` shows real-time CPU, memory, event loop lag per process.
    * **Ecosystem file**: `ecosystem.config.js` for declarative configuration.

    ```javascript theme={null}
    // ecosystem.config.js
    module.exports = {
        apps: [{
            name: 'api',
            script: './src/server.js',
            instances: 'max',           // All cores
            exec_mode: 'cluster',
            max_memory_restart: '500M', // Auto-restart if memory exceeds 500MB
            env: {
                NODE_ENV: 'production',
                PORT: 3000
            },
            // Graceful shutdown
            kill_timeout: 5000,         // Wait 5s for connections to drain
            listen_timeout: 10000       // Wait 10s for ready signal
        }]
    };
    ```

    **PM2 vs Docker/Kubernetes**: In containerized environments, you typically run **one process per container** and let the orchestrator handle scaling, restarts, and health checks. PM2's cluster mode inside a container is usually redundant -- instead, scale by increasing container replicas. However, PM2 is still useful for non-containerized deployments (bare EC2, traditional VMs).

    **What interviewers are really testing**: Do you know when PM2 is appropriate vs when the orchestrator handles it? Can you explain zero-downtime reload?

    **Red flag answer**: "I use PM2 in production inside my Docker container in cluster mode." This is usually double-clustering and complicates debugging.

    **Follow-up**:

    * "How does PM2's zero-downtime reload work? What happens to in-flight requests during the reload?"
    * "In a Kubernetes deployment, would you still use PM2? Why or why not?"
    * "How would you configure PM2 to auto-restart a worker that exceeds 500MB of memory?"
  </Accordion>

  <Accordion title="33. Worker Threads">
    **Answer**:
    Worker Threads (`worker_threads` module) enable **true parallel JavaScript execution** within a single Node.js process. Unlike Cluster (separate processes), workers share the same process and can share memory.

    **Key differences from Cluster**:

    | Aspect        | Cluster                              | Worker Threads                                                      |
    | ------------- | ------------------------------------ | ------------------------------------------------------------------- |
    | Isolation     | Separate processes, separate memory  | Same process, can share memory                                      |
    | Overhead      | \~30MB per worker (full V8 instance) | \~5-10MB per worker (V8 isolate)                                    |
    | Communication | IPC (serialized messages)            | `postMessage` (structured clone) or `SharedArrayBuffer` (zero-copy) |
    | Use case      | Scaling HTTP servers                 | CPU-intensive tasks                                                 |
    | Crash impact  | Worker crash doesn't affect others   | Worker crash can affect the process                                 |

    **SharedArrayBuffer for zero-copy communication**:

    ```javascript theme={null}
    // Main thread
    const { Worker } = require('worker_threads');

    const sharedBuffer = new SharedArrayBuffer(4);
    const arr = new Int32Array(sharedBuffer);
    arr[0] = 42;

    const worker = new Worker('./worker.js', { workerData: { sharedBuffer } });
    // Worker can read/write arr[0] directly -- no serialization needed!
    ```

    **Gotcha -- atomics and race conditions**: If two threads write to the same `SharedArrayBuffer` location, you get race conditions. Use `Atomics.add()`, `Atomics.compareExchange()`, etc. for thread-safe operations.

    **When to use Worker Threads**:

    * Image resizing (sharp library already uses threads internally)
    * Parsing large JSON/CSV files
    * Cryptographic operations (bcrypt, key derivation)
    * Data compression
    * Number crunching, simulations

    **When NOT to use Worker Threads**:

    * I/O-bound work (that's what the event loop is for)
    * Simple request handling (overhead of creating/managing threads outweighs benefit)

    **What interviewers are really testing**: Can you distinguish between Worker Threads and Cluster? Do you know about SharedArrayBuffer and the race condition risks?

    **Red flag answer**: "Worker Threads and Cluster are the same thing." They have fundamentally different memory models and use cases.

    **Follow-up**:

    * "How does `postMessage` serialize data between threads? What types can't be transferred?"
    * "What is a `Transferable` object and when would you use `worker.postMessage(data, [transferList])`?"
    * "You need to process 10,000 images. Do you create 10,000 worker threads? What's your architecture?"
  </Accordion>

  <Accordion title="34. Memory Leaks Causes">
    **Answer**:
    Memory leaks in Node.js are insidious -- the process slowly consumes more memory over hours/days until it OOMs. Here are the most common causes ranked by how frequently they appear in production.

    **Top causes**:

    1. **Event Listener Accumulation** (most common):

    ```javascript theme={null}
    // BUG: Adding a listener on EVERY request -- never removing it
    app.get('/stream', (req, res) => {
        emitter.on('data', (d) => res.write(d));
        // After response ends, the listener is still attached!
        // After 10k requests: 10k listeners, 10k closures holding `res` objects
    });
    ```

    Fix: Remove listeners when done, or use `.once()`.

    2. **Closures Holding Large Objects**:

    ```javascript theme={null}
    function processData() {
        const hugeArray = new Array(1_000_000).fill('x'); // 10MB
        return function getFirstItem() {
            return hugeArray[0]; // Closure keeps hugeArray alive!
        };
    }
    // The returned function prevents hugeArray from being GC'd
    ```

    3. **Global Variables / Module-Level Caches Without TTL**:

    ```javascript theme={null}
    // Module-level cache grows unboundedly
    const cache = {};
    function getData(key) {
        if (!cache[key]) {
            cache[key] = fetchFromDB(key); // Never evicted!
        }
        return cache[key];
    }
    ```

    Fix: Use LRU cache with max size (`lru-cache` package) or Redis with TTL.

    4. **Unreferenced Timers**:

    ```javascript theme={null}
    // setInterval without clearInterval
    const intervalId = setInterval(fetchMetrics, 1000);
    // If this module is "hot-reloaded", the old interval keeps running
    ```

    5. **Forgotten Streams**: Readable streams that are created but never consumed or destroyed hold their internal buffer in memory.

    **Detection tools**:

    * `process.memoryUsage()` logged over time (look for monotonic heapUsed growth)
    * `--inspect` + Chrome DevTools heap snapshots (compare two snapshots, look at "Objects allocated between snapshots")
    * `clinic doctor` / `clinic heapprofiler` for automated analysis
    * `node --heapsnapshot-signal=SIGUSR2` -- take a heap snapshot on demand

    **What interviewers are really testing**: Can you name specific patterns (not just "memory leaks happen")? Do you know how to detect and diagnose them?

    **Red flag answer**: "Memory leaks don't really happen in JavaScript because of garbage collection." GC only collects unreachable objects -- all the patterns above keep objects reachable.

    **Follow-up**:

    * "Walk me through how you'd diagnose a memory leak in a production Node service that's slowly growing from 200MB to 2GB over 24 hours."
    * "What's the difference between a memory leak and high memory usage? How do you distinguish them?"
    * "How does the `WeakRef` / `WeakMap` help prevent certain kinds of memory leaks?"
  </Accordion>

  <Accordion title="35. N+1 Problem (GraphQL/ORM)">
    **Answer**:
    The N+1 problem is a **query explosion** pattern where fetching a list of N items triggers N additional queries for related data.

    **Example**:

    ```javascript theme={null}
    // Query 1: Fetch 50 users
    const users = await User.findAll();

    // N queries: For each user, fetch their posts
    for (const user of users) {
        user.posts = await Post.findAll({ where: { userId: user.id } });
        // This executes 50 separate SQL queries!
    }
    // Total: 1 + 50 = 51 queries. Should be 2.
    ```

    **Solutions**:

    1. **Eager Loading (ORM-level)**: Load related data in a JOIN or second query.

    ```javascript theme={null}
    // Sequelize: 2 queries instead of 51
    const users = await User.findAll({ include: [Post] });

    // Prisma
    const users = await prisma.user.findMany({ include: { posts: true } });
    ```

    2. **DataLoader (GraphQL)**: Batches individual lookups within a single event loop tick into one query.

    ```javascript theme={null}
    const DataLoader = require('dataloader');

    const postLoader = new DataLoader(async (userIds) => {
        // ONE query: SELECT * FROM posts WHERE userId IN (1,2,3,...50)
        const posts = await Post.findAll({ where: { userId: userIds } });
        // Return in the same order as input userIds
        return userIds.map(id => posts.filter(p => p.userId === id));
    });

    // In resolver -- each call is batched automatically
    const resolvers = {
        User: {
            posts: (user) => postLoader.load(user.id) // Batched!
        }
    };
    ```

    3. **Database Views / Denormalization**: For read-heavy paths, pre-compute the joined data.

    **Why this matters at scale**: 51 queries at 2ms each = 102ms. Seems fine. But with 100 concurrent requests = 5,100 queries hitting your database simultaneously. With proper batching: 200 queries. That's a 25x reduction in database load.

    **What interviewers are really testing**: Do you know DataLoader specifically? Can you reason about database load multiplication?

    **Red flag answer**: "I just add `.include()` to everything." Eager loading everything causes over-fetching and can be worse than N+1 for deeply nested relationships.

    **Follow-up**:

    * "How does DataLoader batch requests? What's the mechanism that collects individual `.load()` calls into a single batch?"
    * "DataLoader caches results per-request. What happens if you share a DataLoader across requests?"
    * "When is eager loading worse than N+1? Give a scenario."
  </Accordion>

  <Accordion title="36. Database Connection Pooling">
    **Answer**:
    Every database query needs a TCP connection. Opening a new TCP connection involves a 3-way handshake (\~1ms on LAN, 50-100ms cross-region), plus TLS negotiation (\~5-30ms), plus database authentication. Connection pooling amortizes this cost.

    **How pools work**:

    1. At startup, the pool opens `min` connections.
    2. When your code needs a connection, it **borrows** one from the pool.
    3. After the query completes, the connection is **returned** to the pool (not closed).
    4. If all connections are in use, new requests **wait in a queue** until one is returned.
    5. If the queue exceeds a timeout, the request fails with a connection timeout error.

    ```javascript theme={null}
    // PostgreSQL pool configuration
    const { Pool } = require('pg');
    const pool = new Pool({
        host: process.env.DB_HOST,
        max: 20,                        // Max connections in pool
        min: 5,                         // Min idle connections
        idleTimeoutMillis: 30000,       // Close idle connections after 30s
        connectionTimeoutMillis: 5000,  // Fail if can't get connection in 5s
        maxUses: 7500                   // Close connection after 7500 queries (prevent stale)
    });
    ```

    **Sizing the pool** (critical production decision):

    * **Too small**: Requests queue up waiting for connections. Latency spikes under load.
    * **Too large**: Each connection uses \~5-10MB on the database server. 100 connections x 10 workers = 1000 connections on the DB, overwhelming it.
    * **Rule of thumb**: Start with `max = (2 * CPU cores) + effective_spindle_count` (per PostgreSQL docs), typically 10-20 per Node process.
    * **With Cluster mode**: If you have 8 workers, each with a pool of 20 = 160 total connections. Make sure your database can handle this.

    **Connection pooler tools**: For large deployments, use a connection pooler like **PgBouncer** (PostgreSQL) or **ProxySQL** (MySQL) between your app and the database. They multiplex hundreds of application connections onto a small number of actual database connections.

    **What interviewers are really testing**: Can you size a pool correctly? Do you know about the connection multiplication problem with Cluster/multiple instances?

    **Red flag answer**: "I set `max: 100` to be safe." More connections is not safer -- it can kill your database.

    **Follow-up**:

    * "You have 10 Node instances, each with a pool of 20 connections. Your database allows 100 connections max. What happens?"
    * "What is PgBouncer and when would you use it?"
    * "How do you monitor pool health (idle connections, queue depth, wait time)?"
  </Accordion>

  <Accordion title="37. Caching Strategies">
    **Answer**:
    Caching is the most impactful performance optimization you can make, but choosing the wrong strategy creates consistency bugs that are hard to debug.

    **Two categories**:

    1. **In-Process (Node memory)**:
       * `Map`, `lru-cache`, `node-cache`.
       * **Pros**: Fastest possible (\~nanoseconds), no network hop.
       * **Cons**: Lost on restart, duplicated in every Cluster worker, no sharing between instances, bounded by V8 heap size.
       * **Use for**: Compiled regex, parsed config, tiny lookup tables that change rarely.

    2. **Distributed (Redis/Memcached)**:
       * Shared across all instances, survives restarts (Redis with AOF/RDB persistence).
       * **Pros**: Shared, persistent, rich data structures (sorted sets for leaderboards, pub/sub, Lua scripting).
       * **Cons**: Network hop (\~0.5-2ms per request), serialization overhead.
       * **Use for**: API response caching, session storage, rate limit counters, feature flags.

    **Cache invalidation strategies** (the hard part):

    * **TTL (Time-To-Live)**: Set expiry. Simple but allows stale data during TTL window.
    * **Write-Through**: Write to cache AND database simultaneously. Consistent but slower writes.
    * **Write-Behind (Write-Back)**: Write to cache, asynchronously flush to database. Fast but data loss risk.
    * **Cache-Aside (Lazy Loading)**: Check cache first, miss goes to DB, result written to cache. Most common pattern.

    ```javascript theme={null}
    // Cache-aside pattern with Redis
    async function getUser(id) {
        const cacheKey = `user:${id}`;
        
        // 1. Check cache
        const cached = await redis.get(cacheKey);
        if (cached) return JSON.parse(cached);
        
        // 2. Cache miss -- fetch from DB
        const user = await db.users.findById(id);
        if (!user) return null;
        
        // 3. Populate cache with TTL
        await redis.set(cacheKey, JSON.stringify(user), 'EX', 3600);
        return user;
    }

    // On update, invalidate cache
    async function updateUser(id, data) {
        await db.users.update(id, data);
        await redis.del(`user:${id}`); // Invalidate
    }
    ```

    **What interviewers are really testing**: Do you know multiple caching strategies and their trade-offs? Can you reason about cache invalidation?

    **Red flag answer**: "I cache everything in a global object" -- no eviction, no TTL, duplicated across workers, grows unbounded.

    **Follow-up**:

    * "What is a cache stampede (thundering herd) and how do you prevent it?"
    * "How do you decide what TTL to set? What's the trade-off between a 10-second TTL and a 1-hour TTL?"
    * "Your cache and database are out of sync. How do you detect and fix this?"
  </Accordion>

  <Accordion title="38. Profiling Node App">
    **Answer**:
    Profiling is how you find the actual bottleneck instead of guessing. Node has excellent built-in and ecosystem profiling tools.

    **CPU Profiling** (finding slow functions):

    ```bash theme={null}
    # Built-in V8 profiler
    node --prof app.js                           # Generates isolate-*.log
    node --prof-process isolate-*.log > processed.txt  # Human-readable

    # Chrome DevTools (interactive)
    node --inspect app.js
    # Open chrome://inspect, go to "Profiler" tab, record a CPU profile
    ```

    **Flame Graphs** (visual CPU profiling):

    ```bash theme={null}
    # clinic.js (the best all-in-one tool)
    npx clinic flame -- node app.js
    # Generates an interactive flame graph HTML file

    # 0x (standalone flame graph)
    npx 0x app.js
    ```

    Reading a flame graph: Width = time spent. Look for wide bars (functions that take the most time). Tall stacks = deep call chains. **Plateau patterns** = the same function called many times (potential optimization target).

    **Memory Profiling**:

    ```bash theme={null}
    # Heap snapshot
    node --inspect app.js
    # Chrome DevTools > Memory > Take heap snapshot
    # Take two snapshots, compare "Objects allocated between Snapshot 1 and Snapshot 2"

    # Heap timeline
    node --inspect app.js
    # Chrome DevTools > Memory > Allocation timeline
    # Shows which allocations survive GC (potential leaks)
    ```

    **Event Loop Profiling** (finding blockage):

    ```bash theme={null}
    npx clinic doctor -- node app.js
    # Automatically detects: event loop lag, I/O issues, GC problems
    ```

    **Production monitoring metrics** to track:

    * Event loop lag (P50, P99)
    * Heap used / heap total
    * GC pause duration and frequency
    * Active handles and requests (`process._getActiveHandles().length`)

    **What interviewers are really testing**: Have you actually profiled a Node app? Can you interpret a flame graph? Do you know the difference between CPU and memory profiling?

    **Red flag answer**: "I add `console.time()` everywhere." This is manual and imprecise -- real profiling tools show the full picture.

    **Follow-up**:

    * "You have a Node API where P99 latency jumped from 50ms to 500ms. Walk me through your profiling approach."
    * "What's the difference between a CPU profile and a flame graph? When would you use each?"
    * "How do you profile a Node app in production without significant performance overhead?"
  </Accordion>

  <Accordion title="39. Event Loop Blocking Pitfalls">
    **Answer**:
    Even without explicit `fs.readFileSync` calls, several common operations can block the event loop and cause latency spikes.

    **Common blockers people miss**:

    1. **`JSON.parse` / `JSON.stringify` on large objects**: These are synchronous and CPU-bound. A 50MB JSON string can block the loop for 200-500ms.

    ```javascript theme={null}
    // BAD: Parsing a huge API response on the main thread
    const data = JSON.parse(hugeJsonString); // 500ms block!

    // BETTER: Use a streaming JSON parser or Worker Thread
    const { Worker } = require('worker_threads');
    // Offload to worker for objects > 1MB
    ```

    2. **Regex Denial of Service (ReDoS)**: Certain regex patterns with backtracking can take exponential time on crafted inputs.

    ```javascript theme={null}
    // DANGEROUS: Catastrophic backtracking
    const evil = /^(a+)+$/;
    evil.test('aaaaaaaaaaaaaaaaaaaaaaaaaaa!'); // Hangs for seconds/minutes!

    // SAFE: Avoid nested quantifiers, use linear-time regex engines
    ```

    3. **Sorting large arrays**: `Array.sort()` on 1M+ elements blocks for 100ms+.

    4. **Crypto operations**: `crypto.pbkdf2Sync`, `crypto.scryptSync` -- always use async versions.

    5. **Template rendering**: Rendering complex templates (EJS, Handlebars) with large datasets.

    6. **`require()` in hot paths**: Module loading reads files synchronously and compiles them.

    **How to detect event loop blocking**:

    ```javascript theme={null}
    // Simple lag monitor
    const CHECK_INTERVAL = 100; // ms
    let lastCheck = process.hrtime.bigint();

    setInterval(() => {
        const now = process.hrtime.bigint();
        const elapsed = Number(now - lastCheck) / 1_000_000; // Convert to ms
        const lag = elapsed - CHECK_INTERVAL;
        if (lag > 20) {
            console.warn(`Event loop lag: ${lag.toFixed(1)}ms`);
        }
        lastCheck = now;
    }, CHECK_INTERVAL).unref();
    ```

    **Libraries**: `blocked-at` (detects which call site is blocking), `event-loop-lag` (metrics).

    **What interviewers are really testing**: Can you identify non-obvious blockers? Do you have a strategy for detecting them?

    **Red flag answer**: "I avoid blocking by using async/await everywhere." `async/await` only helps with I/O -- `await JSON.parse(huge)` still blocks because `JSON.parse` is synchronous.

    **Follow-up**:

    * "How would you safely parse a 100MB JSON file without blocking the event loop?"
    * "What is ReDoS and how do you protect against it? Can you write a safe regex for email validation?"
    * "You notice periodic 200ms latency spikes every few minutes. How would you determine if the event loop is being blocked and by what?"
  </Accordion>

  <Accordion title="40. Microservices Communication">
    **Answer**:
    How services communicate is one of the most consequential architectural decisions. Each pattern has specific strengths and weaknesses.

    **Synchronous (request-response)**:

    * **HTTP/REST**: Universal, human-readable (JSON), easy to debug with curl. Overhead: TCP connection + TLS + HTTP framing + JSON serialization. Latency: \~5-50ms per hop. Use for: CRUD APIs, simple service-to-service calls.
    * **gRPC**: Protocol Buffers (binary serialization, 5-10x smaller than JSON), HTTP/2 (multiplexing, streaming), strict contract via `.proto` files. Latency: \~1-5ms per hop. Use for: high-throughput internal communication, streaming data, polyglot environments.

    **Asynchronous (event-driven)**:

    * **Message Queues (RabbitMQ)**: Point-to-point or fan-out. Guaranteed delivery with acknowledgements. Use for: task queues, work distribution, decoupling services that don't need immediate responses.
    * **Event Streaming (Kafka)**: Distributed log, ordered within partitions, replay capability, high throughput (millions of messages/sec). Use for: event sourcing, real-time analytics pipelines, audit logs, inter-service events at scale.
    * **Redis Pub/Sub**: Simple, fast, fire-and-forget (no persistence/replay). Use for: real-time notifications, cache invalidation broadcasts.

    **Choosing between them**:

    | Factor      | REST             | gRPC         | Message Queue | Kafka      |
    | ----------- | ---------------- | ------------ | ------------- | ---------- |
    | Latency     | Medium           | Low          | Variable      | Low-Medium |
    | Coupling    | Tight            | Tight        | Loose         | Loose      |
    | Reliability | Retry needed     | Retry needed | Built-in      | Built-in   |
    | Debugging   | Easy (curl)      | Harder       | Harder        | Harder     |
    | Streaming   | No (workarounds) | Native       | No            | Native     |

    **Pattern: Saga for distributed transactions**:
    When an order involves inventory, payment, and shipping services, you can't use a single database transaction. Use the Saga pattern -- each service performs its step and publishes an event. If any step fails, compensating transactions undo previous steps.

    **What interviewers are really testing**: Can you pick the right communication pattern for a given scenario? Do you understand sync vs async trade-offs?

    **Red flag answer**: "REST is always the best because it's simple." Simplicity matters, but using REST for everything leads to tight coupling and cascading failures.

    **Follow-up**:

    * "Service A calls Service B, which calls Service C. Service C is down. What happens with synchronous REST vs async messaging?"
    * "When would you choose Kafka over RabbitMQ? What's the fundamental architectural difference?"
    * "How do you handle distributed transactions across microservices that each have their own database?"
  </Accordion>
</AccordionGroup>

## 5. Security & Testing

<AccordionGroup>
  <Accordion title="41. SQL Injection & NoSQL Injection">
    **Answer**:
    Injection attacks exploit the boundary between code and data -- when user input is treated as executable query logic.

    **SQL Injection**:

    ```javascript theme={null}
    // VULNERABLE: String concatenation
    const query = `SELECT * FROM users WHERE email = '${req.body.email}'`;
    // Attacker sends: email = "'; DROP TABLE users; --"
    // Executed: SELECT * FROM users WHERE email = ''; DROP TABLE users; --'

    // SAFE: Parameterized queries (prepared statements)
    const result = await pool.query(
        'SELECT * FROM users WHERE email = $1',
        [req.body.email]  // Value is never interpreted as SQL
    );
    ```

    **NoSQL Injection** (MongoDB):

    ```javascript theme={null}
    // VULNERABLE: Passing raw request body to query
    const user = await User.findOne({
        email: req.body.email,
        password: req.body.password
    });
    // Attacker sends: { "email": "admin@site.com", "password": { "$ne": null } }
    // MongoDB interprets $ne as an operator: password != null (always true!)

    // SAFE: Explicitly cast to string or use a validation library
    const user = await User.findOne({
        email: String(req.body.email),
        password: String(req.body.password) // Forces string, $ne is treated as literal
    });

    // BETTER: Use mongo-sanitize or express-mongo-sanitize
    const sanitize = require('express-mongo-sanitize');
    app.use(sanitize()); // Strips $ and . from req.body, req.query, req.params
    ```

    **Defense in depth**:

    1. **Parameterized queries** (primary defense -- prevents the attack entirely)
    2. **Input validation** (reject unexpected types/formats before they reach the query)
    3. **Least privilege** (DB user should have minimal permissions -- no `DROP TABLE`)
    4. **ORM/ODM** (Sequelize, Prisma, Mongoose -- they parameterize by default, but raw queries still need care)
    5. **WAF rules** (Web Application Firewall as an additional layer)

    **What interviewers are really testing**: Do you know about NoSQL injection (not just SQL)? Do you rely on parameterized queries or just "sanitization"?

    **Red flag answer**: "I use an ORM so injection is impossible." ORMs help, but raw queries within an ORM are still vulnerable. Also, ORM query builder methods can still be exploited if you pass unsanitized objects.

    **Follow-up**:

    * "Can injection happen with an ORM like Prisma or Sequelize? How?"
    * "What's the difference between parameterized queries and escaping/sanitizing input? Which is more reliable?"
    * "How would you test for injection vulnerabilities in an existing codebase?"
  </Accordion>

  <Accordion title="42. XSS (Cross Site Scripting)">
    **Answer**:
    XSS occurs when an attacker injects malicious JavaScript that executes in another user's browser. It's the most common web vulnerability.

    **Three types**:

    1. **Stored XSS**: Attacker saves malicious script in the database (e.g., a comment containing a script tag that steals cookies via `document.cookie`). Every user who views the comment executes the script.
    2. **Reflected XSS**: Malicious script is in the URL and reflected in the response: `https://site.com/search?q=<script>alert(1)</script>`.
    3. **DOM-based XSS**: Client-side JS inserts untrusted data into the DOM without sanitization: `document.innerHTML = userInput`.

    **Backend defenses** (Node.js perspective):

    1. **Output encoding/escaping**: Use a templating engine that auto-escapes (EJS with `<%= %>`, Handlebars, React's JSX). Never use `<%- %>` (raw output) with user data.
    2. **Content Security Policy (CSP) header**: Restricts which scripts can execute.

    ```javascript theme={null}
    // Helmet.js sets CSP
    app.use(helmet.contentSecurityPolicy({
        directives: {
            defaultSrc: ["'self'"],
            scriptSrc: ["'self'", "'nonce-abc123'"], // Only scripts with this nonce run
            styleSrc: ["'self'", "'unsafe-inline'"],
            imgSrc: ["'self'", "data:", "https:"],
        }
    }));
    ```

    3. **HttpOnly cookies**: Prevents JavaScript from accessing cookies (`document.cookie` returns nothing).
    4. **Input sanitization** (secondary defense): Libraries like `DOMPurify` (client-side) or `sanitize-html` (server-side) for when you must accept HTML input (rich text editors).

    **What interviewers are really testing**: Do you know all three types? Do you rely on CSP, not just escaping? Can you articulate why HttpOnly cookies matter?

    **Red flag answer**: "I just escape all user input." Escaping prevents stored/reflected XSS but doesn't protect against DOM-based XSS. Also, CSP is the strongest defense layer.

    **Follow-up**:

    * "What is Content Security Policy and how does it prevent XSS even if your escaping has a bug?"
    * "How does React prevent XSS by default? Can you still have XSS in a React app?"
    * "What's the difference between encoding/escaping and sanitizing? When do you use each?"
  </Accordion>

  <Accordion title="43. CSRF (Cross Site Request Forgery)">
    **Answer**:
    CSRF tricks a user's browser into making an unwanted request to a site where the user is already authenticated. The browser automatically includes the user's cookies (session, JWT).

    **Attack scenario**:

    1. User is logged into `bank.com` (session cookie is set).
    2. User visits `evil-site.com` which has: `<img src="https://bank.com/transfer?to=attacker&amount=10000">`.
    3. Browser sends the request to `bank.com` WITH the user's session cookie.
    4. `bank.com` processes the transfer because the cookie is valid.

    **Defenses**:

    1. **SameSite Cookie Attribute** (strongest, simplest):

    ```javascript theme={null}
    res.cookie('session', sessionId, {
        httpOnly: true,
        secure: true,
        sameSite: 'Strict'  // Cookie only sent on same-site requests
        // 'Lax' allows GET navigations (clicking a link) but blocks POST from other sites
    });
    ```

    2. **CSRF Tokens** (traditional approach):

    ```javascript theme={null}
    const csrf = require('csurf');
    app.use(csrf({ cookie: true }));

    // In form: include hidden input with _csrf value
    // Token is validated on submission -- attacker can't know the token
    ```

    3. **Double Submit Cookie**: Send CSRF token in both a cookie and a request header. Server checks they match. Works because `evil-site.com` can trigger sending cookies but cannot read them or set custom headers on cross-origin requests.

    4. **Origin/Referer Header Validation**: Check that the `Origin` or `Referer` header matches your domain. Less reliable (can be stripped by proxies).

    **Modern reality**: With `SameSite=Lax` as the default in modern browsers (Chrome 80+), CSRF attacks are largely mitigated for POST requests. But you should still use CSRF tokens for defense in depth, especially for older browser support.

    **What interviewers are really testing**: Do you understand the attack mechanism? Do you know `SameSite` cookies are the modern primary defense?

    **Red flag answer**: "CSRF is the same as XSS." They're completely different attacks -- XSS executes code in the victim's browser, CSRF tricks the browser into making authenticated requests.

    **Follow-up**:

    * "With `SameSite=Strict`, what breaks? Why might you choose `Lax` instead?"
    * "How does CSRF differ from XSS? Can you combine them for a more powerful attack?"
    * "Is CSRF possible with JWT authentication stored in localStorage instead of cookies?"
  </Accordion>

  <Accordion title="44. Helmet.js">
    **Answer**:
    Helmet is a collection of **15+ middleware functions** that set HTTP security headers. It's the fastest security win for any Express app -- a single line of code addresses multiple attack vectors.

    ```javascript theme={null}
    const helmet = require('helmet');
    app.use(helmet()); // Enables all default protections
    ```

    **Key headers Helmet sets**:

    | Header                             | Purpose                                      | Default                            |
    | ---------------------------------- | -------------------------------------------- | ---------------------------------- |
    | `Content-Security-Policy`          | Prevents XSS by restricting resource sources | Strict default                     |
    | `Strict-Transport-Security` (HSTS) | Forces HTTPS for future requests             | `max-age=15552000`                 |
    | `X-Content-Type-Options`           | Prevents MIME type sniffing                  | `nosniff`                          |
    | `X-Frame-Options`                  | Prevents clickjacking via iframes            | `SAMEORIGIN`                       |
    | `X-XSS-Protection`                 | Legacy XSS filter (modern browsers use CSP)  | `0` (disabled -- can cause issues) |
    | `Referrer-Policy`                  | Controls what's sent in the Referer header   | `no-referrer`                      |
    | `X-DNS-Prefetch-Control`           | Disables DNS prefetching (privacy)           | `off`                              |

    **Customization** (you should customize CSP for your app):

    ```javascript theme={null}
    app.use(helmet({
        contentSecurityPolicy: {
            directives: {
                defaultSrc: ["'self'"],
                scriptSrc: ["'self'", "cdn.example.com"],
                styleSrc: ["'self'", "'unsafe-inline'"],   // Often needed for CSS-in-JS
                imgSrc: ["'self'", "data:", "*.amazonaws.com"],
                connectSrc: ["'self'", "api.example.com"],
            }
        },
        crossOriginEmbedderPolicy: false, // Disable if you embed third-party resources
    }));
    ```

    **What interviewers are really testing**: Can you name specific security headers and what they protect against? Do you customize Helmet or just use defaults?

    **Red flag answer**: "I add `app.use(helmet())` and security is handled." Helmet is a starting point, not complete security.

    **Follow-up**:

    * "What is HSTS and what problem does it solve? What happens if you set HSTS and then lose your TLS certificate?"
    * "What is clickjacking and how does `X-Frame-Options` prevent it?"
    * "If you're using a CDN for your JavaScript, how do you configure CSP to allow it while still preventing XSS?"
  </Accordion>

  <Accordion title="45. Rate Limiting">
    **Answer**:
    Rate limiting controls how many requests a client can make in a time window, protecting against DDoS, brute-force attacks, and API abuse.

    **Strategies**:

    1. **Fixed Window**: Count requests in fixed time windows (e.g., 100 requests per 15 minutes). Simple but has burst problems at window boundaries -- a client can send 100 requests at 14:59 and 100 at 15:00 = 200 in 2 minutes.

    2. **Sliding Window**: Calculates rate based on a sliding window. Smoother than fixed window. `express-rate-limit` uses this.

    3. **Token Bucket**: Tokens accumulate at a fixed rate (e.g., 10/sec). Each request consumes a token. If no tokens, request is rejected. Allows bursts up to the bucket size. Used by AWS, Stripe, most production APIs.

    4. **Leaky Bucket**: Requests enter a queue (bucket) and are processed at a fixed rate. Excess requests overflow (rejected). Smooths traffic to a constant rate.

    **Implementation layers** (defense in depth):

    ```
    [CDN/WAF Rate Limit] --> [API Gateway/Nginx] --> [Application Rate Limit] --> [Per-Route Limits]
    ```

    **Application-level with Redis** (for distributed systems):

    ```javascript theme={null}
    const rateLimit = require('express-rate-limit');
    const RedisStore = require('rate-limit-redis');
    const Redis = require('ioredis');

    const limiter = rateLimit({
        store: new RedisStore({
            sendCommand: (...args) => redisClient.call(...args),
        }),
        windowMs: 15 * 60 * 1000,  // 15 minutes
        max: 100,                    // 100 requests per window
        standardHeaders: true,       // Return rate limit info in RateLimit-* headers
        legacyHeaders: false,
        keyGenerator: (req) => req.ip, // Or req.user.id for authenticated rate limiting
        handler: (req, res) => {
            res.status(429).json({
                error: 'Too Many Requests',
                retryAfter: Math.ceil(req.rateLimit.resetTime / 1000)
            });
        }
    });

    app.use('/api/', limiter);
    ```

    **Why Redis is essential**: With `express-rate-limit` using memory store and Cluster mode with 8 workers, the actual rate limit is 8x what you configured (each worker tracks independently). Redis provides a single shared counter.

    **What interviewers are really testing**: Do you know the difference between rate limiting strategies? Do you understand the distributed system implications?

    **Red flag answer**: "I use `express-rate-limit` with default memory store in production." This breaks with multiple processes/servers.

    **Follow-up**:

    * "What's the difference between Token Bucket and Leaky Bucket? When would you choose one over the other?"
    * "How do you rate limit by API key for authenticated users vs by IP for anonymous users? What about users behind a NAT?"
    * "You rate limit at 100 req/min, but an attacker uses 1000 different IPs. How do you handle this?"
  </Accordion>

  <Accordion title="46. Unit Testing (Jest/Mocha)">
    **Answer**:
    Unit testing in Node.js means testing functions/modules **in isolation** by mocking their dependencies.

    **Testing pyramid for Node backends**:

    1. **Unit tests** (70%): Individual functions, service methods, utility functions. Fast, no I/O.
    2. **Integration tests** (20%): Multiple modules together, often hitting a real test database.
    3. **End-to-end tests** (10%): Full HTTP request lifecycle with `supertest`.

    **Jest vs Mocha**:

    * **Jest**: Zero-config, built-in mocking, snapshot testing, parallel test runner, code coverage. The default choice for most Node projects.
    * **Mocha**: More flexible, needs separate assertion (Chai) and mocking (Sinon) libraries. Preferred in some enterprise environments.

    **Testing patterns for Node services**:

    ```javascript theme={null}
    // Service with a dependency
    class UserService {
        constructor(db, emailService) {
            this.db = db;
            this.emailService = emailService;
        }
        
        async createUser(data) {
            const user = await this.db.users.create(data);
            await this.emailService.sendWelcome(user.email);
            return user;
        }
    }

    // Test with mocks
    describe('UserService', () => {
        let service, mockDb, mockEmail;
        
        beforeEach(() => {
            mockDb = { users: { create: jest.fn() } };
            mockEmail = { sendWelcome: jest.fn() };
            service = new UserService(mockDb, mockEmail);
        });
        
        it('creates user and sends welcome email', async () => {
            const userData = { name: 'Alice', email: 'alice@test.com' };
            mockDb.users.create.mockResolvedValue({ id: 1, ...userData });
            
            const result = await service.createUser(userData);
            
            expect(mockDb.users.create).toHaveBeenCalledWith(userData);
            expect(mockEmail.sendWelcome).toHaveBeenCalledWith('alice@test.com');
            expect(result.id).toBe(1);
        });
        
        it('does not send email if user creation fails', async () => {
            mockDb.users.create.mockRejectedValue(new Error('Duplicate email'));
            
            await expect(service.createUser({})).rejects.toThrow('Duplicate email');
            expect(mockEmail.sendWelcome).not.toHaveBeenCalled();
        });
    });
    ```

    **Common mistakes**:

    * Testing implementation details instead of behavior (checking which internal methods were called vs verifying the output).
    * Not testing error paths (only happy path coverage).
    * Mocking too much -- if everything is mocked, you're testing your mocks, not your code.

    **What interviewers are really testing**: Do you write tests that test *behavior*, not implementation? Do you mock at the right boundaries?

    **Red flag answer**: "I test by running the app manually and checking the output." No automated testing means no confidence in changes.

    **Follow-up**:

    * "What's the difference between a mock, a stub, and a spy? When do you use each?"
    * "How do you test an Express route handler? What is `supertest`?"
    * "When is mocking harmful? Give an example where too much mocking hid a real bug."
  </Accordion>

  <Accordion title="47. Dependency Injection">
    **Answer**:
    Dependency Injection (DI) means passing dependencies into a function/class **from the outside** rather than creating them internally. This is the single most important pattern for testable Node.js code.

    **Without DI (hard to test)**:

    ```javascript theme={null}
    const db = require('./database');        // Tight coupling
    const mailer = require('./mailer');

    async function createUser(data) {
        const user = await db.users.create(data);  // How do you mock db in tests?
        await mailer.send(user.email, 'Welcome');
        return user;
    }
    ```

    **With DI (testable)**:

    ```javascript theme={null}
    function createUserService(db, mailer) {
        return {
            async createUser(data) {
                const user = await db.users.create(data);
                await mailer.send(user.email, 'Welcome');
                return user;
            }
        };
    }

    // Production
    const service = createUserService(realDb, realMailer);

    // Test
    const service = createUserService(mockDb, mockMailer);
    ```

    **DI approaches in Node**:

    1. **Constructor injection** (classes): `new Service(db, cache, logger)`. Most explicit.
    2. **Factory functions**: `createService({ db, cache })`. Flexible, functional style.
    3. **DI containers** (InversifyJS, tsyringe, awilix): Automatic resolution of dependencies. More magic but handles complex graphs.

    ```javascript theme={null}
    // Using awilix (popular Node.js DI container)
    const awilix = require('awilix');
    const container = awilix.createContainer();

    container.register({
        db: awilix.asFunction(createDbPool).singleton(),
        userService: awilix.asClass(UserService).scoped(),
        orderService: awilix.asClass(OrderService).scoped(),
    });

    // Resolves all dependencies automatically
    const userService = container.resolve('userService');
    ```

    **What interviewers are really testing**: Do you design for testability? Can you explain DI without it being "just passing arguments"?

    **Red flag answer**: "I use `jest.mock()` to mock the `require` calls." This works but is a testing workaround for bad design -- proper DI is a better architectural solution.

    **Follow-up**:

    * "What are the trade-offs of using a DI container vs manual dependency injection in Node?"
    * "How does DI relate to the SOLID principles, specifically the Dependency Inversion Principle?"
    * "In Express, how would you inject dependencies into route handlers without a DI container?"
  </Accordion>

  <Accordion title="48. TDD (Test Driven Development)">
    **Answer**:
    TDD is a development methodology: write the test first, watch it fail, write the minimal code to pass, then refactor. The cycle is **Red -> Green -> Refactor**.

    **The cycle**:

    1. **Red**: Write a test for the next piece of functionality. Run it -- it fails (because the code doesn't exist yet).
    2. **Green**: Write the **minimum** code to make the test pass. No more, no less.
    3. **Refactor**: Clean up the code (remove duplication, improve naming) while keeping tests green.

    **Example -- building a URL shortener**:

    ```javascript theme={null}
    // Step 1 (RED): Write the test first
    describe('shortenUrl', () => {
        it('returns a 6-character code', () => {
            const result = shortenUrl('https://example.com/very/long/path');
            expect(result.code).toHaveLength(6);
        });
        
        it('returns the same code for the same URL', () => {
            const result1 = shortenUrl('https://example.com');
            const result2 = shortenUrl('https://example.com');
            expect(result1.code).toBe(result2.code);
        });
        
        it('rejects invalid URLs', () => {
            expect(() => shortenUrl('not-a-url')).toThrow('Invalid URL');
        });
    });

    // Step 2 (GREEN): Write minimal code
    function shortenUrl(url) {
        if (!url.startsWith('http')) throw new Error('Invalid URL');
        const hash = crypto.createHash('md5').update(url).digest('hex');
        return { code: hash.substring(0, 6) };
    }

    // Step 3 (REFACTOR): Improve the URL validation, add URL class parsing, etc.
    ```

    **When TDD works well**: Pure functions, business logic, utility functions, data transformations, algorithms.

    **When TDD is harder**: UI code, integration with external services, rapid prototyping where requirements are unclear.

    **What interviewers are really testing**: Do you practice TDD or just know the theory? Can you articulate when it helps and when it hurts?

    **Red flag answer**: "I always write tests after the code is done" (not TDD, just testing) or "TDD is too slow for real-world development" (shows rigidity -- TDD is a tool, not a religion).

    **Follow-up**:

    * "Walk me through how you'd TDD an endpoint that creates a user, hashes their password, and sends a welcome email."
    * "What's the difference between TDD and BDD (Behavior Driven Development)?"
    * "When would you choose NOT to use TDD? What are its costs?"
  </Accordion>

  <Accordion title="49. Environment Variables">
    **Answer**:
    Environment variables separate configuration from code -- the same application binary behaves differently based on the environment it runs in.

    **The `dotenv` pattern**:

    ```javascript theme={null}
    // .env file (NEVER commit to git!)
    DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
    JWT_SECRET=supersecretkey123
    REDIS_URL=redis://localhost:6379
    NODE_ENV=development

    // app.js
    require('dotenv').config(); // Loads .env into process.env
    const dbUrl = process.env.DATABASE_URL;
    ```

    **Best practices**:

    1. **Never commit secrets to git**: Add `.env` to `.gitignore`. Use `.env.example` (with placeholder values) as a template.
    2. **Validate env vars at startup**: Fail fast if required variables are missing.

    ```javascript theme={null}
    const required = ['DATABASE_URL', 'JWT_SECRET', 'REDIS_URL'];
    for (const key of required) {
        if (!process.env[key]) {
            console.error(`Missing required env var: ${key}`);
            process.exit(1);
        }
    }
    ```

    3. **Use typed/validated config** (not raw `process.env` everywhere):

    ```javascript theme={null}
    // config.js -- single source of truth
    const config = {
        port: parseInt(process.env.PORT, 10) || 3000,
        db: {
            url: process.env.DATABASE_URL,
            poolSize: parseInt(process.env.DB_POOL_SIZE, 10) || 10,
        },
        jwt: {
            secret: process.env.JWT_SECRET,
            expiresIn: process.env.JWT_EXPIRES_IN || '1h',
        },
        isProduction: process.env.NODE_ENV === 'production',
    };
    module.exports = config;
    ```

    4. **In production, use secrets managers** (not `.env` files): AWS Secrets Manager, HashiCorp Vault, Kubernetes Secrets. `.env` files are fine for development.

    **What interviewers are really testing**: Do you validate env vars at startup? Do you use a secrets manager in production or just `.env` files?

    **Red flag answer**: "I hardcode the database password in my config file and just change it on each server." This is a security incident waiting to happen.

    **Follow-up**:

    * "What's the difference between `.env`, `.env.local`, `.env.production`? How do they layer?"
    * "How do you manage secrets in a Kubernetes deployment? What are Kubernetes Secrets and their limitations?"
    * "What happens if `process.env.PORT` is the string `'undefined'`? How does your validation catch this?"
  </Accordion>

  <Accordion title="50. Logging Best Practices">
    **Answer**:
    Production logging in Node requires structured, leveled, performant logging -- not `console.log`.

    **Why `console.log` is bad for production**:

    * **Synchronous when writing to a TTY** (terminal) -- blocks the event loop.
    * **No log levels** -- can't filter debug messages in production.
    * **No structure** -- `console.log('User created', userId)` is not machine-parseable.
    * **No context** -- which request triggered this log? Which user?

    **Production logging stack**:

    ```javascript theme={null}
    // Pino (fastest Node.js logger -- 5-10x faster than Winston)
    const pino = require('pino');

    const logger = pino({
        level: process.env.LOG_LEVEL || 'info',
        // In production: JSON to stdout (let the log aggregator handle formatting)
        // In development: pretty-print for readability
        ...(process.env.NODE_ENV !== 'production' && {
            transport: { target: 'pino-pretty' }
        })
    });

    // Structured logging with context
    logger.info({ userId: 123, orderId: 'abc-456', duration: 42 }, 'Order created');
    // Output: {"level":30,"time":1234567890,"userId":123,"orderId":"abc-456","duration":42,"msg":"Order created"}
    ```

    **Log levels** (from most to least severe):

    * `fatal`: App is crashing, needs immediate attention.
    * `error`: Something failed but the app continues.
    * `warn`: Unexpected situation, but handled.
    * `info`: Normal business events (user created, payment processed).
    * `debug`: Development-time detail.
    * `trace`: Very verbose, rarely used in production.

    **Correlation IDs** (essential for microservices):

    ```javascript theme={null}
    const { randomUUID } = require('crypto');

    app.use((req, res, next) => {
        req.id = req.headers['x-request-id'] || randomUUID();
        req.log = logger.child({ requestId: req.id, method: req.method, url: req.url });
        next();
    });

    // Now every log in the request lifecycle has the requestId
    app.get('/users/:id', (req, res) => {
        req.log.info({ userId: req.params.id }, 'Fetching user');
        // {"requestId":"abc-123","method":"GET","url":"/users/42","userId":"42","msg":"Fetching user"}
    });
    ```

    **Log pipeline**: Application -> stdout -> Log collector (Fluentd/Vector/Filebeat) -> Log aggregator (ELK/Datadog/Grafana Loki) -> Dashboards/Alerts.

    **What interviewers are really testing**: Do you use structured logging? Do you know about correlation IDs? Can you explain why Pino is faster than Winston?

    **Red flag answer**: "I use `console.log` and redirect stdout to a file." No structure, no levels, no correlation, and file I/O issues.

    **Follow-up**:

    * "Why is Pino faster than Winston? What architectural decision makes the difference?"
    * "How do you trace a single request across 5 microservices? What headers/IDs do you propagate?"
    * "You're generating 10GB of logs per day. How do you manage log storage, rotation, and retention?"
  </Accordion>
</AccordionGroup>

## 6. Node.js Medium Level Questions

<AccordionGroup>
  <Accordion title="51. Express Middleware Order">
    **Answer**:
    Middleware executes **strictly in the order it is defined**. This is not a detail -- it is fundamental to how Express works, and getting it wrong causes subtle bugs.

    ```javascript theme={null}
    app.use(helmet());                    // 1. Security headers (FIRST -- before any response)
    app.use(cors(corsOptions));           // 2. CORS (before body parsing)
    app.use(compression());              // 3. Compress responses
    app.use(express.json({ limit: '10mb' })); // 4. Parse JSON bodies
    app.use(morgan('combined'));          // 5. Log requests (after body parsing so we can log body size)
    app.use(authMiddleware);              // 6. Authenticate
    app.get('/api/users', handler);       // 7. Route handler
    app.use(notFoundHandler);             // 8. 404 for unmatched routes
    app.use(errorHandler);                // 9. Error handling (MUST BE LAST)
    ```

    **Common ordering bugs**:

    * Putting `errorHandler` before routes: errors thrown in routes never reach it.
    * Putting `authMiddleware` after routes: routes execute unauthenticated.
    * Putting `express.json()` after routes: `req.body` is `undefined` in handlers.
    * Putting `cors()` after routes: preflight OPTIONS requests get 404.

    **What interviewers are really testing**: Can you reason about why order matters and diagnose ordering bugs?

    **Red flag answer**: "I just put middleware in whatever order and it works." It works until it doesn't, and the bugs are subtle.

    **Follow-up**:

    * "You add `express.json()` but `req.body` is always `undefined`. What went wrong?"
    * "Should `compression()` go before or after `express.static()`? Why?"
  </Accordion>

  <Accordion title="52. Route Parameters and Query Strings">
    **Answer**:

    ```javascript theme={null}
    // Route params: /users/:id -- part of the URL path, required
    app.get('/users/:id', (req, res) => {
        const id = req.params.id; // Always a string! Must parse for numbers.
    });

    // Multiple params: /users/:userId/posts/:postId
    app.get('/users/:userId/posts/:postId', (req, res) => {
        const { userId, postId } = req.params;
    });

    // Query strings: /search?q=node&limit=10 -- optional filters
    app.get('/search', (req, res) => {
        const { q, limit = '20', page = '1' } = req.query; // All strings!
        const parsedLimit = Math.min(parseInt(limit, 10), 100); // Always validate/cap
    });

    // Optional params with regex
    app.get('/files/:path(*)', (req, res) => {
        // Matches /files/a/b/c.txt -- req.params.path = 'a/b/c.txt'
    });
    ```

    **Key gotcha**: Both `req.params` and `req.query` values are **always strings**. `req.params.id` is `"42"`, not `42`. Always parse and validate.

    **Security consideration**: Query strings can be manipulated. Never trust `req.query.isAdmin` without server-side authorization.

    **What interviewers are really testing**: Do you know params are strings? Do you validate and sanitize?

    **Follow-up**:

    * "What is `req.params` vs `req.query` vs `req.body`? When do you use each?"
    * "How would you handle a route parameter that must be a valid MongoDB ObjectId?"
  </Accordion>

  <Accordion title="53. Body Parsing (Code Example)">
    **Answer**:

    ```javascript theme={null}
    // JSON bodies (Content-Type: application/json)
    app.use(express.json({ limit: '10mb' }));

    // URL-encoded bodies (Content-Type: application/x-www-form-urlencoded)
    app.use(express.urlencoded({ extended: true }));
    // extended: true -> uses `qs` library (nested objects: user[name]=foo)
    // extended: false -> uses `querystring` (flat only: user=foo)

    // Multipart form data (file uploads)
    const multer = require('multer');
    const upload = multer({
        dest: 'uploads/',
        limits: { fileSize: 5 * 1024 * 1024 } // 5MB limit
    });
    app.post('/upload', upload.single('avatar'), (req, res) => {
        // req.file contains upload metadata
        // req.body contains text fields
    });
    ```

    **What happens without body parsing**: `req.body` is `undefined`. This is the number one "Express isn't working" complaint from beginners.

    **What interviewers are really testing**: Do you always set size limits? Do you understand the different content types?

    **Follow-up**:

    * "What's the difference between `extended: true` and `extended: false`? When does it matter?"
    * "How does body parsing relate to streams internally?"
  </Accordion>

  <Accordion title="54. Cookie and Session Management">
    **Answer**:

    ```javascript theme={null}
    const cookieParser = require('cookie-parser');
    const session = require('express-session');
    const RedisStore = require('connect-redis').default;
    const redis = require('redis');

    app.use(cookieParser());

    // Production session config
    const redisClient = redis.createClient({ url: process.env.REDIS_URL });
    await redisClient.connect();

    app.use(session({
        store: new RedisStore({ client: redisClient }),  // Shared session store
        secret: process.env.SESSION_SECRET,              // Sign the session cookie
        resave: false,                                   // Don't re-save unchanged sessions
        saveUninitialized: false,                        // Don't create session until something stored
        cookie: {
            secure: process.env.NODE_ENV === 'production', // HTTPS only in prod
            httpOnly: true,                                 // No JS access
            maxAge: 24 * 60 * 60 * 1000,                  // 24 hours
            sameSite: 'lax'                                // CSRF protection
        }
    }));

    // Usage
    app.post('/login', (req, res) => {
        req.session.userId = user.id;  // Stored in Redis, only session ID in cookie
        req.session.role = user.role;
    });

    app.get('/profile', (req, res) => {
        if (!req.session.userId) return res.status(401).json({ error: 'Not authenticated' });
        // Fetch user by req.session.userId
    });

    app.post('/logout', (req, res) => {
        req.session.destroy((err) => {
            res.clearCookie('connect.sid');
            res.json({ message: 'Logged out' });
        });
    });
    ```

    **Why Redis for sessions**: In-memory sessions break with multiple servers/workers. Redis is the standard shared session store.

    **What interviewers are really testing**: Do you use Redis for session storage? Do you set cookie security flags?

    **Follow-up**:

    * "What is `resave: false` vs `true`? What are the implications of each?"
    * "How do you handle session expiry gracefully from the user's perspective?"
  </Accordion>

  <Accordion title="55. Error Handling in Express (Comprehensive)">
    **Answer**:

    ```javascript theme={null}
    // 1. Async error wrapper (Express 4 requirement)
    const asyncHandler = (fn) => (req, res, next) =>
        Promise.resolve(fn(req, res, next)).catch(next);

    // 2. Custom error classes for consistent error responses
    class AppError extends Error {
        constructor(message, statusCode) {
            super(message);
            this.statusCode = statusCode;
            this.isOperational = true; // Expected error, not a bug
        }
    }

    class NotFoundError extends AppError {
        constructor(resource = 'Resource') {
            super(`${resource} not found`, 404);
        }
    }

    class ValidationError extends AppError {
        constructor(message) {
            super(message, 400);
        }
    }

    // 3. Route handlers throw custom errors
    app.get('/users/:id', asyncHandler(async (req, res) => {
        const user = await db.users.findById(req.params.id);
        if (!user) throw new NotFoundError('User');
        res.json(user);
    }));

    // 4. Centralized error handler (LAST middleware)
    app.use((err, req, res, next) => {
        // Log full error for debugging
        req.log?.error({ err, url: req.originalUrl }, 'Request error');
        
        // Operational errors: send message to client
        if (err.isOperational) {
            return res.status(err.statusCode).json({ error: err.message });
        }
        
        // Programming errors: don't leak internals
        res.status(500).json({
            error: 'Internal Server Error',
            ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
        });
    });
    ```

    **What interviewers are really testing**: Do you distinguish between operational errors (expected, e.g., 404) and programmer errors (bugs)? Do you have a centralized error handler?

    **Follow-up**:

    * "What's the difference between an operational error and a programmer error? How do you handle each differently?"
    * "In Express 5, do you still need the `asyncHandler` wrapper?"
  </Accordion>

  <Accordion title="56. CORS Configuration (Production)">
    **Answer**:

    ```javascript theme={null}
    const cors = require('cors');

    // DEVELOPMENT: Allow all (quick and dirty)
    app.use(cors());

    // PRODUCTION: Whitelist specific origins
    const allowedOrigins = [
        'https://app.example.com',
        'https://admin.example.com',
        process.env.NODE_ENV === 'development' && 'http://localhost:3000'
    ].filter(Boolean);

    app.use(cors({
        origin: (origin, callback) => {
            // Allow requests with no origin (mobile apps, curl, server-to-server)
            if (!origin) return callback(null, true);
            
            if (allowedOrigins.includes(origin)) {
                callback(null, true);
            } else {
                callback(new Error(`Origin ${origin} not allowed by CORS`));
            }
        },
        credentials: true,                        // Allow cookies
        methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
        allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-ID'],
        exposedHeaders: ['X-Total-Count'],        // Headers the client can read
        maxAge: 86400,                            // Cache preflight for 24 hours
    }));
    ```

    **Dynamic origin** is important because `origin: '*'` cannot be used with `credentials: true`. The function-based approach lets you whitelist multiple origins while still supporting credentials.

    **What interviewers are really testing**: Do you use dynamic origin checking in production?

    **Follow-up**:

    * "Why does the function receive `origin: undefined` for some requests? Which requests have no origin?"
    * "How do you debug CORS errors? Where do you look?"
  </Accordion>

  <Accordion title="57. Rate Limiting (Code Example)">
    **Answer**:

    ```javascript theme={null}
    const rateLimit = require('express-rate-limit');

    // General API rate limit
    const apiLimiter = rateLimit({
        windowMs: 15 * 60 * 1000, // 15 minutes
        max: 100,                  // 100 requests per window per IP
        standardHeaders: true,     // Return rate limit info in RateLimit-* headers
        legacyHeaders: false,      // Disable X-RateLimit-* headers
        message: { error: 'Too many requests, please try again later.' }
    });

    // Strict limit for auth endpoints (prevent brute force)
    const authLimiter = rateLimit({
        windowMs: 15 * 60 * 1000,
        max: 5,                    // Only 5 login attempts per 15 min
        skipSuccessfulRequests: true // Don't count successful logins
    });

    app.use('/api/', apiLimiter);
    app.use('/api/auth/login', authLimiter);
    app.use('/api/auth/register', authLimiter);
    ```

    **Important**: In production with multiple servers, use a Redis store (see question 45 for details).

    **What interviewers are really testing**: Do you apply different limits to different endpoints? Do you know about `skipSuccessfulRequests`?

    **Follow-up**:

    * "How do you handle rate limiting for authenticated users vs anonymous users?"
    * "What's the `RateLimit-*` header standard (RFC 6585) and how does the client use it?"
  </Accordion>

  <Accordion title="58. Input Validation">
    **Answer**:
    **Never trust client input.** Validate every field for type, format, range, and length.

    ```javascript theme={null}
    // Using express-validator (declarative, middleware-based)
    const { body, param, query, validationResult } = require('express-validator');

    app.post('/users',
        body('email').isEmail().normalizeEmail(),
        body('password').isLength({ min: 8 }).matches(/[A-Z]/).matches(/[0-9]/),
        body('age').optional().isInt({ min: 0, max: 150 }),
        body('name').trim().notEmpty().escape(), // escape() prevents XSS
        (req, res) => {
            const errors = validationResult(req);
            if (!errors.isEmpty()) {
                return res.status(400).json({
                    errors: errors.array().map(e => ({
                        field: e.path,
                        message: e.msg,
                        value: e.value
                    }))
                });
            }
            // req.body is now validated and sanitized
        }
    );

    // Alternative: Zod (TypeScript-first, schema-based)
    const { z } = require('zod');

    const createUserSchema = z.object({
        email: z.string().email(),
        password: z.string().min(8).regex(/[A-Z]/).regex(/[0-9]/),
        age: z.number().int().min(0).max(150).optional(),
        name: z.string().min(1).max(100),
    });

    app.post('/users', (req, res) => {
        const result = createUserSchema.safeParse(req.body);
        if (!result.success) {
            return res.status(400).json({ errors: result.error.flatten() });
        }
        const validData = result.data; // Fully typed and validated
    });
    ```

    **What interviewers are really testing**: Do you validate on the server even if the client validates? Do you sanitize in addition to validating?

    **Red flag answer**: "I validate on the frontend so the backend doesn't need to." Client-side validation is UX -- server-side validation is security.

    **Follow-up**:

    * "What's the difference between validation (is this valid?) and sanitization (make this safe)?"
    * "How does Zod compare to express-validator? When would you choose one over the other?"
  </Accordion>

  <Accordion title="59. Database Connection Pooling (Code)">
    **Answer**:

    ```javascript theme={null}
    // PostgreSQL with pg
    const { Pool } = require('pg');
    const pool = new Pool({
        connectionString: process.env.DATABASE_URL,
        max: 20,                        // Max connections in pool
        idleTimeoutMillis: 30000,       // Close idle connections after 30s
        connectionTimeoutMillis: 5000,  // Fail if can't get connection in 5s
        ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false
    });

    // Proper usage: let the pool manage connections
    const result = await pool.query('SELECT * FROM users WHERE id = $1', [userId]);

    // For transactions: checkout a client, run queries, release
    const client = await pool.connect();
    try {
        await client.query('BEGIN');
        await client.query('INSERT INTO orders ...', [orderData]);
        await client.query('UPDATE inventory SET stock = stock - 1 WHERE id = $1', [itemId]);
        await client.query('COMMIT');
    } catch (err) {
        await client.query('ROLLBACK');
        throw err;
    } finally {
        client.release(); // CRITICAL: always release back to pool!
    }
    ```

    **The `client.release()` trap**: If you forget to release a client, it's "leaked" -- the pool thinks it's still in use. After `max` leaks, all new queries hang forever waiting for a connection.

    **What interviewers are really testing**: Do you handle transactions correctly? Do you always release connections?

    **Follow-up**:

    * "What happens if you forget to call `client.release()` after checking out a client from the pool?"
    * "How do you monitor pool health to detect connection leaks?"
  </Accordion>

  <Accordion title="60. MongoDB with Mongoose">
    **Answer**:

    ```javascript theme={null}
    const mongoose = require('mongoose');

    // Connection with production options
    await mongoose.connect(process.env.MONGODB_URI, {
        maxPoolSize: 10,          // Connection pool size
        serverSelectionTimeoutMS: 5000,
        socketTimeoutMS: 45000,
    });

    // Schema with validation, indexes, virtuals
    const userSchema = new mongoose.Schema({
        name: { type: String, required: true, trim: true },
        email: { type: String, required: true, unique: true, lowercase: true },
        password: { type: String, required: true, select: false }, // Excluded from queries by default
        role: { type: String, enum: ['user', 'admin'], default: 'user' },
        loginAttempts: { type: Number, default: 0 },
    }, {
        timestamps: true, // createdAt, updatedAt
        toJSON: { virtuals: true }
    });

    // Index for performance
    userSchema.index({ email: 1 });
    userSchema.index({ createdAt: -1 });

    // Pre-save hook (hash password)
    userSchema.pre('save', async function(next) {
        if (!this.isModified('password')) return next();
        this.password = await bcrypt.hash(this.password, 12);
        next();
    });

    // Instance method
    userSchema.methods.comparePassword = async function(candidatePassword) {
        return bcrypt.compare(candidatePassword, this.password);
    };

    const User = mongoose.model('User', userSchema);
    ```

    **What interviewers are really testing**: Do you use schemas with validation? Do you know about pre/post hooks, `select: false`, and indexing?

    **Follow-up**:

    * "What's the difference between Mongoose validation and MongoDB schema validation? When would you use each?"
    * "How do you handle the `unique` constraint in Mongoose -- is it a validator or an index?"
  </Accordion>

  <Accordion title="61. JWT Authentication (Production)">
    **Answer**:

    ```javascript theme={null}
    const jwt = require('jsonwebtoken');

    // Token generation with proper claims
    function generateTokens(user) {
        const accessToken = jwt.sign(
            { sub: user.id, role: user.role },
            process.env.JWT_ACCESS_SECRET,
            { expiresIn: '15m', issuer: 'api.example.com' }
        );
        
        const refreshToken = jwt.sign(
            { sub: user.id, tokenVersion: user.tokenVersion },
            process.env.JWT_REFRESH_SECRET,
            { expiresIn: '7d' }
        );
        
        return { accessToken, refreshToken };
    }

    // Auth middleware with proper error handling
    function authenticate(req, res, next) {
        const authHeader = req.headers.authorization;
        if (!authHeader?.startsWith('Bearer ')) {
            return res.status(401).json({ error: 'No token provided' });
        }
        
        const token = authHeader.split(' ')[1];
        try {
            const payload = jwt.verify(token, process.env.JWT_ACCESS_SECRET, {
                issuer: 'api.example.com'
            });
            req.user = { id: payload.sub, role: payload.role };
            next();
        } catch (err) {
            if (err.name === 'TokenExpiredError') {
                return res.status(401).json({ error: 'Token expired', code: 'TOKEN_EXPIRED' });
            }
            return res.status(401).json({ error: 'Invalid token' });
        }
    }

    // Role-based authorization
    function authorize(...roles) {
        return (req, res, next) => {
            if (!roles.includes(req.user.role)) {
                return res.status(403).json({ error: 'Insufficient permissions' });
            }
            next();
        };
    }

    app.get('/admin/users', authenticate, authorize('admin'), handler);
    ```

    **What interviewers are really testing**: Do you separate authentication from authorization? Do you handle token expiry gracefully?

    **Follow-up**:

    * "How do you implement token refresh? What happens if the refresh token is stolen?"
    * "How would you revoke all tokens for a user who changes their password?"
  </Accordion>

  <Accordion title="62. Password Hashing with bcrypt">
    **Answer**:

    ```javascript theme={null}
    const bcrypt = require('bcrypt');

    // Hash password -- salt rounds determine computational cost
    // 10 rounds ~= 100ms, 12 rounds ~= 300ms, 14 rounds ~= 1s
    const SALT_ROUNDS = 12;

    async function hashPassword(plaintext) {
        return bcrypt.hash(plaintext, SALT_ROUNDS);
        // Output: "$2b$12$LJ3m4ys3Lk0TH9Kz..." (includes algorithm, cost, salt, and hash)
    }

    async function verifyPassword(plaintext, hash) {
        return bcrypt.compare(plaintext, hash);
        // bcrypt extracts the salt from the stored hash -- no need to store salt separately
    }
    ```

    **Why bcrypt specifically**:

    * **Adaptive cost**: Increase `SALT_ROUNDS` as hardware gets faster (double cost every 18 months to match Moore's Law).
    * **Built-in salt**: Each hash includes a random salt -- identical passwords produce different hashes.
    * **Intentionally slow**: 12 rounds = \~300ms per hash. An attacker trying 10 billion passwords needs \~95 years. MD5/SHA would take minutes.

    **Alternatives**: `argon2` (winner of the Password Hashing Competition, recommended for new projects), `scrypt` (built into Node's `crypto` module).

    **What interviewers are really testing**: Do you know why bcrypt is slow *on purpose*? Can you explain adaptive cost?

    **Red flag answer**: "I use SHA256 to hash passwords." SHA is a fast hash designed for integrity checks, not password storage.

    **Follow-up**:

    * "Why is bcrypt better than SHA-256 for passwords? They're both hash functions."
    * "What salt rounds would you choose and how do you decide when to increase them?"
  </Accordion>

  <Accordion title="63. File Uploads with Multer (Production)">
    **Answer**:

    ```javascript theme={null}
    const multer = require('multer');
    const path = require('path');
    const crypto = require('crypto');

    // Production storage config
    const storage = multer.diskStorage({
        destination: (req, file, cb) => {
            cb(null, path.join(__dirname, 'uploads'));
        },
        filename: (req, file, cb) => {
            // Generate random filename to prevent collisions and path traversal
            const ext = path.extname(file.originalname);
            const name = crypto.randomBytes(16).toString('hex');
            cb(null, `${name}${ext}`);
        }
    });

    // File filter with MIME type validation
    const fileFilter = (req, file, cb) => {
        const allowedTypes = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
        if (allowedTypes.includes(file.mimetype)) {
            cb(null, true);
        } else {
            cb(new multer.MulterError('LIMIT_UNEXPECTED_FILE', 'Invalid file type'), false);
        }
    };

    const upload = multer({
        storage,
        fileFilter,
        limits: {
            fileSize: 5 * 1024 * 1024, // 5MB
            files: 5                     // Max 5 files per request
        }
    });

    // Error handling for multer
    app.post('/upload', (req, res, next) => {
        upload.single('avatar')(req, res, (err) => {
            if (err instanceof multer.MulterError) {
                return res.status(400).json({ error: err.message });
            }
            if (err) return next(err);
            
            if (!req.file) return res.status(400).json({ error: 'No file uploaded' });
            res.json({ filename: req.file.filename, size: req.file.size });
        });
    });
    ```

    **What interviewers are really testing**: Do you validate file types, set size limits, and generate random filenames?

    **Follow-up**:

    * "How would you stream uploads directly to S3 without saving to disk?"
    * "Why do you generate random filenames instead of using the original filename?"
  </Accordion>

  <Accordion title="64. Email Sending with Nodemailer">
    **Answer**:

    ```javascript theme={null}
    const nodemailer = require('nodemailer');

    // Production: use a dedicated email service (SendGrid, SES, Mailgun)
    const transporter = nodemailer.createTransport({
        host: process.env.SMTP_HOST,       // e.g., smtp.sendgrid.net
        port: 587,
        secure: false,                      // true for 465, false for other ports
        auth: {
            user: process.env.SMTP_USER,
            pass: process.env.SMTP_PASS
        },
        pool: true,                         // Reuse connections
        maxConnections: 5,
        maxMessages: 100                    // Max messages per connection
    });

    // Send with proper error handling and retry
    async function sendEmail(to, subject, html) {
        try {
            const info = await transporter.sendMail({
                from: '"MyApp" <noreply@example.com>',
                to,
                subject,
                html,
                text: htmlToText(html)      // Always include plaintext fallback
            });
            logger.info({ messageId: info.messageId }, 'Email sent');
            return info;
        } catch (err) {
            logger.error({ err, to, subject }, 'Email send failed');
            throw err;
        }
    }
    ```

    **Production pattern**: Never send emails synchronously in a request handler. Use a job queue:

    ```javascript theme={null}
    // In request handler
    await emailQueue.add('welcome-email', { userId: user.id, email: user.email });

    // In worker (separate process)
    emailQueue.process('welcome-email', async (job) => {
        await sendEmail(job.data.email, 'Welcome!', welcomeTemplate(job.data));
    });
    ```

    **What interviewers are really testing**: Do you send emails asynchronously via a queue? Do you use connection pooling?

    **Follow-up**:

    * "Why should emails be sent via a queue instead of directly in the request handler?"
    * "How do you handle email delivery failures and retries?"
  </Accordion>

  <Accordion title="65. Testing with Jest (Patterns)">
    **Answer**:

    ```javascript theme={null}
    // Unit test with good patterns
    describe('UserService', () => {
        let service;
        
        beforeEach(() => {
            service = createUserService(mockDb, mockMailer);
            jest.clearAllMocks(); // Reset mock call counts between tests
        });
        
        describe('createUser', () => {
            it('creates user with hashed password', async () => {
                const input = { email: 'test@test.com', password: 'Pass123!' };
                mockDb.users.create.mockResolvedValue({ id: 1, ...input });
                
                const user = await service.createUser(input);
                
                expect(user.id).toBe(1);
                expect(mockDb.users.create).toHaveBeenCalledTimes(1);
            });
            
            it('rejects duplicate emails', async () => {
                mockDb.users.create.mockRejectedValue(new Error('Duplicate key'));
                
                await expect(service.createUser({ email: 'dup@test.com' }))
                    .rejects.toThrow('Duplicate key');
            });
        });
    });

    // Integration test with supertest
    const request = require('supertest');
    const app = require('./app');

    describe('POST /api/users', () => {
        it('returns 201 with valid data', async () => {
            const res = await request(app)
                .post('/api/users')
                .send({ email: 'test@test.com', password: 'Pass123!' })
                .expect(201);
            
            expect(res.body).toHaveProperty('id');
            expect(res.body.email).toBe('test@test.com');
        });
        
        it('returns 400 with invalid email', async () => {
            await request(app)
                .post('/api/users')
                .send({ email: 'invalid', password: 'Pass123!' })
                .expect(400);
        });
    });
    ```

    **What interviewers are really testing**: Do you test both happy and error paths? Do you use `beforeEach` to reset state?

    **Follow-up**:

    * "How do you test a route that requires authentication? How do you mock the auth middleware?"
    * "What's the difference between unit testing a service and integration testing an endpoint?"
  </Accordion>

  <Accordion title="66. Mocking with Jest">
    **Answer**:

    ```javascript theme={null}
    // 1. Mock function (spy on calls, control return values)
    const mockFn = jest.fn();
    mockFn.mockReturnValue(42);
    mockFn.mockResolvedValue({ data: 'async result' });
    mockFn.mockImplementation((x) => x * 2);

    // 2. Mock an entire module
    jest.mock('./database');
    const db = require('./database');
    db.query.mockResolvedValue([{ id: 1, name: 'Alice' }]);

    // 3. Partial mock (keep some real implementations)
    jest.mock('./utils', () => ({
        ...jest.requireActual('./utils'),  // Keep real implementations
        sendEmail: jest.fn()                // Only mock this one
    }));

    // 4. Mock timers (for setTimeout/setInterval tests)
    jest.useFakeTimers();
    setTimeout(callback, 1000);
    jest.advanceTimersByTime(1000);
    expect(callback).toHaveBeenCalled();

    // 5. Spy on existing methods (without replacing)
    const spy = jest.spyOn(console, 'error').mockImplementation(() => {});
    // ... test code that logs errors ...
    expect(spy).toHaveBeenCalledWith(expect.stringContaining('failed'));
    spy.mockRestore(); // Clean up
    ```

    **Mock vs Stub vs Spy**:

    * **Mock**: Replace a function entirely, control its behavior and verify calls.
    * **Stub**: A mock with pre-programmed return values (focused on output, not call verification).
    * **Spy**: Wraps the real function, records calls but still executes the original.

    **What interviewers are really testing**: Do you know when to use `jest.mock` vs `jest.spyOn`? Can you mock at the right granularity?

    **Follow-up**:

    * "When would you use `jest.spyOn` instead of `jest.mock`?"
    * "How do you mock a function that's imported using ES module named exports?"
  </Accordion>

  <Accordion title="67. Environment Variables (Code)">
    **Answer**:

    ```javascript theme={null}
    // Load at the very top of entry point
    require('dotenv').config();

    // Typed, validated config module
    const config = {
        port: parseInt(process.env.PORT, 10) || 3000,
        nodeEnv: process.env.NODE_ENV || 'development',
        db: {
            url: process.env.DATABASE_URL,
            poolSize: parseInt(process.env.DB_POOL_SIZE, 10) || 10,
        },
        redis: {
            url: process.env.REDIS_URL || 'redis://localhost:6379',
        },
        jwt: {
            accessSecret: process.env.JWT_ACCESS_SECRET,
            refreshSecret: process.env.JWT_REFRESH_SECRET,
        },
        get isProduction() { return this.nodeEnv === 'production'; },
        get isDevelopment() { return this.nodeEnv === 'development'; },
    };

    // Validate required vars
    const required = ['DATABASE_URL', 'JWT_ACCESS_SECRET'];
    const missing = required.filter(key => !process.env[key]);
    if (missing.length > 0) {
        throw new Error(`Missing required env vars: ${missing.join(', ')}`);
    }

    module.exports = config;
    ```

    **Always provide `.env.example`**:

    ```bash theme={null}
    # .env.example (committed to git, no real values)
    DATABASE_URL=postgresql://user:password@localhost:5432/mydb
    JWT_ACCESS_SECRET=change-me-in-production
    REDIS_URL=redis://localhost:6379
    ```

    **What interviewers are really testing**: Do you validate env vars at startup? Do you centralize config?

    **Follow-up**:

    * "How do you handle different configs for development, staging, and production?"
    * "What's the security risk of logging `process.env` for debugging?"
  </Accordion>

  <Accordion title="68. Logging with Winston / Pino">
    **Answer**:

    ```javascript theme={null}
    // Pino (recommended -- 5-10x faster than Winston)
    const pino = require('pino');

    const logger = pino({
        level: process.env.LOG_LEVEL || 'info',
        serializers: {
            err: pino.stdSerializers.err,    // Properly serialize Error objects
            req: pino.stdSerializers.req,
        },
        redact: ['req.headers.authorization', 'password'], // Strip sensitive fields
    });

    // Winston (more configurable, slower)
    const winston = require('winston');

    const logger = winston.createLogger({
        level: 'info',
        format: winston.format.combine(
            winston.format.timestamp(),
            winston.format.errors({ stack: true }),
            winston.format.json()
        ),
        defaultMeta: { service: 'user-api' },
        transports: [
            new winston.transports.Console(),
            new winston.transports.File({ filename: 'error.log', level: 'error' }),
        ]
    });

    // Why Pino is faster: it defers JSON serialization to a worker thread
    // (pino-worker transport) and writes to stdout asynchronously.
    // Winston serializes synchronously on the main thread.
    ```

    **What interviewers are really testing**: Do you know why Pino is faster? Do you redact sensitive fields?

    **Follow-up**:

    * "Why does Pino recommend writing to stdout instead of files?"
    * "How do you add request context (requestId, userId) to every log line in a request lifecycle?"
  </Accordion>

  <Accordion title="69. HTTP Request Logging (Morgan)">
    **Answer**:

    ```javascript theme={null}
    const morgan = require('morgan');

    // Development: concise, colored output
    app.use(morgan('dev'));
    // GET /api/users 200 12.345 ms

    // Production: combined format (Apache-style) piped to logger
    app.use(morgan('combined', {
        stream: {
            write: (message) => logger.info(message.trim()) // Pipe to Pino/Winston
        },
        skip: (req, res) => res.statusCode < 400  // Only log errors in production
    }));

    // Custom format with response time and body size
    app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
    ```

    **Production consideration**: Morgan writes to stdout synchronously. For high-throughput APIs (10k+ req/s), consider using Pino's built-in HTTP logging instead (`pino-http`), which is asynchronous and integrates with request context.

    **What interviewers are really testing**: Do you know about piping Morgan output to a proper logger?

    **Follow-up**:

    * "What's the performance difference between Morgan and pino-http?"
    * "How do you avoid logging sensitive data (passwords, tokens) in request/response logs?"
  </Accordion>

  <Accordion title="70. Static File Serving">
    **Answer**:

    ```javascript theme={null}
    const path = require('path');

    // Basic static serving
    app.use(express.static(path.join(__dirname, 'public')));

    // With prefix
    app.use('/assets', express.static(path.join(__dirname, 'public')));

    // Production options
    app.use(express.static(path.join(__dirname, 'public'), {
        maxAge: '1y',                // Cache static assets for 1 year
        etag: true,                  // Enable ETag for cache validation
        lastModified: true,
        immutable: true,             // Files won't change (use with fingerprinted filenames)
        index: false,                // Don't serve index.html for directories
        dotfiles: 'deny',           // Reject requests for .env, .git, etc.
    }));
    ```

    **Production reality**: In production, you typically don't serve static files from Node at all. Use a **CDN** (CloudFront, Cloudflare) or **Nginx** reverse proxy. Node's event loop should be reserved for dynamic API responses, not static file I/O.

    **What interviewers are really testing**: Do you know that Node shouldn't serve static files in production? Do you set proper cache headers?

    **Follow-up**:

    * "Why shouldn't Node.js serve static files in production? What should serve them instead?"
    * "What is the `immutable` cache directive and when should you use it?"
  </Accordion>
</AccordionGroup>

## 7. Node.js Advanced Level Questions

<AccordionGroup>
  <Accordion title="71. Custom Stream Implementation">
    **Answer**:
    Understanding custom streams shows deep knowledge of Node's I/O model.

    ```javascript theme={null}
    const { Readable, Writable, Transform } = require('stream');

    // Custom Readable: generates data
    class DatabaseCursor extends Readable {
        constructor(query, options) {
            super({ ...options, objectMode: true }); // Object mode for JS objects
            this.query = query;
            this.offset = 0;
            this.batchSize = 100;
        }
        
        async _read() {
            try {
                const rows = await db.query(this.query, {
                    offset: this.offset,
                    limit: this.batchSize
                });
                
                if (rows.length === 0) {
                    this.push(null); // Signal end of stream
                    return;
                }
                
                for (const row of rows) {
                    // push returns false when highWaterMark reached (backpressure)
                    if (!this.push(row)) break;
                }
                this.offset += rows.length;
            } catch (err) {
                this.destroy(err); // Propagate error
            }
        }
    }

    // Usage: stream millions of rows with constant memory
    const cursor = new DatabaseCursor('SELECT * FROM large_table');
    cursor.pipe(transformStream).pipe(outputStream);
    ```

    **Key methods to implement**:

    * `Readable._read(size)`: Called when the consumer wants more data. Call `this.push(data)` or `this.push(null)` to end.
    * `Writable._write(chunk, encoding, callback)`: Called for each chunk. Call `callback()` when done, `callback(err)` on error.
    * `Transform._transform(chunk, encoding, callback)`: Process and push modified data. Call `callback()` when done.
    * `Transform._flush(callback)`: Called when all input has been consumed. Emit any remaining buffered output.

    **What interviewers are really testing**: Can you implement streams from scratch, not just use built-in ones?

    **Red flag answer**: Only knowing how to use `fs.createReadStream` without understanding how to build custom streams.

    **Follow-up**:

    * "What is the `_flush` method in a Transform stream and when would you use it?"
    * "How do you handle errors in a custom Readable stream? What happens if `_read` throws?"
    * "When would you use `objectMode: true` and what changes about the stream's behavior?"
  </Accordion>

  <Accordion title="72. Transform Streams (Practical)">
    **Answer**:
    Transform streams are the workhorses of data pipelines -- they sit between a Readable and Writable, modifying data as it flows through.

    ```javascript theme={null}
    const { Transform } = require('stream');

    // CSV to JSON transform
    class CsvToJson extends Transform {
        constructor(options) {
            super({ ...options, objectMode: true });
            this.headers = null;
            this.buffer = '';
        }
        
        _transform(chunk, encoding, callback) {
            this.buffer += chunk.toString();
            const lines = this.buffer.split('\n');
            this.buffer = lines.pop(); // Keep incomplete last line
            
            for (const line of lines) {
                if (!this.headers) {
                    this.headers = line.split(',').map(h => h.trim());
                    continue;
                }
                
                const values = line.split(',');
                const obj = {};
                this.headers.forEach((header, i) => {
                    obj[header] = values[i]?.trim();
                });
                this.push(obj);
            }
            callback();
        }
        
        _flush(callback) {
            // Process any remaining data in the buffer
            if (this.buffer && this.headers) {
                const values = this.buffer.split(',');
                const obj = {};
                this.headers.forEach((header, i) => {
                    obj[header] = values[i]?.trim();
                });
                this.push(obj);
            }
            callback();
        }
    }

    // Usage: process a 10GB CSV file with constant memory
    const { pipeline } = require('stream/promises');

    await pipeline(
        fs.createReadStream('massive.csv'),
        new CsvToJson(),
        new Transform({
            objectMode: true,
            transform(record, enc, cb) {
                // Filter and transform each record
                if (record.status === 'active') {
                    this.push(JSON.stringify(record) + '\n');
                }
                cb();
            }
        }),
        fs.createWriteStream('output.jsonl')
    );
    ```

    **What interviewers are really testing**: Can you handle partial data across chunks (the `buffer` pattern)? Do you implement `_flush` for remaining data?

    **Follow-up**:

    * "What happens if a chunk splits in the middle of a UTF-8 multi-byte character? How do you handle it?"
    * "How would you add parallel processing to a Transform stream (process multiple chunks concurrently)?"
  </Accordion>

  <Accordion title="73. Backpressure Handling (Manual)">
    **Answer**:
    When you cannot use `.pipe()` or `pipeline()`, you need to handle backpressure manually.

    ```javascript theme={null}
    const readable = getReadableStream();
    const writable = getWritableStream();

    // Manual backpressure handling
    readable.on('data', (chunk) => {
        const canContinue = writable.write(chunk);
        if (!canContinue) {
            // Writable buffer is full -- STOP reading!
            readable.pause();
            console.log('Backpressure: pausing reader');
        }
    });

    writable.on('drain', () => {
        // Writable buffer has drained -- RESUME reading
        readable.resume();
        console.log('Drain: resuming reader');
    });

    writable.on('error', (err) => {
        readable.destroy(); // Clean up on error
        console.error('Write error:', err);
    });

    readable.on('end', () => {
        writable.end(); // Signal end of writing
    });
    ```

    **Why this matters**: Without backpressure handling, a fast network read (100MB/s) writing to a slow disk (10MB/s) will buffer 90MB/s in memory. In 10 seconds, you've consumed 900MB of RAM. In a minute, you're OOM.

    **`highWaterMark`** determines when backpressure kicks in:

    ```javascript theme={null}
    // Writable with custom buffer size
    const writable = new Writable({
        highWaterMark: 1024 * 64,  // 64KB buffer before signaling backpressure
        write(chunk, encoding, callback) {
            // Slow operation...
            setTimeout(callback, 100); // Simulating slow write
        }
    });
    ```

    **What interviewers are really testing**: Can you implement backpressure without `.pipe()`? Do you understand `highWaterMark`?

    **Follow-up**:

    * "What is the `highWaterMark` and how does choosing the wrong value affect performance?"
    * "How does `pipeline()` handle backpressure differently from `.pipe()`?"
  </Accordion>

  <Accordion title="74. Child Processes (spawn vs exec vs fork)">
    **Answer**:
    Node provides three ways to create child processes, each with different characteristics.

    ```javascript theme={null}
    const { spawn, exec, fork } = require('child_process');

    // spawn: Stream-based, no shell, best for long-running processes
    const ls = spawn('ls', ['-lh', '/var/log']);
    ls.stdout.on('data', (data) => console.log(`stdout: ${data}`));
    ls.stderr.on('data', (data) => console.error(`stderr: ${data}`));
    ls.on('close', (code) => console.log(`Exit code: ${code}`));

    // exec: Buffer-based, runs in a shell, best for short commands
    // WARNING: maxBuffer defaults to 1MB -- large output causes ENOBUFS
    exec('ls -lh /var/log', { maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => {
        console.log(stdout); // Entire output as a string
    });

    // fork: Specialized spawn for Node.js processes with IPC channel
    const child = fork('./worker.js', [], {
        env: { ...process.env, WORKER_ID: '1' }
    });
    child.send({ type: 'PROCESS', data: payload });
    child.on('message', (msg) => console.log('Worker result:', msg));
    ```

    **When to use each**:

    | Method  | Shell | Output | IPC            | Use Case                                                   |
    | ------- | ----- | ------ | -------------- | ---------------------------------------------------------- |
    | `spawn` | No    | Stream | No             | Long-running processes, large output (ffmpeg, log tailing) |
    | `exec`  | Yes   | Buffer | No             | Short commands, small output (git status, shell scripts)   |
    | `fork`  | No    | Stream | Yes (built-in) | Node.js worker processes that communicate with parent      |

    **Security**: `exec` runs in a shell, making it vulnerable to **command injection** if user input is interpolated:

    ```javascript theme={null}
    // VULNERABLE
    exec(`convert ${userFilename} output.png`); // userFilename = "; rm -rf /"

    // SAFE: Use spawn (no shell, arguments are separate)
    spawn('convert', [userFilename, 'output.png']);
    ```

    **What interviewers are really testing**: Do you know the shell injection risk with `exec`? Can you choose the right method for a given scenario?

    **Red flag answer**: Using `exec` for everything, especially with user-provided input.

    **Follow-up**:

    * "Why is `exec` vulnerable to command injection while `spawn` is not?"
    * "How does `fork`'s IPC channel work? What serialization format does it use?"
    * "How would you implement a process pool for CPU-intensive tasks using `fork`?"
  </Accordion>

  <Accordion title="75. Cluster Module (Production Pattern)">
    **Answer**:

    ```javascript theme={null}
    const cluster = require('cluster');
    const os = require('os');

    if (cluster.isPrimary) {
        const numWorkers = parseInt(process.env.WEB_CONCURRENCY, 10) || os.cpus().length;
        console.log(`Primary ${process.pid} starting ${numWorkers} workers`);
        
        for (let i = 0; i < numWorkers; i++) {
            cluster.fork();
        }
        
        // Auto-restart dead workers
        cluster.on('exit', (worker, code, signal) => {
            if (signal !== 'SIGTERM') { // Only restart if not intentional shutdown
                console.error(`Worker ${worker.process.pid} died (${signal || code}). Restarting...`);
                cluster.fork();
            }
        });
        
        // Graceful shutdown
        process.on('SIGTERM', () => {
            console.log('Primary received SIGTERM, shutting down workers');
            for (const id in cluster.workers) {
                cluster.workers[id].process.kill('SIGTERM');
            }
        });
    } else {
        // Worker process
        const app = require('./app');
        const server = app.listen(process.env.PORT || 3000, () => {
            console.log(`Worker ${process.pid} listening`);
        });
        
        // Graceful shutdown for worker
        process.on('SIGTERM', () => {
            server.close(() => {
                console.log(`Worker ${process.pid} closed`);
                process.exit(0);
            });
        });
    }
    ```

    **Use `WEB_CONCURRENCY` env var** instead of `os.cpus().length` for container environments where CPU count may not reflect the container's allocation.

    **What interviewers are really testing**: Do you handle graceful shutdown? Do you handle the container CPU issue?

    **Follow-up**:

    * "How does the master distribute connections to workers? What load balancing algorithm is used?"
    * "What happens to WebSocket connections when a worker dies?"
  </Accordion>

  <Accordion title="76. Worker Threads (Practical Pattern)">
    **Answer**:

    ```javascript theme={null}
    // main.js -- Worker thread pool pattern
    const { Worker } = require('worker_threads');
    const os = require('os');

    class WorkerPool {
        constructor(workerPath, poolSize = os.cpus().length) {
            this.workerPath = workerPath;
            this.workers = [];
            this.queue = [];
            
            for (let i = 0; i < poolSize; i++) {
                this.addWorker();
            }
        }
        
        addWorker() {
            const worker = new Worker(this.workerPath);
            worker.busy = false;
            worker.on('message', (result) => {
                worker.busy = false;
                worker.currentResolve(result);
                this.processQueue();
            });
            worker.on('error', (err) => {
                worker.busy = false;
                worker.currentReject(err);
                this.processQueue();
            });
            this.workers.push(worker);
        }
        
        runTask(data) {
            return new Promise((resolve, reject) => {
                const idle = this.workers.find(w => !w.busy);
                if (idle) {
                    idle.busy = true;
                    idle.currentResolve = resolve;
                    idle.currentReject = reject;
                    idle.postMessage(data);
                } else {
                    this.queue.push({ data, resolve, reject });
                }
            });
        }
        
        processQueue() {
            if (this.queue.length === 0) return;
            const idle = this.workers.find(w => !w.busy);
            if (!idle) return;
            
            const { data, resolve, reject } = this.queue.shift();
            idle.busy = true;
            idle.currentResolve = resolve;
            idle.currentReject = reject;
            idle.postMessage(data);
        }
    }

    // worker.js
    const { parentPort } = require('worker_threads');
    parentPort.on('message', (data) => {
        // CPU-intensive work here
        const result = heavyComputation(data);
        parentPort.postMessage(result);
    });

    // Usage
    const pool = new WorkerPool('./worker.js', 4);
    const result = await pool.runTask({ image: imageBuffer });
    ```

    **What interviewers are really testing**: Can you design a worker pool? Do you understand the task queuing and worker lifecycle?

    **Follow-up**:

    * "What happens if a worker crashes? How do you make the pool resilient?"
    * "How would you add a timeout to tasks so a hung worker doesn't block the pool forever?"
  </Accordion>

  <Accordion title="77. Performance Hooks">
    **Answer**:

    ```javascript theme={null}
    const { PerformanceObserver, performance } = require('perf_hooks');

    // Set up observer for custom measurements
    const obs = new PerformanceObserver((items) => {
        for (const entry of items.getEntries()) {
            console.log(`${entry.name}: ${entry.duration.toFixed(2)}ms`);
        }
    });
    obs.observe({ entryTypes: ['measure'] });

    // Measure database query time
    performance.mark('db-start');
    const result = await db.query('SELECT * FROM users');
    performance.mark('db-end');
    performance.measure('db-query', 'db-start', 'db-end');

    // Measure with timerify (auto-wrap functions)
    const { performance: perf, createHistogram } = require('perf_hooks');
    const wrappedFn = perf.timerify(myFunction);
    wrappedFn(); // Automatically records duration

    // Histogram for repeated measurements
    const histogram = createHistogram();
    for (let i = 0; i < 1000; i++) {
        const start = performance.now();
        doWork();
        histogram.record(Math.round((performance.now() - start) * 1000)); // microseconds
    }
    console.log(`P50: ${histogram.percentile(50)}us, P99: ${histogram.percentile(99)}us`);
    ```

    **Use in production**: Wrap critical operations (DB queries, external API calls, template rendering) with performance marks. Export histograms to Prometheus/Datadog for P50/P95/P99 monitoring.

    **What interviewers are really testing**: Do you use built-in performance tools or only `Date.now()`? Do you think in percentiles?

    **Follow-up**:

    * "Why is P99 latency more important than average latency? What does it tell you?"
    * "How do you export performance metrics from Node to a monitoring system?"
  </Accordion>

  <Accordion title="78. Heap Snapshots & Memory Analysis">
    **Answer**:

    ```javascript theme={null}
    const v8 = require('v8');

    // Take snapshot programmatically
    const snapshotPath = v8.writeHeapSnapshot();
    console.log(`Snapshot written to ${snapshotPath}`);

    // Trigger snapshot on demand via signal (production-safe)
    process.on('SIGUSR2', () => {
        const path = v8.writeHeapSnapshot();
        console.log(`Heap snapshot: ${path}`);
    });
    // Then: kill -SIGUSR2 <pid>
    ```

    **Analyzing snapshots in Chrome DevTools**:

    1. Open `chrome://inspect` -> "Open dedicated DevTools for Node"
    2. Memory tab -> Load snapshot
    3. Key views:
       * **Summary**: Objects grouped by constructor. Look for unexpectedly large counts.
       * **Comparison**: Load two snapshots, see what grew between them. This is the most powerful memory leak detection tool.
       * **Containment**: Shows the retention tree -- who is keeping an object alive.
       * **Statistics**: Pie chart of memory by type.

    **The "three snapshot technique"** for finding leaks:

    1. Take snapshot after warmup (baseline).
    2. Perform the suspected leaky operation 5-10 times.
    3. Force GC (if `--expose-gc` is enabled) and take snapshot.
    4. Compare snapshots 1 and 3 -- objects that grew are leak candidates.

    **What interviewers are really testing**: Have you actually used heap snapshots to find a real memory leak? Can you describe the workflow?

    **Follow-up**:

    * "You have a production Node service that's leaking memory. How do you take a heap snapshot safely without impacting users?"
    * "What is 'retained size' vs 'shallow size' in a heap snapshot?"
  </Accordion>

  <Accordion title="79. CPU Profiling (Deep Dive)">
    **Answer**:

    ```bash theme={null}
    # V8 built-in profiler (tick-based sampling)
    node --prof app.js
    # Run your workload...
    # Generates isolate-0x*.log

    # Process the profile into human-readable format
    node --prof-process isolate-0x*.log > profile.txt
    # Look for [JavaScript] section -- functions sorted by "ticks" (time spent)

    # Chrome DevTools profiler (interactive, more powerful)
    node --inspect app.js
    # chrome://inspect -> Profiler -> Start -> Run workload -> Stop
    # Shows: flame chart, heavy (bottom-up), tree (top-down), function-by-function

    # Clinic.js flame (best DX for flame graphs)
    npx clinic flame -- node app.js
    # Auto-opens interactive flame graph in browser
    ```

    **Reading a flame graph**:

    * **X-axis**: Not time! It's sorted alphabetically. Width = percentage of total CPU time.
    * **Y-axis**: Call stack depth. Bottom = entry point, top = leaf functions.
    * **Hot path**: Follow the widest bars from bottom to top -- that's where most CPU time is spent.
    * **Plateau**: A wide bar at the top = a single function consuming lots of CPU (optimization target).

    **Common findings**:

    * Wide `JSON.parse` bars: Parse large JSON on a worker thread.
    * Wide regex bars: Optimize or replace the regex.
    * Wide GC bars: Memory pressure -- reduce allocations.
    * Wide `_read` or `_write` bars in streams: Stream processing bottleneck.

    **What interviewers are really testing**: Can you interpret profiling output? Have you used these tools on a real performance issue?

    **Follow-up**:

    * "How does V8's sampling profiler work? What's the difference between sampling and instrumented profiling?"
    * "You see a flame graph where 40% of CPU time is in `JSON.stringify`. What do you do?"
  </Accordion>

  <Accordion title="80. HTTP/2 Server">
    **Answer**:

    ```javascript theme={null}
    const http2 = require('http2');
    const fs = require('fs');

    const server = http2.createSecureServer({
        key: fs.readFileSync('server.key'),
        cert: fs.readFileSync('server.crt'),
        allowHTTP1: true  // Fallback for clients that don't support HTTP/2
    });

    server.on('stream', (stream, headers) => {
        const path = headers[':path'];
        
        // Server Push: proactively send resources the client will need
        if (path === '/index.html') {
            stream.pushStream({ ':path': '/style.css' }, (err, pushStream) => {
                if (!err) {
                    pushStream.respond({ ':status': 200, 'content-type': 'text/css' });
                    pushStream.end(fs.readFileSync('style.css'));
                }
            });
        }
        
        stream.respond({
            ':status': 200,
            'content-type': 'text/html'
        });
        stream.end('<h1>Hello HTTP/2</h1>');
    });

    server.listen(8443);
    ```

    **HTTP/2 advantages over HTTP/1.1**:

    * **Multiplexing**: Multiple requests/responses over a single TCP connection (no head-of-line blocking at the HTTP level).
    * **Header compression** (HPACK): Reduces overhead for repeated headers.
    * **Server Push**: Proactively send resources before the client requests them.
    * **Binary framing**: More efficient than text-based HTTP/1.1.

    **In practice**: Most Node apps don't implement HTTP/2 directly. Instead, a reverse proxy (Nginx, Cloudflare) handles HTTP/2 with clients and uses HTTP/1.1 to communicate with Node. This is simpler and Nginx handles HTTP/2 more efficiently.

    **What interviewers are really testing**: Do you know HTTP/2's multiplexing benefit? Do you understand the typical deployment pattern with a reverse proxy?

    **Follow-up**:

    * "What is HTTP/2 server push and why has it been controversial? Why did Chrome remove push support?"
    * "What is head-of-line blocking and how does HTTP/2 partially solve it? What about HTTP/3?"
  </Accordion>

  <Accordion title="81. WebSocket Server (ws library)">
    **Answer**:

    ```javascript theme={null}
    const { WebSocketServer } = require('ws');
    const http = require('http');

    const server = http.createServer();
    const wss = new WebSocketServer({ server });

    // Connection tracking
    const clients = new Map();

    wss.on('connection', (ws, req) => {
        const userId = authenticateFromHeaders(req.headers);
        clients.set(userId, ws);
        
        ws.isAlive = true;
        ws.on('pong', () => { ws.isAlive = true; });
        
        ws.on('message', (data) => {
            const message = JSON.parse(data);
            switch (message.type) {
                case 'chat':
                    broadcast(message, userId);
                    break;
                case 'ping':
                    ws.send(JSON.stringify({ type: 'pong' }));
                    break;
            }
        });
        
        ws.on('close', () => {
            clients.delete(userId);
        });
        
        ws.on('error', (err) => {
            console.error('WebSocket error:', err);
            clients.delete(userId);
        });
    });

    // Heartbeat: detect dead connections
    const heartbeat = setInterval(() => {
        wss.clients.forEach((ws) => {
            if (!ws.isAlive) return ws.terminate();
            ws.isAlive = false;
            ws.ping();
        });
    }, 30000);

    wss.on('close', () => clearInterval(heartbeat));

    function broadcast(message, senderId) {
        const data = JSON.stringify({ ...message, from: senderId });
        for (const [id, client] of clients) {
            if (id !== senderId && client.readyState === 1) { // 1 = OPEN
                client.send(data);
            }
        }
    }

    server.listen(8080);
    ```

    **Production concerns**: Heartbeat for dead connection detection, authentication on upgrade, message size limits, rate limiting per connection, and scaling across multiple servers via Redis pub/sub.

    **What interviewers are really testing**: Do you implement heartbeat? Do you handle connection cleanup?

    **Follow-up**:

    * "How do you authenticate a WebSocket connection? Can you use cookies?"
    * "What happens if a client sends messages faster than the server can process them? How do you handle backpressure on WebSocket?"
  </Accordion>

  <Accordion title="82. GraphQL Server">
    **Answer**:

    ```javascript theme={null}
    const { ApolloServer } = require('@apollo/server');
    const { expressMiddleware } = require('@apollo/server/express4');

    // Type definitions
    const typeDefs = `#graphql
        type User {
            id: ID!
            name: String!
            email: String!
            posts: [Post!]!
        }
        
        type Post {
            id: ID!
            title: String!
            author: User!
        }
        
        type Query {
            user(id: ID!): User
            users(limit: Int, offset: Int): [User!]!
        }
        
        type Mutation {
            createUser(name: String!, email: String!): User!
        }
    `;

    // Resolvers with DataLoader for N+1 prevention
    const resolvers = {
        Query: {
            user: (_, { id }, { dataSources }) => dataSources.users.findById(id),
            users: (_, { limit = 20, offset = 0 }, { dataSources }) =>
                dataSources.users.findAll({ limit, offset }),
        },
        User: {
            // DataLoader batches these calls automatically
            posts: (user, _, { loaders }) => loaders.postsByUser.load(user.id),
        },
        Mutation: {
            createUser: (_, args, { dataSources }) => dataSources.users.create(args),
        }
    };

    const server = new ApolloServer({ typeDefs, resolvers });
    await server.start();

    app.use('/graphql', expressMiddleware(server, {
        context: async ({ req }) => ({
            user: await authenticate(req),
            dataSources: { users: new UserDataSource() },
            loaders: {
                postsByUser: new DataLoader(batchPostsByUserIds)
            }
        })
    }));
    ```

    **What interviewers are really testing**: Do you use DataLoader to solve N+1? Do you create per-request context?

    **Follow-up**:

    * "Why is a new DataLoader created for each request instead of sharing one globally?"
    * "What is query complexity analysis and how do you prevent abusive queries (deeply nested, huge)?"
  </Accordion>

  <Accordion title="83. Message Queues with RabbitMQ">
    **Answer**:

    ```javascript theme={null}
    const amqp = require('amqplib');

    class MessageQueue {
        async connect() {
            this.connection = await amqp.connect(process.env.RABBITMQ_URL);
            this.channel = await this.connection.createChannel();
            
            // Prefetch: only process 10 messages at a time per consumer
            await this.channel.prefetch(10);
            
            // Handle connection errors
            this.connection.on('error', (err) => {
                console.error('RabbitMQ connection error:', err);
                setTimeout(() => this.connect(), 5000); // Reconnect
            });
        }
        
        async publish(queue, message, options = {}) {
            await this.channel.assertQueue(queue, {
                durable: true,               // Survives broker restart
                deadLetterExchange: 'dlx',   // Failed messages go here
            });
            
            this.channel.sendToQueue(queue, Buffer.from(JSON.stringify(message)), {
                persistent: true,            // Message survives broker restart
                ...options
            });
        }
        
        async consume(queue, handler) {
            await this.channel.assertQueue(queue, { durable: true });
            
            this.channel.consume(queue, async (msg) => {
                if (!msg) return;
                
                try {
                    const data = JSON.parse(msg.content.toString());
                    await handler(data);
                    this.channel.ack(msg);   // Acknowledge: remove from queue
                } catch (err) {
                    console.error('Processing failed:', err);
                    // Reject and requeue (or send to dead letter queue)
                    this.channel.nack(msg, false, false); // Don't requeue, send to DLX
                }
            });
        }
    }

    // Usage
    const mq = new MessageQueue();
    await mq.connect();

    // Producer
    await mq.publish('email-tasks', { to: 'user@example.com', template: 'welcome' });

    // Consumer (separate process)
    await mq.consume('email-tasks', async (task) => {
        await sendEmail(task.to, task.template);
    });
    ```

    **Key concepts**: Durable queues, persistent messages, prefetch (concurrency control), acknowledgments, dead letter queues (DLQ) for failed messages.

    **What interviewers are really testing**: Do you handle message acknowledgment? Do you know about dead letter queues? Do you set prefetch limits?

    **Follow-up**:

    * "What happens if a consumer crashes while processing a message but before acknowledging it?"
    * "What is a dead letter queue and when would you use it?"
    * "How does RabbitMQ's prefetch relate to consumer concurrency?"
  </Accordion>

  <Accordion title="84. Redis Caching (Advanced Patterns)">
    **Answer**:

    ```javascript theme={null}
    const Redis = require('ioredis');
    const client = new Redis(process.env.REDIS_URL);

    // 1. Cache-aside with stampede protection
    async function getCachedData(key, fetchFn, ttlSeconds = 3600) {
        const cached = await client.get(key);
        if (cached) return JSON.parse(cached);
        
        // Stampede protection: use a lock so only one process fetches
        const lockKey = `lock:${key}`;
        const acquired = await client.set(lockKey, '1', 'EX', 10, 'NX'); // NX = only if not exists
        
        if (acquired) {
            try {
                const data = await fetchFn();
                await client.set(key, JSON.stringify(data), 'EX', ttlSeconds);
                return data;
            } finally {
                await client.del(lockKey);
            }
        } else {
            // Another process is fetching -- wait and retry
            await new Promise(resolve => setTimeout(resolve, 100));
            return getCachedData(key, fetchFn, ttlSeconds); // Retry
        }
    }

    // 2. Pub/Sub for cache invalidation across instances
    const subscriber = new Redis(process.env.REDIS_URL);
    subscriber.subscribe('cache-invalidation');
    subscriber.on('message', (channel, message) => {
        const { key } = JSON.parse(message);
        localCache.delete(key); // Invalidate local in-memory cache
    });

    // When updating data:
    async function updateUser(id, data) {
        await db.users.update(id, data);
        await client.del(`user:${id}`);
        // Notify all instances to clear their local cache
        await client.publish('cache-invalidation', JSON.stringify({ key: `user:${id}` }));
    }

    // 3. Rate limiting with sliding window (Lua for atomicity)
    const slidingWindowRateLimit = `
        local key = KEYS[1]
        local now = tonumber(ARGV[1])
        local window = tonumber(ARGV[2])
        local limit = tonumber(ARGV[3])
        
        redis.call('ZREMRANGEBYSCORE', key, 0, now - window)
        local count = redis.call('ZCARD', key)
        
        if count < limit then
            redis.call('ZADD', key, now, now .. math.random())
            redis.call('EXPIRE', key, window / 1000)
            return 1
        end
        return 0
    `;
    ```

    **What interviewers are really testing**: Do you know about cache stampede prevention? Can you use Redis beyond simple get/set?

    **Follow-up**:

    * "What is a cache stampede and how does your locking pattern prevent it?"
    * "Why use a Lua script for rate limiting instead of multiple Redis commands?"
    * "How does Redis Cluster differ from Redis Sentinel? When do you need each?"
  </Accordion>

  <Accordion title="85. Graceful Shutdown">
    **Answer**:
    Graceful shutdown ensures in-flight requests complete before the process exits. Without it, users get broken responses and database transactions get left uncommitted.

    ```javascript theme={null}
    const server = app.listen(3000);
    let isShuttingDown = false;

    // Reject new requests during shutdown
    app.use((req, res, next) => {
        if (isShuttingDown) {
            res.set('Connection', 'close');
            return res.status(503).json({ error: 'Server is shutting down' });
        }
        next();
    });

    async function gracefulShutdown(signal) {
        console.log(`${signal} received. Starting graceful shutdown...`);
        isShuttingDown = true;
        
        // 1. Stop accepting new connections
        server.close(async () => {
            console.log('HTTP server closed');
            
            try {
                // 2. Close database connections
                await mongoose.connection.close();
                console.log('Database connections closed');
                
                // 3. Close Redis connections
                await redisClient.quit();
                console.log('Redis connection closed');
                
                // 4. Close message queue connections
                await rabbitMQConnection.close();
                console.log('RabbitMQ connection closed');
                
                console.log('Graceful shutdown complete');
                process.exit(0);
            } catch (err) {
                console.error('Error during shutdown:', err);
                process.exit(1);
            }
        });
        
        // Force shutdown after timeout (don't hang forever)
        setTimeout(() => {
            console.error('Forced shutdown after timeout');
            process.exit(1);
        }, 30000); // 30 second timeout
    }

    process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
    process.on('SIGINT', () => gracefulShutdown('SIGINT'));
    ```

    **Why this matters in Kubernetes**: When a pod is terminated, Kubernetes sends SIGTERM, then waits `terminationGracePeriodSeconds` (default 30s), then sends SIGKILL. Your app must handle SIGTERM to drain connections within that window.

    **What interviewers are really testing**: Do you drain connections? Do you close resources in the right order? Do you have a force-kill timeout?

    **Red flag answer**: "I just let the process die, it restarts quickly." This drops in-flight requests and can corrupt data.

    **Follow-up**:

    * "In what order should you close resources during shutdown? Why?"
    * "What happens to WebSocket connections during graceful shutdown?"
    * "How does Kubernetes's pod termination lifecycle interact with your graceful shutdown?"
  </Accordion>

  <Accordion title="86. Health Checks (Production Pattern)">
    **Answer**:

    ```javascript theme={null}
    // Liveness check: "Is the process alive and not deadlocked?"
    app.get('/health/live', (req, res) => {
        res.status(200).json({ status: 'ok' });
    });

    // Readiness check: "Can this instance serve traffic?"
    app.get('/health/ready', async (req, res) => {
        const checks = {};
        let healthy = true;
        
        // Check database
        try {
            await pool.query('SELECT 1');
            checks.database = 'ok';
        } catch (err) {
            checks.database = 'failed';
            healthy = false;
        }
        
        // Check Redis
        try {
            await redis.ping();
            checks.redis = 'ok';
        } catch (err) {
            checks.redis = 'failed';
            healthy = false;
        }
        
        // Check memory
        const memUsage = process.memoryUsage();
        const heapPercent = memUsage.heapUsed / memUsage.heapTotal;
        checks.memory = {
            heapUsedMB: Math.round(memUsage.heapUsed / 1024 / 1024),
            heapTotalMB: Math.round(memUsage.heapTotal / 1024 / 1024),
            heapPercent: Math.round(heapPercent * 100),
        };
        if (heapPercent > 0.95) healthy = false;
        
        // Check event loop lag
        checks.uptime = Math.round(process.uptime());
        
        res.status(healthy ? 200 : 503).json({
            status: healthy ? 'healthy' : 'degraded',
            checks,
            timestamp: new Date().toISOString()
        });
    });
    ```

    **Liveness vs Readiness** (Kubernetes terminology):

    * **Liveness**: "Is the process stuck?" Failure -> Kubernetes kills and restarts the pod.
    * **Readiness**: "Can it handle traffic?" Failure -> Kubernetes removes the pod from the Service load balancer (no traffic routed) but doesn't kill it. Pod can recover and become ready again.

    **Anti-pattern**: Making the liveness check depend on external services (database, Redis). If the database is down, killing and restarting your pod won't fix it -- now you have a restart loop on top of a database outage.

    **What interviewers are really testing**: Do you separate liveness from readiness? Do you avoid the "cascading restart" anti-pattern?

    **Follow-up**:

    * "Why should the liveness check NOT depend on the database? What happens if it does?"
    * "What is a startup probe in Kubernetes and when do you need it?"
  </Accordion>

  <Accordion title="87. Request Timeout Handling">
    **Answer**:

    ```javascript theme={null}
    // 1. Server-side timeout (protect your server)
    const server = app.listen(3000);
    server.timeout = 30000;       // 30s overall timeout
    server.keepAliveTimeout = 65000; // Must be > load balancer idle timeout (usually 60s)
    server.headersTimeout = 66000;   // Must be > keepAliveTimeout

    // 2. Route-level timeout middleware
    function timeout(ms) {
        return (req, res, next) => {
            const timer = setTimeout(() => {
                if (!res.headersSent) {
                    res.status(504).json({ error: 'Request timeout' });
                }
            }, ms);
            
            res.on('finish', () => clearTimeout(timer));
            next();
        };
    }

    app.use('/api/reports', timeout(60000));  // 60s for heavy reports
    app.use('/api', timeout(10000));          // 10s default

    // 3. Outgoing request timeout (protect from slow dependencies)
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 5000);

    try {
        const response = await fetch('http://slow-service/api', {
            signal: controller.signal
        });
        clearTimeout(timeoutId);
    } catch (err) {
        if (err.name === 'AbortError') {
            console.error('Request to slow-service timed out');
        }
        throw err;
    }
    ```

    **The `keepAliveTimeout` gotcha**: If your load balancer (ALB, Nginx) has a 60s idle timeout but Node's `keepAliveTimeout` is the default 5s, Node closes the connection before the load balancer expects it, causing 502 errors. Always set `keepAliveTimeout > load_balancer_timeout`.

    **What interviewers are really testing**: Do you set timeouts on both incoming and outgoing requests? Do you know about the `keepAliveTimeout` issue?

    **Follow-up**:

    * "What is the difference between `server.timeout` and `server.keepAliveTimeout`?"
    * "How do you handle a timeout when a database query is already in progress? Does the query get cancelled?"
  </Accordion>

  <Accordion title="88. Memory Leak Detection (Production Workflow)">
    **Answer**:

    ```javascript theme={null}
    // 1. Continuous monitoring: track heap over time
    const memoryMetrics = setInterval(() => {
        const { heapUsed, heapTotal, rss, external } = process.memoryUsage();
        metrics.gauge('node.heap.used', heapUsed);
        metrics.gauge('node.heap.total', heapTotal);
        metrics.gauge('node.rss', rss);
        metrics.gauge('node.external', external);
        metrics.gauge('node.active_handles', process._getActiveHandles().length);
        metrics.gauge('node.active_requests', process._getActiveRequests().length);
    }, 10000).unref();

    // 2. On-demand heap snapshots (production-safe)
    process.on('SIGUSR2', () => {
        const v8 = require('v8');
        const path = v8.writeHeapSnapshot();
        console.log(`Heap snapshot written to ${path}`);
        // WARNING: Takes ~1-2 seconds per GB of heap. Causes a pause!
    });

    // 3. Automated leak detection (alert on monotonic growth)
    // If heapUsed grows by > 50MB over 1 hour without dropping, alert.
    ```

    **The three-snapshot workflow**:

    1. Take snapshot at baseline (after warmup).
    2. Reproduce the suspected leak (run load test for 5 minutes).
    3. Take snapshot after forced GC.
    4. Compare: sort by "Objects allocated between Snapshot 1 and 2." Look for unexpected growth in specific constructors.

    **Common findings**:

    * `(closure)` growing: Event listeners or closures holding references.
    * `(string)` growing: Unbounded string concatenation or logging.
    * `(array)` growing: Cache without eviction.
    * Specific class names growing: Code-level leak in that class.

    **What interviewers are really testing**: Do you have a systematic approach to leak detection, or do you just restart the process when memory gets high?

    **Follow-up**:

    * "Your production service's heap grows from 200MB to 1.5GB over 6 hours, then OOMs. Walk me through your debugging approach."
    * "How do you take a heap snapshot in production without impacting user requests?"
  </Accordion>

  <Accordion title="89. Native Addons with N-API">
    **Answer**:
    N-API (Node-API) provides a stable C ABI for building native addons that work across Node.js versions without recompilation.

    ```cpp theme={null}
    // addon.cc -- C++ N-API addon
    #include <napi.h>

    // CPU-intensive function implemented in C++
    Napi::Number FibonacciSync(const Napi::CallbackInfo& info) {
        Napi::Env env = info.Env();
        int n = info[0].As<Napi::Number>().Int32Value();
        
        long long a = 0, b = 1;
        for (int i = 0; i < n; i++) {
            long long temp = a;
            a = b;
            b = temp + b;
        }
        
        return Napi::Number::New(env, a);
    }

    // Async version (runs on Libuv thread pool, doesn't block event loop)
    class FibonacciWorker : public Napi::AsyncWorker {
    public:
        FibonacciWorker(Napi::Function& callback, int n)
            : Napi::AsyncWorker(callback), n(n), result(0) {}
        
        void Execute() override {
            // Runs on worker thread -- NO V8 access here!
            long long a = 0, b = 1;
            for (int i = 0; i < n; i++) {
                long long temp = a;
                a = b;
                b = temp + b;
            }
            result = a;
        }
        
        void OnOK() override {
            // Back on main thread -- can access V8
            Callback().Call({ Env().Null(), Napi::Number::New(Env(), result) });
        }
        
    private:
        int n;
        long long result;
    };

    Napi::Object Init(Napi::Env env, Napi::Object exports) {
        exports.Set("fibSync", Napi::Function::New(env, FibonacciSync));
        // ... register async version too
        return exports;
    }

    NODE_API_MODULE(addon, Init)
    ```

    **When to write native addons**:

    * Performance-critical code (100x+ speedup over JS for numerical work).
    * Interfacing with system libraries (libcurl, OpenCV, system calls).
    * Reusing existing C/C++ code.

    **Alternatives to C++**: Use **Rust** via `neon` or `napi-rs` for memory-safe native addons. Increasingly popular in the Node ecosystem.

    **What interviewers are really testing**: Do you know when native addons are justified? Do you understand the async worker pattern?

    **Follow-up**:

    * "What is the difference between N-API and the older NAN (Native Abstractions for Node)?"
    * "Why can't you access V8/JavaScript objects from within the `Execute()` method of an AsyncWorker?"
    * "When would you choose a Rust native addon over C++?"
  </Accordion>

  <Accordion title="90. Microservices Communication (Service-to-Service)">
    **Answer**:

    ```javascript theme={null}
    // Pattern 1: REST with circuit breaker
    const CircuitBreaker = require('opossum');

    const breaker = new CircuitBreaker(
        async (orderId) => {
            const res = await fetch(`http://inventory-service/api/check/${orderId}`, {
                signal: AbortSignal.timeout(3000) // 3s timeout
            });
            if (!res.ok) throw new Error(`Inventory check failed: ${res.status}`);
            return res.json();
        },
        {
            timeout: 5000,              // Consider request failed after 5s
            errorThresholdPercentage: 50, // Open circuit if 50% of requests fail
            resetTimeout: 30000,        // Try again after 30s
        }
    );

    breaker.on('open', () => console.warn('Circuit breaker OPEN: inventory service degraded'));
    breaker.on('halfOpen', () => console.info('Circuit breaker HALF-OPEN: testing inventory service'));
    breaker.on('close', () => console.info('Circuit breaker CLOSED: inventory service recovered'));

    // Usage in route handler
    app.post('/api/order', async (req, res) => {
        try {
            const inventory = await breaker.fire(req.body.orderId);
            // Process order...
        } catch (err) {
            if (err.message === 'Breaker is open') {
                return res.status(503).json({ error: 'Inventory service unavailable, try later' });
            }
            throw err;
        }
    });

    // Pattern 2: Event-driven with message queue (decoupled)
    // Order service publishes event, doesn't wait for response
    await messageQueue.publish('order.created', {
        orderId: order.id,
        items: order.items,
        userId: order.userId
    });

    // Inventory service subscribes and processes asynchronously
    await messageQueue.consume('order.created', async (event) => {
        await reserveInventory(event.items);
        await messageQueue.publish('inventory.reserved', { orderId: event.orderId });
    });
    ```

    **The circuit breaker pattern** prevents cascading failures: when a downstream service is failing, stop sending requests to it (open the circuit). This prevents your service from hanging on timeouts and using up all its connections.

    **Service mesh** (Istio, Linkerd): In production Kubernetes environments, these concerns (circuit breaking, retries, timeouts, mTLS) are often handled at the infrastructure layer via a sidecar proxy, not in application code.

    **What interviewers are really testing**: Do you know about circuit breakers? Can you choose between synchronous and event-driven communication?

    **Follow-up**:

    * "What are the three states of a circuit breaker? How does the half-open state work?"
    * "When would you use an API gateway vs direct service-to-service communication?"
    * "How does a service mesh differ from implementing resilience patterns in application code?"
  </Accordion>
</AccordionGroup>

## 8. Gap-Filling Questions (Critical Topics)

<AccordionGroup>
  <Accordion title="91. AsyncLocalStorage (Request Context Propagation)">
    **Answer**:
    `AsyncLocalStorage` (from `async_hooks` module) provides a way to store context that is **automatically propagated** through the entire async call chain of a request -- without passing it explicitly through every function parameter.

    **The problem it solves**: In a microservice handling concurrent requests, you need to associate logs, metrics, and database queries with the specific request that triggered them. Without `AsyncLocalStorage`, you'd have to pass a `requestId` through every function call.

    ```javascript theme={null}
    const { AsyncLocalStorage } = require('async_hooks');
    const crypto = require('crypto');

    const requestContext = new AsyncLocalStorage();

    // Middleware: create context for each request
    app.use((req, res, next) => {
        const context = {
            requestId: req.headers['x-request-id'] || crypto.randomUUID(),
            userId: null, // Set after auth
            startTime: Date.now(),
        };
        
        requestContext.run(context, () => next());
    });

    // Anywhere in your code -- no parameter passing needed!
    function getRequestId() {
        return requestContext.getStore()?.requestId || 'no-context';
    }

    // Logger automatically includes request context
    const logger = {
        info(msg, data = {}) {
            const ctx = requestContext.getStore() || {};
            console.log(JSON.stringify({
                level: 'info',
                requestId: ctx.requestId,
                userId: ctx.userId,
                msg,
                ...data,
                timestamp: new Date().toISOString()
            }));
        }
    };

    // Deep in your service layer -- context is available
    async function processOrder(orderData) {
        logger.info('Processing order', { orderId: orderData.id });
        // Log output includes requestId automatically!
        
        const result = await db.query('INSERT INTO orders ...');
        logger.info('Order saved', { orderId: orderData.id });
        return result;
    }
    ```

    **How it works internally**: `AsyncLocalStorage` hooks into Node's async resource tracking (`async_hooks`). When an async operation (Promise, setTimeout, I/O callback) is created within a `.run()` context, the context is automatically associated with that async operation and all its descendants.

    **Performance**: In Node 16+, `AsyncLocalStorage` has minimal overhead (\~2-5% in benchmarks). Earlier versions had higher overhead due to the `async_hooks` implementation.

    **What interviewers are really testing**: Do you know how to propagate request context without prop-drilling? This is essential for observability in production services.

    **Red flag answer**: "I pass `requestId` as a parameter to every function." This works but doesn't scale and makes function signatures noisy.

    **Follow-up**:

    * "How does `AsyncLocalStorage` differ from thread-local storage in Java? What's the Node equivalent?"
    * "What are `async_hooks` and what performance concerns do they have?"
    * "How would you use `AsyncLocalStorage` to implement distributed tracing across microservices?"
  </Accordion>

  <Accordion title="92. Node.js Security: Dependency Supply Chain Attacks">
    **Answer**:
    Supply chain attacks exploit the npm ecosystem -- compromising a package that thousands of projects depend on. This is one of the most dangerous attack vectors for Node.js applications.

    **Notable incidents**:

    * **event-stream (2018)**: Malicious code injected into a popular package (2M weekly downloads) that targeted a specific Bitcoin wallet. The attacker gained maintainer access through social engineering.
    * **ua-parser-js (2021)**: Compromised package ran cryptominers on developer machines. 8M weekly downloads affected.
    * **colors/faker (2022)**: Maintainer intentionally sabotaged their own packages in protest, breaking thousands of projects.

    **Defense layers**:

    1. **Lock files** (`package-lock.json` / `yarn.lock`): Ensures reproducible installs. Always commit lock files.

    2. **`npm audit`** / **Snyk** / **Socket.dev**: Scan dependencies for known vulnerabilities.

    ```bash theme={null}
    npm audit                     # Check for known vulnerabilities
    npm audit fix                 # Auto-fix compatible updates
    npx socket scan ./            # Detect supply chain risks (new)
    ```

    3. **Lockfile-only installs in CI**:

    ```bash theme={null}
    npm ci                        # Clean install from lockfile only (no modifications)
    # NOT `npm install` which can modify the lockfile
    ```

    4. **Pin exact versions** (debatable but safer):

    ```json theme={null}
    {
        "dependencies": {
            "express": "4.18.2"   
        }
    }
    ```

    5. **Review new dependencies**:
       * Check npm download counts, GitHub stars, maintainer history.
       * Use `npm pack` to inspect what's actually published (can differ from GitHub source).
       * Check for install scripts: `"preinstall": "node malicious.js"` is a red flag.

    6. **Disable install scripts for untrusted packages**:

    ```bash theme={null}
    npm install --ignore-scripts
    # Or in .npmrc: ignore-scripts=true
    ```

    7. **Use a private registry** (Artifactory, Verdaccio) that proxies npm and caches approved packages.

    **What interviewers are really testing**: Are you aware of supply chain risks specific to npm? Do you have a strategy beyond `npm audit`?

    **Red flag answer**: "I just run `npm install` and trust the packages." This is how supply chain attacks succeed.

    **Follow-up**:

    * "How would you detect if a dependency update introduces malicious code?"
    * "What is the difference between `npm install` and `npm ci`? Why does it matter in CI/CD?"
    * "A critical vulnerability is found in a transitive dependency 4 levels deep. How do you fix it?"
  </Accordion>

  <Accordion title="93. Node.js Signals, Process Lifecycle, and Container Integration">
    **Answer**:
    Understanding how Node.js processes interact with the OS and container orchestrators is critical for production reliability.

    **Process signals**:

    * `SIGTERM` (15): Polite "please terminate." Sent by Kubernetes, Docker, PM2. Your app should handle this for graceful shutdown.
    * `SIGINT` (2): Interrupt from terminal (Ctrl+C). Handle like SIGTERM.
    * `SIGKILL` (9): Forced kill. **Cannot be caught or handled.** The process dies immediately. Kubernetes sends this after `terminationGracePeriodSeconds`.
    * `SIGHUP` (1): Terminal hangup. Some apps use it to reload config.
    * `SIGUSR1` (10): Node uses this to activate the debugger. Don't override.
    * `SIGUSR2` (12): User-defined. Good for triggering heap snapshots.

    **The PID 1 problem in Docker**:

    ```dockerfile theme={null}
    # BAD: Node runs as PID 1, doesn't handle signals properly
    CMD ["node", "server.js"]

    # GOOD: Use tini as init system
    RUN apk add --no-cache tini
    ENTRYPOINT ["/sbin/tini", "--"]
    CMD ["node", "server.js"]

    # Or use Docker's --init flag
    # docker run --init my-app
    ```

    When Node runs as PID 1, it doesn't have default signal handlers for SIGTERM/SIGINT -- signals are ignored unless you explicitly `process.on('SIGTERM', ...)`. Tini acts as a proper init process that forwards signals correctly.

    **Container lifecycle in Kubernetes**:

    ```
    Pod scheduled -> Init containers run -> Main containers start -> Readiness probe passes -> Traffic routed
                                                                                                |
    Pod termination: SIGTERM sent -> terminationGracePeriodSeconds (default 30s) -> SIGKILL if still running
                                      |
                               Your graceful shutdown happens here
    ```

    **`beforeExit` vs `exit` events**:

    ```javascript theme={null}
    process.on('beforeExit', async (code) => {
        // Runs when event loop is empty (async work possible)
        await flushLogs();
    });

    process.on('exit', (code) => {
        // Runs synchronously just before process exits
        // NO async work here -- event loop is done
        console.log(`Process exiting with code ${code}`);
    });
    ```

    **What interviewers are really testing**: Do you know the PID 1 problem? Can you reason about the container termination lifecycle?

    **Red flag answer**: "I just deploy the Docker image and it works." Missing signal handling means dropped requests and data loss on every deployment.

    **Follow-up**:

    * "What is the PID 1 problem in Docker and how does `tini` solve it?"
    * "Your Node app in Kubernetes restarts frequently with exit code 137. What does that mean?"
    * "How do you ensure zero-downtime deployments in Kubernetes with a Node.js app?"
  </Accordion>

  <Accordion title="94. `AbortController` and Cancellation Patterns">
    **Answer**:
    `AbortController` provides a standard mechanism to cancel async operations -- network requests, database queries, timers, or any long-running work.

    ```javascript theme={null}
    // Basic usage: cancel a fetch request
    const controller = new AbortController();
    const { signal } = controller;

    setTimeout(() => controller.abort(), 5000); // 5s timeout

    try {
        const response = await fetch('https://slow-api.com/data', { signal });
        const data = await response.json();
    } catch (err) {
        if (err.name === 'AbortError') {
            console.log('Request was cancelled (timeout)');
        } else {
            throw err; // Re-throw non-cancellation errors
        }
    }

    // Convenience: AbortSignal.timeout (Node 18+)
    const response = await fetch(url, {
        signal: AbortSignal.timeout(5000) // Built-in timeout signal
    });

    // AbortSignal.any (Node 20+): cancel when ANY signal fires
    const userCancel = new AbortController();
    const timeoutSignal = AbortSignal.timeout(10000);

    const response = await fetch(url, {
        signal: AbortSignal.any([userCancel.signal, timeoutSignal])
    });

    // Custom cancellable operations
    async function processWithCancellation(data, signal) {
        for (const item of data) {
            if (signal?.aborted) {
                throw new DOMException('Operation cancelled', 'AbortError');
            }
            await processItem(item);
        }
    }

    // Cancel all operations for a request when client disconnects
    app.get('/long-operation', async (req, res) => {
        const controller = new AbortController();
        
        req.on('close', () => controller.abort()); // Client disconnected
        
        try {
            const result = await longOperation(controller.signal);
            res.json(result);
        } catch (err) {
            if (err.name === 'AbortError') {
                // Client disconnected, no response needed
                return;
            }
            throw err;
        }
    });
    ```

    **What interviewers are really testing**: Do you handle client disconnection? Do you use `AbortController` for timeouts instead of manual `setTimeout` + cleanup?

    **Red flag answer**: Not knowing `AbortController` exists, or using manual timeout patterns with potential cleanup issues.

    **Follow-up**:

    * "How do you propagate cancellation through a chain of async operations (e.g., HTTP handler -> service -> database)?"
    * "What happens to a database query that's already executing when you abort the signal?"
    * "How does `AbortSignal.any()` simplify complex cancellation scenarios?"
  </Accordion>

  <Accordion title="95. Node.js Diagnostic Channels and Modern Observability">
    **Answer**:
    `diagnostics_channel` (Node 16+) is a publish/subscribe API for diagnostic data within a Node.js application. It provides a low-overhead way to instrument code without modifying it.

    ```javascript theme={null}
    const diagnostics_channel = require('diagnostics_channel');

    // Create a channel
    const httpChannel = diagnostics_channel.channel('http.request');

    // Publisher (in your HTTP handling code)
    app.use((req, res, next) => {
        const start = process.hrtime.bigint();
        
        res.on('finish', () => {
            const duration = Number(process.hrtime.bigint() - start) / 1_000_000;
            httpChannel.publish({
                method: req.method,
                url: req.url,
                statusCode: res.statusCode,
                duration,
                contentLength: res.get('content-length'),
            });
        });
        
        next();
    });

    // Subscriber (in your monitoring/observability setup)
    httpChannel.subscribe((message) => {
        metrics.histogram('http.request.duration', message.duration, {
            method: message.method,
            status: message.statusCode,
            path: message.url,
        });
        
        if (message.duration > 1000) {
            logger.warn({ ...message }, 'Slow request detected');
        }
    });
    ```

    **Modern Node.js observability stack**:

    1. **Metrics**: `prom-client` (Prometheus) or custom metrics -> Grafana dashboards.
    2. **Tracing**: OpenTelemetry SDK -> trace requests across services.
    3. **Logging**: Pino -> stdout -> log collector -> centralized search.
    4. **Profiling**: `--inspect` + Chrome DevTools for ad-hoc, `clinic.js` for automated.

    **OpenTelemetry integration** (the emerging standard):

    ```javascript theme={null}
    const { NodeSDK } = require('@opentelemetry/sdk-node');
    const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');

    const sdk = new NodeSDK({
        serviceName: 'user-api',
        traceExporter: new OTLPTraceExporter({ url: 'http://otel-collector:4318' }),
        instrumentations: [getNodeAutoInstrumentations()]
        // Auto-instruments: http, express, pg, redis, grpc, etc.
    });

    sdk.start();
    // All HTTP requests, DB queries, Redis calls are now traced automatically
    ```

    **What interviewers are really testing**: Do you think about observability as a first-class concern? Do you know OpenTelemetry? Can you set up distributed tracing?

    **Red flag answer**: "I check the logs when something goes wrong." Reactive debugging without proactive observability means you only find problems after users report them.

    **Follow-up**:

    * "What is the difference between metrics, logs, and traces? When do you use each?"
    * "How does OpenTelemetry's auto-instrumentation work? What does it instrument in a Node.js app?"
    * "What is context propagation in distributed tracing and why is it essential for microservices?"
  </Accordion>

  <Accordion title="96. Event Loop Monitoring and `libuv` Thread Pool Tuning">
    **Answer**:
    In production, you need to actively monitor the event loop and tune the thread pool -- these are the two most impactful performance levers.

    **Event Loop Monitoring**:

    ```javascript theme={null}
    const { monitorEventLoopDelay } = require('perf_hooks');

    // High-resolution event loop delay histogram
    const histogram = monitorEventLoopDelay({ resolution: 20 }); // 20ms resolution
    histogram.enable();

    // Report every 10 seconds
    setInterval(() => {
        console.log({
            min: histogram.min / 1e6,         // Convert nanoseconds to ms
            max: histogram.max / 1e6,
            mean: histogram.mean / 1e6,
            p50: histogram.percentile(50) / 1e6,
            p99: histogram.percentile(99) / 1e6,
            stddev: histogram.stddev / 1e6,
        });
        histogram.reset();
    }, 10000).unref();

    // Healthy: P99 < 20ms
    // Warning: P99 20-100ms (something is blocking)
    // Critical: P99 > 100ms (event loop is seriously blocked)
    ```

    **Libuv Thread Pool Tuning**:

    ```bash theme={null}
    # Default: 4 threads
    UV_THREADPOOL_SIZE=4

    # For I/O-heavy apps (many concurrent file reads, DNS lookups, crypto):
    UV_THREADPOOL_SIZE=16

    # Max: 1024 (but more isn't always better -- each thread uses ~2MB stack)
    ```

    **What uses the thread pool**:

    * `fs.*` operations (all file system calls)
    * `dns.lookup()` (NOT `dns.resolve()` which uses c-ares and the network directly)
    * `crypto.pbkdf2()`, `crypto.scrypt()`, `crypto.randomBytes()`
    * `zlib` compression
    * Custom C++ addons using `uv_queue_work`

    **What does NOT use the thread pool** (uses OS kernel async directly):

    * TCP/UDP sockets (`net`, `http`, `https`)
    * `dns.resolve()`, `dns.resolveAny()` (uses c-ares library)
    * Pipes, signals, child process I/O

    **Sizing heuristic**: If you're seeing event loop lag and your workload is I/O-heavy (lots of file operations, crypto), try doubling `UV_THREADPOOL_SIZE`. Monitor the impact. Too many threads waste memory and cause context-switching overhead.

    **What interviewers are really testing**: Can you distinguish between what uses the thread pool and what uses kernel async? Can you monitor event loop health?

    **Red flag answer**: "I just set `UV_THREADPOOL_SIZE=1024` to be safe." Too many threads wastes memory and increases context switching.

    **Follow-up**:

    * "Why does `dns.lookup()` use the thread pool but `dns.resolve()` does not?"
    * "You notice your Node app's P99 latency increases when you add more concurrent `fs.readFile` calls. Why might that happen?"
    * "How does `monitorEventLoopDelay` differ from the simple `setInterval` lag detection approach?"
  </Accordion>

  <Accordion title="97. Node.js Version Management and LTS Strategy">
    **Answer**:
    Understanding Node.js release cycles is important for production decision-making and interview discussions about technical leadership.

    **Release schedule**:

    * **Even-numbered releases** (18, 20, 22): Become **LTS (Long-Term Support)**. Get 30 months of support (12 months Active LTS + 18 months Maintenance LTS).
    * **Odd-numbered releases** (19, 21, 23): **Current** release. Only supported for 6 months. Never becomes LTS. Meant for testing new features before the next LTS.

    **Version manager**: Use `nvm` (Unix) or `fnm` (cross-platform, faster) to manage multiple Node versions:

    ```bash theme={null}
    # .nvmrc or .node-version in project root
    20.11.1

    # Auto-switch on directory change (in .bashrc/.zshrc)
    nvm use
    ```

    **Key features by version** (interview-relevant):

    * **Node 18 LTS**: `fetch` API built-in, test runner (`node:test`), `AbortSignal.timeout()`.
    * **Node 20 LTS**: Stable test runner, `import.meta.resolve`, permission model (`--experimental-permission`).
    * **Node 22 LTS**: `require()` for ESM modules (experimental), WebSocket client, `glob` and `matchesGlob` in `fs`.

    **Upgrade strategy for production**:

    1. Run tests on new LTS version in CI before upgrading.
    2. Check for deprecated APIs (`node --pending-deprecation app.js`).
    3. Test native addon compatibility (C++ modules may need recompilation).
    4. Roll out to staging, monitor for 1-2 weeks, then production.

    **What interviewers are really testing**: Do you make informed decisions about Node versions? Do you stay on LTS? Can you articulate what changed between major versions?

    **Red flag answer**: "I just use whatever version comes with the Docker image" or "I'm still on Node 14." Running unsupported versions in production is a security risk.

    **Follow-up**:

    * "You're starting a new project today. Which Node.js version do you choose and why?"
    * "What is the Node.js permission model (`--experimental-permission`) and what problem does it solve?"
    * "How do you handle Node.js upgrades in a monorepo with 20 services?"
  </Accordion>
</AccordionGroup>
