Skip to main content

The Circuit Breaker Pattern Explained

· 4 min read
Free system design course

When a downstream service starts failing, the naive response — every caller just keeps retrying — makes things worse, not better. A circuit breaker is the pattern that stops the pileup.

Why retrying everything makes a bad situation worse

A struggling service gets hit with the same load (or more, from retries) exactly when it's least able to handle it, while every caller wastes time and resources waiting on calls that are very likely to fail anyway. A circuit breaker wraps calls to a dependency, watches the failure rate, and once failures cross a threshold, stops sending traffic to that dependency entirely for a while — failing fast instead of piling on.

The three states

The pattern borrows its name, and its state machine, directly from the electrical original:

System Design Lab
  • Closed — the normal state. Calls flow through, and the breaker tracks the failure rate.
  • Open — once failures exceed a threshold, the breaker trips: for a cooldown period, it fails every call immediately, without even attempting to reach the struggling dependency. This is the core value — protecting the caller from wasted waiting, and the callee from added load on top of whatever's already wrong.
  • Half-open — after the cooldown, the breaker cautiously lets a small number of test calls through. If they succeed, it closes and resumes normal traffic; if they still fail, it reopens and waits another cooldown before trying again.

Failing fast is the entire point

Without a breaker, a caller waiting on a slow, failing dependency ties up its own resources — connections, threads, request-handling capacity — for however long that call takes to time out. If enough callers pile up waiting the same way, the caller can fail too, purely from resource exhaustion, even though its own logic was fine. This is exactly the cascading-failure scenario a circuit breaker exists to interrupt: by failing immediately once the breaker is open, a caller gets an instant, predictable failure and can fall back to a default response, a cached value, or a graceful error, far faster than waiting out a real timeout.

Where it lives in a real system

A circuit breaker naturally wraps any single outbound call to a dependency — a service-to-service call, a database query, a call to a third-party API. It's a natural fit for an API gateway to own, since every downstream call in a system tends to flow through it anyway, making it a convenient single place to track per-dependency failure rates without instrumenting every individual service that makes the call.

Reacting vs. preventing: circuit breaker vs. rate limiting

Both are protective patterns that reject requests under stress, but they solve different problems. Rate limiting protects a service from being overwhelmed by too much legitimate demand — it's proactive, rejecting requests before they'd exceed a known-safe capacity, and doesn't require anything to actually be broken yet. A circuit breaker instead protects callers from a dependency that's already failing, reacting to observed failures rather than anticipating load. Many real systems use both together: rate limiting on the way in, circuit breakers on every outbound call to their own dependencies.

Why this matters in an interview

Any design involving a call to another service or third-party dependency benefits from naming a circuit breaker as the answer to "what happens if that dependency is slow or down." Naming the three states and the half-open recovery step specifically — not just "we'll add a circuit breaker" — signals you understand the mechanism, not just the vocabulary.

Go deeper

The full lesson expands on tuning failure thresholds and cooldown periods for real dependencies:

👉 Read the full Circuit Breaker lesson — part of the free System Design Lab course.