What Is the Circuit Breaker Pattern?
The circuit breaker is a resilience pattern that stops an app from hammering a failing dependency. Like an electrical breaker, it trips after too many failures and fails fast for a while — letting the struggling service recover and stopping one outage from cascading.
Definition
The circuit breaker pattern stops an application from repeatedly calling a failing dependency. It counts failures and, once they cross a threshold, 'trips' — failing fast for a cooldown window instead of making the call — so the struggling service can recover and one outage does not cascade into many.
The name is borrowed from electrics on purpose. A household breaker sits between the mains and your appliances; when it senses a fault it opens the circuit so a short does not burn the house down. A software circuit breaker does the same job between a caller and a remote dependency. You wrap the risky call — a database query, an HTTP request to another service, a payment gateway — in an object that watches its outcomes. While the calls succeed, the breaker stays out of the way. When they start failing, it opens and short-circuits every subsequent call, returning immediately instead of waiting on a service that cannot answer.
The pattern was popularized by Michael Nygard in his book Release It! and then hardened at scale by Netflix, whose Hystrix library made circuit breakers a default building block for microservices. Today it is a named entry in every major architecture catalogue, including Microsoft's cloud design patterns. The reason it earned that status is simple: in a distributed system, the failure you have to design for is not a clean crash but a slow dependency, and the circuit breaker is the cheapest reliable defense against it.
The problem it solves: cascading failure
Picture a checkout service that calls a pricing service on every request. One day the pricing service gets slow — not down, just slow, taking eight seconds instead of eighty milliseconds. Checkout has no breaker, so every incoming request dutifully calls pricing and blocks waiting for a response. Threads pile up. Connection pools drain. Within a minute, checkout has hundreds of requests all parked on a call that will time out, and it can no longer answer anything — including the healthy requests that never needed pricing at all. The outage has spread from one service to its callers, and from there to their callers. That is a cascading failure, and it is how a small local problem becomes a full site outage.
Retries make it worse. A caller that retries a failing dependency multiplies the load on a service that is already struggling — the classic retry storm. The Google SRE book's chapter on handling overload makes the same point from the server's side: under pressure, the kindest thing a system can do is shed load and fail fast rather than accept work it cannot complete. A circuit breaker is the client-side half of that discipline. By opening, it converts a slow, resource-consuming failure into an instant, resource-free one — and it stops sending the retries that were keeping the dependency on the floor.
The three states
Martin Fowler's description is the canonical one, and it has exactly three states. Understanding the transitions between them is the whole pattern.
- CLOSED — the normal state. Calls pass straight through to the dependency, and the breaker tallies failures. As long as the failure rate stays under the threshold, nothing changes.
- OPEN — the tripped state. Once failures exceed the threshold, the breaker opens. Every call now fails immediately, without touching the dependency, for the length of a cooldown timeout. This is the fail-fast window that protects both caller and callee.
- HALF-OPEN — the probe state. When the cooldown expires, the breaker does not fling all traffic back at the dependency at once. Instead it lets a small number of trial calls through. If they succeed, it assumes the dependency has recovered and returns to CLOSED. If any fail, it snaps back to OPEN and restarts the cooldown.
The half-open state is what makes the breaker self-healing. Without it you would need a human to decide the dependency is back and flip the switch. With it, the breaker probes cautiously, recovers on its own when the trial calls pass, and re-trips harmlessly if it probed too early. The cost of a wrong guess is just a handful of trial requests, not another flood.
# Pseudocode: the core state logic of a circuit breaker.
class CircuitBreaker:
def __init__(self, threshold=5, cooldown=30, trial_calls=1):
self.threshold = threshold # failures before we trip
self.cooldown = cooldown # seconds to stay OPEN
self.trial_calls = trial_calls # probes allowed in HALF_OPEN
self.state = "CLOSED"
self.failures = 0
self.opened_at = None
def call(self, fn):
if self.state == "OPEN":
if now() - self.opened_at >= self.cooldown:
self.state = "HALF_OPEN" # cooldown done -> probe
else:
return fallback() # fail fast, don't call
try:
result = fn() # actually hit the dependency
except Exception:
self._on_failure()
return fallback()
self._on_success()
return result
def _on_success(self):
self.failures = 0
self.state = "CLOSED" # HALF_OPEN probe passed -> close
def _on_failure(self):
self.failures += 1
if self.state == "HALF_OPEN" or self.failures >= self.threshold:
self.state = "OPEN" # trip (or re-trip)
self.opened_at = now()Key parameters and what to do when open
A circuit breaker is only as good as its tuning, and there are three knobs. The failure threshold decides how many failures (or what failure rate over a window) trips the breaker. The timeout / cooldown decides how long it stays open before probing. The half-open trial count decides how many probe calls it lets through to test recovery. Set the threshold too low and the breaker trips on normal noise; set the cooldown too long and you keep failing requests that would now succeed; set it too short and you re-open under a load the dependency still cannot take.
Equally important is what your code does while the breaker is open. Failing fast is the floor, but you usually want to fail gracefully: return a cached or default value, degrade the feature (hide the recommendations panel rather than error the whole page), or queue the work for later. A well-designed fallback turns an open breaker from a visible error into a quietly reduced experience — the user gets a slightly worse page instead of a broken one.
Related patterns and how they combine
The circuit breaker rarely works alone. Timeouts are its prerequisite — a breaker cannot count a failure it never detects, so every wrapped call needs a bounded timeout or the caller blocks forever. Retries with backoff handle the transient blips a breaker should ignore, but they must sit inside the breaker: once it opens, the retries stop, or you recreate the retry storm the breaker was meant to prevent. Bulkheads isolate resources so one saturated dependency cannot drain the thread pool that serves everything else. And rate limiting caps how much load reaches a service in the first place — where a breaker reacts to failure, a rate limiter prevents the overload that causes it. Used together, they form a layered defense: rate limit the front door, bulkhead the resources, time out the calls, retry the blips, and break the circuit when a dependency is genuinely down.
Why it needs observability — and how it relates to MTTR
You cannot tune a breaker you cannot see. Getting the threshold and cooldown right depends on knowing your dependency's real failure rate, latency distribution, and recovery time — which means the breaker's state transitions have to be instrumented and graphed. Every mature implementation emits metrics for exactly this reason: how often the breaker opens, how long it stays open, how many probes fail. That is a straight observability problem, and it is the difference between a breaker that protects you and one you set once and forget.
The payoff shows up in mean time to recovery (MTTR). By failing fast and recovering automatically through the half-open probe, a breaker shrinks the blast radius of a dependency outage and often resolves it with no human involved — the incident that would have taken an hour of firefighting becomes a blip the system rode out on its own. Cascading-failure prevention is MTTR reduction: the fewer services an outage takes down, the less there is to bring back up.
Where BugMojo fits
Resilience is ultimately about what the user experiences when something breaks. A breaker that opens cleanly and serves a fallback should look almost invisible; a breaker that trips wrongly, or a fallback that is subtly broken, produces a degraded page that is hard to reproduce after the fact. That is where session capture earns its place: recording the session during a partial outage shows exactly what the user saw — which panel went blank, which value went stale, whether the fallback actually rendered — so you can tell a graceful degradation from a silent failure. The breaker keeps the system standing; the captured session tells you whether it kept the experience intact.
When a breaker trips and the page degrades, BugMojo captures the session — rrweb replay, console, and network — so you can tell a graceful fallback from a silent failure, and hand the whole bundle to your AI agent over MCP.
Install the extensionFrequently asked questions
Frequently asked questions
Sources
- CircuitBreaker — the three-state pattern and its state transitions — martinfowler.com (2026)
- Circuit breaker design pattern — states, parameters, and variants — Wikipedia (2026)
- Circuit Breaker pattern — cloud design patterns for resilient services — Microsoft Learn (2026)
- Handling Overload — failing fast and load shedding under pressure — Google SRE Book (2026)
Get bug-tracking insights, weekly.
Engineering deep-dives, QA playbooks, and honest tool comparisons. No spam — unsubscribe in one click.

