Free — no signup required

Heisenbugs — The Observer-Dependent Failures

4 min read

What Is a Heisenbug?

You're driving your car and it makes a strange rattling noise. You take it to the mechanic. The mechanic listens carefully... and the noise is gone. You drive home, and the noise comes back. The act of having the mechanic observe the car somehow changed the conditions enough to suppress the symptom. That's a Heisenbug.

A Heisenbug is a software defect that disappears or changes behavior when you attempt to observe or debug it. The name comes from Werner Heisenberg's Uncertainty Principle in quantum mechanics: the act of measuring a particle's position changes its momentum. In software, the act of adding logging, attaching a debugger, or changing the execution environment alters timing, memory layout, or optimization behavior — and the bug vanishes.

Why do Heisenbugs exist? They almost always involve one of these root causes:

  1. Race conditions: Two threads or processes access shared state without proper synchronization. The bug depends on the exact timing of operations. Adding a debugger slows execution, changing the timing, and the race no longer triggers.

  2. Uninitialized memory: A variable reads whatever garbage was in memory. In debug mode, many compilers zero-initialize memory, so the bug disappears.

  3. Compiler optimizations: Release builds optimize code differently than debug builds. A variable might be kept in a CPU register in release mode but written to memory in debug mode, changing behavior.

  4. Environment sensitivity: The bug depends on network latency, CPU load, disk I/O speed, or the exact order of packet arrival — all of which change between your laptop and production.

The debugging strategy for Heisenbugs is fundamentally different from Bohrbugs:

Technique Bohrbug Heisenbug
Attach debugger ✅ Works great ❌ Changes timing, hides bug
Add print statements ✅ Helpful ⚠️ May change timing enough to hide bug
Distributed tracing ✅ Useful ✅ Essential — low overhead, captures timing
Core dumps / post-mortem ✅ Useful ✅ Essential — captures state at crash time
Reasoning about concurrency Optional ✅ Required
Stress testing / chaos engineering Optional ✅ Helps reproduce timing-dependent failures

A classic example: two processes appending to the same file at once, with no locking between them. Run it once and you might get exactly the line count you expect. Run it again and a handful of lines are missing — not because the writes were lost outright, but because two unsynchronized appends occasionally interleave badly at the OS level. The count you get depends on timing that varies run to run. That unpredictability, tied directly to how closely you're watching, is the Heisenbug signature.

The most insidious property of Heisenbugs is that they erode trust. When a team says "it works on my machine" or "we can't reproduce it," they're often facing a Heisenbug. The failure is real — it happened in production — but the investigation environment is different enough to suppress it. Senior engineers respond to this situation not by trying harder to reproduce it with a debugger, but by switching to passive observation: examining logs, traces, metrics, and core dumps that were captured during the actual failure, without altering the system's behavior.

Interview Tip

A common SRE interview question is: "A service crashes intermittently in production but never in staging. How do you investigate?" The strong answer structure is: (1) Check for environmental differences — load, data volume, network topology, (2) Enable distributed tracing with minimal overhead rather than adding verbose debug logging, (3) Configure core dumps on crash for post-mortem analysis, (4) Hypothesize about race conditions or resource exhaustion and design targeted experiments using tools like ThreadSanitizer. Mentioning the term "Heisenbug" and explaining why traditional debugging fails demonstrates systems thinking that interviewers at senior levels specifically look for.

Key Point: The defining characteristic of a Heisenbug is not that it's rare — it's that the observation method changes the outcome. If a bug is simply hard to reproduce but behaves the same way whether or not you're watching, it's a Bohrbug with a complex trigger, not a Heisenbug. The distinction matters because it determines whether your debugger is a useful tool or an active hindrance. Misidentifying a Heisenbug as a Bohrbug leads teams to spend weeks trying to reproduce something that their investigation method is actively suppressing.

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free