What it is
These questions hand you a function that looks reasonable, already has tests that pass, and fails in a specific, described way — a slightly wrong sort order, a value that only misbehaves in one timezone, a job that runs fine for months and then crashes. There is no algorithm to design; the skill is reading code the way you'd read someone else's pull request, forming a hypothesis from the symptom, and tracing execution until the hypothesis is confirmed or discarded. The categories repeat: language-semantics traps (identity vs. equality, mutable defaults, late-binding closures), numeric edge cases (overflow, floating point), and structural mistakes (mutating a collection while iterating it, shallow-copying nested data).
When it applies
- You're given code plus a described symptom (not a spec to implement from scratch) and asked to explain and fix it.
- The bug is language-semantics or edge-case driven rather than algorithmically wrong — the approach is fine, one assumption underneath it is not.
- The existing tests pass, which is itself a clue: the bug lives in a case the tests don't exercise, not in the code path they do.
- If the code is simply the wrong algorithm for the problem (wrong complexity, wrong approach entirely), that's a design or complexity discussion, not a debugging one — debugging problems keep the shape of the solution and fix one specific defect in it.
Tells in the problem
- Code is handed to you already written, with a request to "explain the symptom and fix it" rather than "implement."
- The symptom is described precisely and differs across environments, inputs, or time ("works in the US, fails in Mumbai"; "fine for months, then one input crashes"; "passes in CI, fails in production").
- A detail seems irrelevant until you realize it's the whole answer — a language port, a container timezone, a chunk size, two adjacent items in a list.
- The existing unit tests are shown and explicitly said to pass, which is a hint that the failing case is one they don't cover.
- Small inputs behave correctly and something about scale, repetition, or a rare data shape triggers the failure.
The classic mistake
Interviewers commonly report candidates patching the symptom instead of the cause: adding a special case for the one example given, or wrapping the failure in a try/except, without forming a mental model of why the original code breaks. This surfaces immediately when the interviewer varies the example slightly — the patch doesn't generalize because it was never connected to the mechanism. The fix is to state the hypothesis out loud before touching code ("I think this fails because X compares by identity, not equality — let me check by predicting what happens on this modified input"), and only edit once tracing through the code confirms it. A correct fix should also make you able to say, in one sentence, why the given tests didn't already catch it.
Questions to start with
- Mutable Default Argument — a classic Python-specific trap where the bug is invisible until the function is called more than once.
- is vs == — shows how a passing test can hide a bug that only appears once real (non-interned) input arrives.
- Modifying a List While Iterating — a structural bug where the failure pattern (adjacent items) is itself the clue to the mechanism.
- Binary Search Off-by-One — trains you to state the loop invariant explicitly, the habit that catches most off-by-one bugs before you run anything.
- Timezone-Naive Datetime — a multi-environment symptom that requires connecting two different numbers (a lead and a lag) back to one root cause.