You're handed a bug that reproduces reliably in production, but it vanishes the moment you attach a debugger or add log statements. How do you reason about what's happening and how do you actually catch it?
technical-conceptual · Mid level · software-engineering
What the interviewer is really asking
Assesses understanding that observation can change timing-sensitive behavior (a heisenbug), and whether the engineer can choose investigation techniques that don't perturb the system. Probes reasoning about concurrency and tooling, not memorized definitions.
What to say
- Recognize the signature of a heisenbug: a debugger breakpoint or an added log changes the timing, and that perturbation hides timing-sensitive defects like race conditions, uninitialized memory, or behavior that depended on a compiler optimization.
- Switch to low-perturbation observation: ring-buffer or async logging that doesn't serialize threads, sampling, or a record-and-replay / time-travel debugger so you can inspect the failing run without altering its timing.
- Once you suspect a race, attack the interleaving directly rather than the symptom: stress the timing (thread sanitizers, injected delays, fuzzing the schedule) to make the bug more frequent, then fix the actual synchronization or shared-state contract.
What to avoid
- Concluding the bug is intermittent or unreproducible and moving on, when it's actually reliable until you observe it.
- Leaving an added sleep or extra log line in place because it makes the failure stop, which masks a real race instead of fixing it.
- Assuming it must be a single-threaded logic error and never considering concurrency, memory, or optimization effects as the cause.
Example answers
Strong: That pattern says the act of observing is changing the timing, so it's likely a race or timing-dependent bug. I'd stop using breakpoints and switch to a lock-free async log or a record-replay debugger that captures the run without serializing threads. Then I'd try to make it worse, not better, by running a thread sanitizer and injecting small delays around the suspect shared state to amplify the bad interleaving, and fix the synchronization once I can see it.
Weak: If it goes away under the debugger it's probably a fluke or a build issue, so I'd just add a small delay or extra logging where it crashes since that seems to stabilize it, and keep an eye on whether it comes back.