DEV Community

Timevolt
Timevolt

Posted on

The 2‑Pass, 20‑Minute Drill That Got Me Through FAANG Interviews

The 2‑Pass, 20‑Minute Drill That Got Me Through FAANG Interviews

Quick context (why you're writing this)

I still remember the first mock interview I did three months out from my target date. The interviewer threw a medium‑difficulty array problem at me, I stared at the blank whiteboard for what felt like forever, and when I finally started coding I kept second‑guessing every line. I ended up with a working solution but ran out of time to talk through the complexity, and the feedback was “you solved it, but you didn’t show the thought process.”

That moment hit me hard: knowing the answer isn’t enough; you have to show how you get there, and you have to do it fast enough to leave room for discussion. I tried grinding hundreds of LeetCode problems, but I kept getting stuck in the same loop—write a solution, realize it’s messy, scrap it, start over, and run out of time.

After a few frustrating weeks I stumbled on a simple tweak that changed everything: treat each practice problem as two distinct passes, each with its own time box. It’s not a magic bullet, but it forced me to separate “getting it working” from “making it interview‑ready,” and that’s where the real improvement lived.

The Insight

The core idea is dead simple:

Set a timer for 20 minutes. Spend the first 10 minutes aiming for any correct solution that passes the given test cases. Use the remaining 10 minutes to refactor, handle edge cases, and articulate the time/space complexity out loud.

Why does this work?

  1. Pressure simulation – Real interviews give you roughly 45 minutes for a problem, but a lot of that is spent talking. By imposing a hard 10‑minute “get it done” limit, you train yourself to produce a working baseline quickly, just like you’d do when the interviewer is watching.
  2. Separation of concerns – When you try to optimize while you’re still figuring out the algorithm, you end up tangled. The first pass lets you focus purely on correctness; the second pass lets you shift to cleanliness and communication without the fear of breaking a working solution.
  3. Deliberate feedback loop – After each 20‑minute block you immediately review: Did you finish the first pass? What took longer than expected? Did the second pass actually improve readability or just add noise? That tight loop surfaces weaknesses far faster than endless solo grinding.

It’s not about the number of problems you solve; it’s about how you solve each one under a realistic constraint.

How (with code)

Let’s walk through a concrete example: Longest Substring Without Repeating Characters (LeetCode 3). I’ll show the kind of first‑pass mess I used to write, then the cleaned‑up second pass.

First pass – “just make it work” (≈10 min)

function lengthOfLongestSubstring(s) {
  let max = 0;
  for (let i = 0; i < s.length; i++) {
    const seen = new Set();
    for (let j = i; j < s.length; j++) {
      if (seen.has(s[j])) break;
      seen.add(s[j]);
      max = Math.max(max, j - i + 1);
    }
  }
  return max;
}
Enter fullscreen mode Exit fullscreen mode

What’s happening here?

  • I’m brute‑forcing every start index i and expanding until I hit a duplicate.
  • It’s O(n²) time, O(k) space (where k is the size of the current window).
  • It passes the basic test cases ("abcabcbb" → 3, "bbbbb" → 1, "pwwkew" → 3).

Common mistake I made in this pass:

I kept resetting max inside the inner loop by accident, which gave wrong answers on strings like "dvdf". The fix was simply moving the max = Math.max(...) line outside the if check, but I only noticed it after running the extra test cases the interviewer would have thrown in.

Second pass – “make it interview ready” (≈10 min)

Now I take the working baseline and refactor to the optimal sliding‑window solution, while talking through my reasoning.

function lengthOfLongestSubstring(s) {
  const charIndex = new Map(); // stores most recent index of each char
  let left = 0;                // start of the current window
  let maxLen = 0;

  for (let right = 0; right < s.length; right++) {
    const ch = s[right];
    // If we've seen ch inside the current window, move left just past it
    if (charIndex.has(ch) && charIndex.get(ch) >= left) {
      left = charIndex.get(ch) + 1;
    }
    charIndex.set(ch, right);
    maxLen = Math.max(maxLen, right - left + 1);
  }
  return maxLen;
}
Enter fullscreen mode Exit fullscreen mode

Why this is better:

  • Single pass → O(n) time.
  • The map gives O(1) look‑ups for the last occurrence, so we never need the inner loop.
  • The variables left, right, and maxLen are named to match the sliding‑window intuition, which makes it easy to explain aloud.

Typical slip‑ups in the second pass:

  • Forgetting the && charIndex.get(ch) >= left check, which incorrectly shrinks the window when the duplicate is outside the current range (e.g., "abba").
  • Off‑by‑one when updating left (left = charIndex.get(ch) vs +1).

What the drill looks like in practice

  1. Timer starts – I read the problem, clarify any ambiguous edge cases out loud (even if I’m alone).
  2. First 10 minutes – I write the first version, run the given examples, and make sure it passes. I don’t worry about style or complexity yet.
  3. Timer buzzes – I stop, take a quick breath, and note what took longer than expected (e.g., “I spent 4 minutes debugging the inner loop”).
  4. Second 10 minutes – I refactor, aiming for readability and optimal complexity, while narrating each step: “Now I’m going to use a hash map to remember the last index…”.
  5. Immediate review – I run a few extra test cases (including edge cases like empty string, all same characters, Unicode) and ask myself: Did I communicate the approach clearly? Did I mention time/space?

That’s it. No fancy frameworks, no endless list of “top 10 patterns.” Just a repeatable, time‑boxed habit that forces you to be both fast and clear.

Why This Matters

After a month of doing this drill three to four times a week, my mock interview scores jumped from “barely passes” to “strong hire”. The biggest shift wasn’t that I knew more algorithms—it was that I could get a working solution on the board within 8 minutes and still have 12 minutes left to talk through trade-offs, ask clarifying questions, and write clean code. Interviewers noticed the confidence; they started asking follow‑up questions about alternative approaches instead of just checking if I got the right answer.

The trade‑off? You won’t finish every problem in 20 minutes, especially the hard ones. That’s okay. The goal isn’t to solve everything instantly; it’s to build the habit of producing a baseline quickly and then improving it deliberately. Over time, your baseline gets better, and your polishing phase shrinks.

If you’re still skeptical, try it on a single problem today and compare how you felt versus your usual “just keep coding until it works” approach.

Actionable next step

Pick a LeetCode medium problem you’ve seen before but never felt comfortable with. Set a timer for 20 minutes and follow the two‑pass rule exactly as written above. When the timer ends, write a quick note: Did you finish the first pass? What was the biggest time sink? Did the second pass actually make the code clearer or just longer?

Then come back here and drop your observation in the comments. I’ll read them and we can compare notes—because the only way to know if this works for you is to try it, not just read about it.

Happy coding, and may your whiteboard stay dry enough for second passes. 🚀

Top comments (0)