Stop 20 · System DesignTopics · 10/15

Circuit Breaker Pattern

Part of the Backend Developer Roadmap

In short

When a service you depend on is down, hammering it with retries only makes things worse for both of you. A circuit breaker stops calling, fails fast, and checks back later.

A pattern that stops calling a failing downstream service after repeated failures, failing fast instead of piling up slow, doomed requests — preventing one failing service from cascading into a system-wide outage.

The purple blob pushes up a tripped breaker switch in a home electrical panel.

Think of it like this

The MCB in your house

When a faulty heater shorts, the MCB trips and cuts power to that circuit before the wiring burns. You don't keep flicking the switch. Later you push the MCB back up to test. If it trips again, the heater's still faulty. A circuit breaker in code works exactly like this.

Watch it happen

The three breaker states

Closed
Open
Half-open

1/5Closed: calls flow to the payments service normally, and the breaker counts failures.

The big ideas

What you need to know

Fail fast

When open, calls return an error or fallback straight away instead of waiting for a timeout. Your server stays responsive.

Have a fallback

Show cached prices, queue the order for later, or show a clear 'try again'. Anything beats hanging.

Timeouts first

A breaker counts failures, and a call with no timeout never fails, it just hangs. Always set timeouts.

Watch the state

Log or graph state changes. An open breaker is an early alarm that a dependency is unhealthy.

See it in code

Walk through the code

ts
1import CircuitBreaker from "opossum";2 3async function chargeCard(orderId: string) {4  const res = await fetch(`http://payments/charge/${orderId}`, { method: "POST" });5  if (!res.ok) throw new Error(`payments ${res.status}`);6  return res.json();7}8 9const breaker = new CircuitBreaker(chargeCard, {10  timeout: 2000,                 // slower than 2 s counts as a failure11  errorThresholdPercentage: 50,  // open when half the calls fail12  resetTimeout: 30_000,          // try again after 30 s13});14breaker.fallback(() => ({ status: "queued" }));15breaker.on("open", () => logger.warn("payments breaker OPEN"));16const result = await breaker.fire("order_981");

1/4Wrap the risky call and set the timeout, failure threshold and cool-down.

opossum is a popular circuit breaker library for Node. Service meshes like Istio can apply the same idea outside your code.

Save yourself the pain

Common mistakes

Oops

Retrying failed calls immediately in a tight loop.

Do this instead

That multiplies load on a struggling service. Retry a few times with backoff and jitter, behind a breaker.

Oops

One breaker shared by every downstream service.

Do this instead

Payments failing would then block emails too. Use one breaker per dependency.

Your mission

Trip a breaker

~30 min

Quick check

The breaker is open. What happens to a new call?

Go deeper

  1. 1Diagram the three circuit breaker states: closed, open, half-open.
  2. 2Implement a basic circuit breaker around a flaky external API call.
  3. 3Understand this pattern specifically prevents cascading failures across services.

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge