Question presented to candidate: "You are reviewing a pull request with five levels of nested callbacks, each indented further than the last. What is this pattern called, why is it a problem, and what would you ask the author to change?"
What a strong answer should cover:
- 📌 Callback hell (also called the "pyramid of doom") is the deep, rightward-drifting nesting that results from chaining several asynchronous steps using plain callbacks, where each step's callback is defined inside the previous one.
- The problem is not merely aesthetic indentation — it is that nested callbacks make error handling, control flow, and variable scoping all harder to follow: there is no single place to catch an error from any step, and reasoning about "what runs after what" requires mentally tracing the nesting.
- Promises flatten the nesting into a chain (
.then().then().then()) with a single.catch()for errors from any step, at the cost of still reading somewhat differently from synchronous code. - async/await flattens it further into code that reads like ordinary synchronous, top-to-bottom logic, with a normal
try/catcharound the whole sequence — this is the modern default recommendation. - The three approaches are not different capabilities, only different shapes of the same underlying async behavior — a good answer proves this rather than asserting it, showing that a nested-callback version, a Promise-chain version, and an async/await version of the same 3-step sequence produce identical results.
- Callback hell is not inherent to using callbacks at all — a single callback, or even two independent (non-nested) callbacks, is not a problem. The issue specifically arises from serial dependency chains expressed through nesting rather than composition.
Clarifying questions expected:
- "Are these steps genuinely sequential (each depends on the previous result), or could some run in parallel?" — a parallel case calls for
Promise.all, not just flattened sequentialawaits. - "Is this in a codebase that can adopt async/await, or does it need to stay compatible with an older callback-based API?" — decides whether promisifying the underlying API is part of the fix.
Code / implementation expected: Yes — the same 3-step sequence written all three ways, with confirmation they behave identically, is the clearest demonstration.