Failed interviews

Receipts of questions I fumbled, kept in public so I never fumble them twice. Each one ships with the answer I should have given.

Sweatworks TypeScript Engineer May 2026

One interview · 2 questions I should have nailed

  1. What are closures?

    Where I slipped: I answered "a function inside a function." That just describes a nested function — basically a callback — not a closure. I even framed it as a function that takes a parameter, which isn't the point at all. Then I really went for it and called array methods like map, forEach and reduce "examples of closures" — they're not. Those are higher-order functions; the callback you hand them is only a closure if it captures variables from the surrounding scope. The part I missed is that a closure is defined by retaining state from its enclosing scope, not by being nested.

    The answer I should've given

    A closure is a function bundled together with references to its surrounding lexical scope. The defining trait isn't the nesting — it's that the inner function keeps access to the outer function's variables even after the outer function has returned, and can read and update that captured state across calls. A nested function (or a callback) only becomes a closure once it actually closes over (remembers) state from its enclosing scope. That retained, private state is the whole point. And array methods like map, forEach or reduce aren't closures either — they're higher-order functions (functions that take a callback); the callback is a closure only when it reaches for variables outside itself.

    function counter() {
      let count = 0;          // captured by the closure
      return () => ++count;   // remembers `count` after counter() returns
    }
    const next = counter();
    next(); // 1
    next(); // 2  ← state persisted via the closure
    ClosuresLexical scopeHigher-order functionsJavaScript
  2. Is JavaScript single-threaded or multi-threaded?

    Where I slipped: I said "single-threaded," then tried to back it up with the event loop and fumbled it. I called the event loop "a queue" and stopped there — true, but I never explained why it's a queue, how it actually works, or how that ties back to there being a single thread. A vague half-answer lands worse than a confident "yes, single-threaded" would have.

    The answer I should've given

    JavaScript runs on a single main thread with one call stack, so it executes one thing at a time. The event loop is what keeps that single thread responsive: it runs the current task to completion, while anything asynchronous — timers, I/O, fetch, DOM events — is handed off to the host (the browser, or Node via libuv), which does that work elsewhere and pushes a callback onto a queue when it's done. There are actually two tiers: the macrotask queue (setTimeout, I/O, UI events) and the microtask queue (Promise callbacks, queueMicrotask). The loop only pulls the next callback once the call stack is empty, and it drains all microtasks before the next macrotask. That ordering — and the fact that one long synchronous block freezes everything — is exactly why it's a queue and not parallel execution. For real parallelism you step outside the language: Web Workers (browser) and Worker Threads (Node) run on separate OS threads and communicate by message passing, with SharedArrayBuffer + Atomics for shared memory. So: single-threaded execution model, concurrency via the event loop, true multithreading via workers.

    console.log('1: sync');                          // runs now, on the stack
    setTimeout(() => console.log('4: macrotask'), 0); // queued as a macrotask
    Promise.resolve().then(() => console.log('3: microtask'));
    console.log('2: sync');
    // → 1, 2, 3, 4
    // stack empties → drain ALL microtasks (3) → then the next macrotask (4)
    Event loopMicrotasksWeb WorkersConcurrency
Overheard Web3 Developer interviews Jun 2026

Not an interview I sat — a question doing the rounds online that I wanted to actually understand.

  1. What is a Merkle tree?

    Where I heard it: This one isn't a question I was asked — I'm not a web3 dev and I never applied. I just kept reading people hiring for web3 roles griping that juniors couldn't explain what a Merkle tree is. So I read an article on it out of curiosity, and the idea of hashing pairs of children over and over to build a tree was too neat to leave off this page.

    What it actually is

    A Merkle tree (or hash tree) is a tree built entirely out of hashes. You split your data into chunks and hash each one — those hashes are the leaves. Then you pair the leaves up, concatenate each pair and hash it to get their parent, and repeat that one level at a time, hashing pairs of children into a single parent, until you're left with one hash at the very top: the Merkle root. That root is a compact fingerprint of the whole dataset — flip a single byte in any leaf and its hash changes, which changes its parent, and that ripples all the way up to the root. The payoff is twofold: it's tamper-evident (any change is visible at the root), and it gives cheap membership proofs. To prove one chunk is in the set you don't need the whole set — just the sibling hash at each level on the path from your leaf up to the root (a "Merkle proof"), which is only about log₂(n) hashes for n leaves. Anyone holding the trusted root can recompute it from your chunk plus those siblings and confirm it matches. That's exactly how a Bitcoin or Ethereum light client checks a transaction is in a block without downloading the whole block, and the same idea underpins Git, IPFS and Certificate Transparency. (Detail worth knowing: when a level has an odd number of nodes, most implementations — Bitcoin included — just duplicate the last hash so it can still be paired.)

    import { createHash } from 'node:crypto';
    const sha = (s) => createHash('sha256').update(s).digest('hex');
    
    // 1. hash each chunk of data → these are the leaves
    let level = ['a', 'b', 'c', 'd'].map(sha);
    
    // 2. hash pairs of children into a parent, repeat up to the top
    while (level.length > 1) {
      const next = [];
      for (let i = 0; i < level.length; i += 2) {
        const left = level[i];
        const right = level[i + 1] ?? left; // odd one out? pair it with itself
        next.push(sha(left + right));
      }
      level = next;
    }
    
    const root = level[0]; // change ANY leaf and this root changes
    Merkle treeHashingBlockchainData structures