Skip to the chapter
←System DesignGGroundworkSDSystem Design
System Design/Intermediate/I7
14 / 36 written chapters
/

The cover
  1. B1The mental model
  2. B2Client-server basics
  3. B3Vertical vs horizontal
  4. B4Databases in design
  5. B5Caching fundamentals
  6. B6APIs & communication
  7. B7Walkthrough: URL shortener
  1. I1Load balancing in depth
  2. I2CDNs
  3. I3Message queues & async
  4. I4Consistency models
  5. I5Replication
  6. I6Rate limiting & throttling
  7. I7Designing for availability
  8. I8Storage systems
  9. I9Search systems
  10. I10Microservices
  11. I11Security basics
  12. I12Walkthrough: feed/chat
  13. I13Walkthrough: notifications
  1. A1CAP theorem in depth
  2. A2Transactions and isolation
  3. A3Storage engines
  4. A4Sharding at scale
  5. A5Time, ordering and IDs
  6. A6Distributed consensus
  7. A7Batch & stream processing
  8. A8Fault tolerance
  9. A9Observability at scale
  10. A10Release safety
  11. A11Capacity estimation
  12. A12Case studies
  13. A13Walkthrough: key-value store
  14. A14Walkthrough: web crawler
  15. A15Walkthrough: payments
  16. A16Tradeoff thinking

SDI7 · Part 2 · Intermediate

Designing for availability

Availability multiplies down your dependency chain — five nines in one box is four nines in a system.

19 min read3 diagrams11 sections0 exercises
14 of 36

In this chapter

  • Availability is arithmetic, and the arithmetic is unforgiving
  • What the nines actually cost you in wall-clock time
  • Redundancy: active-active vs active-passive
  • Failover, and the three ways it betrays you
  • Health checks are a design decision, not a checkbox

and 6 more

about 19 min
11On this page
  1. Availability is arithmetic, and the arithmetic is unforgiving
  2. What the nines actually cost you in wall-clock time
  3. Redundancy: active-active vs active-passive
  4. Failover, and the three ways it betrays you
  5. Health checks are a design decision, not a checkbox
  6. Graceful degradation: shrinking the blast radius on purpose
  7. Hunting single points of failure
  8. Multi-AZ vs multi-region, honestly costed
  9. Limiting blast radius and recovering from disaster
  10. SLA, SLO, SLI and the error budget
  11. Recognizing it in an unseen problem

Availability is arithmetic, and the arithmetic is unforgiving

Every candidate says "I'll make it highly available." Almost nobody can say what that costs. Availability is a measured number — the fraction of requests (or of wall-clock time) during which the system did the thing it promised — and the single most valuable fact about it is that it multiplies down a serial dependency chain. Your service cannot be more available than the product of everything it must reach to answer a request. Show that multiplication on the whiteboard unprompted and you have signalled seniority in about fifteen seconds.

your API auth profiles social graph media meta payments 99.9% 99.9% 99.9% 99.9% 99.9% 0.999 × 0.999 × 0.999 × 0.999 × 0.999 = 0.99501 = 99.50% → 43.7 hours a year, from five that each promised 8.8 make one of them optional and its 0.1% stops counting: 99.60%
Serial dependencies multiply. Adding a sixth 99.9% service costs you another 8.8 hours a year before you have written a line of your own code.

Dry run: adding one 99.9% dependency at a time

StepDependency addedRunning productAvailabilityDowntime / year
0Your own tier alone0.99999.90%8.8 h
1+ auth (99.9%)0.999 × 0.999 = 0.99800199.80%17.5 h
2+ profiles (99.9%)× 0.999 = 0.99700399.70%26.3 h
3+ social graph (99.9%)× 0.999 = 0.99600699.60%35.0 h
4+ media meta (99.9%)× 0.999 = 0.99501099.50%43.7 h
5+ payments (99.9%)× 0.999 = 0.99401599.40%52.5 h
6Make payments optional (degrade instead of hard-fail)back to step 4's product99.50%43.7 h

(Downtime figures use a 365.25-day year, 8,766 hours.) Each additional hard dependency costs roughly another 8-9 hours a year at three nines, regardless of which one it is — the multiplication doesn't care about names. Step 6 is the only row that recovers availability, and it does it without touching a single server: converting payments from a hard dependency to an optional one (queue the order, confirm asynchronously) simply removes its factor from the product.

Include your own tier in that product and five hard dependencies at three nines put you at 99.40% — about 52 hours a year. Notice what that means: you cannot buy availability you did not design for. If the target is four nines end-to-end, either every hop is five nines (expensive) or most hops stop being hard dependencies (design work).

The parallel case runs the other way: if any one of n redundant copies is enough, availability is 1 − (1 − a)n, so two independent 99.9% copies give 99.9999% on paper. The caveat is the word independent. Copies that share a deploy pipeline, a config push, a region, a dependency or a bug fail together, so the real figure is far lower than the formula, which is why shared-fate failures dominate real outages. Availability is also MTBF / (MTBF + MTTR) (mean time between failures over that plus mean time to repair), which shows the two levers: fail less often, or recover faster. Automatic recovery attacks MTTR, which is usually the cheaper lever.

What the nines actually cost you in wall-clock time

AvailabilityDowntime / yearDowntime / monthWhat it realistically implies
99%3.65 days7.3 hoursOne box, business-hours on-call, deploys cause outages
99.9% ("three nines")8.8 hours43.8 minutesRedundant instances, automated deploys, someone paged 24/7
99.95%4.4 hours21.9 minutesMulti-AZ, health-checked failover, tested rollbacks
99.99% ("four nines")52.6 minutes4.4 minutesNo manual step in the recovery path — humans cannot respond that fast
99.999% ("five nines")5.3 minutes26 secondsMulti-region active-active, no dependency below five nines, very few systems truly have it

Read the four-nines row again. 52 minutes a year is your entire budget for every bad deploy, every dependency incident, every certificate expiry and every disk failure combined. A single human paging in, opening a laptop and finding the runbook has already spent a quarter of the annual budget. That is the real threshold: at four nines and above, recovery has to be automatic.

⚠ "Availability" measured over a year hides everything that matters A service that is down for 45 minutes once still reports 99.99% for the year. Users experienced a total outage. Interviewers who work on real systems care about the distribution, not the average — say you would measure availability as successful requests / total requests bucketed per minute, so a short total outage shows up as a cliff rather than being smeared into a rounding error.

Redundancy: active-active vs active-passive

Redundancy is the only actual mechanism for availability; everything else is plumbing around it. The design question is whether the spare capacity is serving traffic right now.

active-active active-passive LB / DNS LB / DNS serving serving serving standby failure is a capacity event each node must run under 50% failure is a state transition tens of seconds to minutes down the standby path is the one that is never exercised
Active-active degrades; active-passive switches. Switching is a discrete event that can fail, which is why the passive side needs deliberate, scheduled exercise.
Active-activeActive-passive
Failure behaviourRemaining nodes absorb the load — no transitionDetect, promote, redirect — a transition that can fail
Recovery timeEffectively zero (a few in-flight requests)Detection window + promotion + DNS/connection churn: commonly tens of seconds to a few minutes (planning range, measure yours)
CostYou pay for N+1 but you use itYou pay for the standby and get nothing back
Hard partEvery node writes: conflicts, split state, sticky sessionsKeeping the standby warm, current, and actually working
Reach for this when…The work is stateless or the store already does multi-writer (Cassandra, Dynamo-style)There is a single logical writer — a relational primary, a leader-elected coordinator

Active-active is not simply "better". Two nodes both accepting writes to the same row is the whole consistency problem from the consistency-models chapter, showing up in your availability design. Most real systems are active-active at the stateless tier and active-passive (or quorum-based) at the storage tier, and saying exactly that is the correct answer.

Failover, and the three ways it betrays you

Failover is the moment a system decides a component is dead and acts on that belief. The belief can be wrong, and the action can be worse than the fault.

network partition primary A accepting writes primary B accepting writes clients, west clients, east heartbeat lost both sides are up, both sides are right, the data is now wrong
Nothing crashed. Two healthy nodes lost sight of each other and each did the responsible thing, which is exactly how split brain happens.
  • Split brain. A partition, not a crash. Both replicas promote and accept writes; when the network heals you have two divergent histories and no principled way to merge them. The fix is not a smarter heartbeat — it is quorum: an odd number of voters, and a node refuses to serve as primary unless it can see a majority. A two-node cluster cannot do this: if the rule is "a majority must agree", losing either node stops the cluster, so it is less available than one node (and without a quorum rule it split-brains instead). That is why "two nodes for HA" is often worse than one.
  • Failback storms. The recovered node comes back, is declared healthy, and instantly receives its full share of traffic — with cold caches, cold connection pools and cold JIT. It falls over, gets marked unhealthy, and the loop repeats while the healthy nodes absorb the oscillation. The fix is slow-start / connection ramping: bring a returning node up to full weight over 30-120 seconds, and require it to pass health checks for a sustained window, not a single probe.
  • Retry amplification. One dependency slows down and every caller retries; with 3 retries (4 attempts) the dependency now receives up to 4× the traffic at exactly the moment it can least handle it. Across layers it compounds: if three stacked layers each make up to 3 attempts (2 retries), one user action becomes 33 = 27 attempts at the bottom, and the Google SRE book's example of 3 retries per layer over three layers is 43 = 64. Retries need exponential backoff with jitter, a retry budget (e.g. retries capped at 10% of requests), and a circuit breaker that stops calling a failing dependency entirely rather than politely queueing for it.
Say it like this → "I'd use quorum-based leader election with three voters rather than a two-node primary/standby, because two nodes can't distinguish a dead peer from a partition. On failback I'd ramp the recovered node's traffic over about a minute instead of restoring full weight instantly, and every retry gets jittered exponential backoff behind a circuit breaker so a slow dependency doesn't get amplified into a dead one."

Health checks are a design decision, not a checkbox

A health check is the sensor your entire availability story depends on, and most candidates specify it in three words. There are two distinct questions and they need different endpoints.

ProbeAsksFailure actionMust NOT check
LivenessIs this process wedged and unrecoverable?Restart the instanceDependencies — a DB outage would restart your whole fleet
ReadinessCan this instance serve a request right now?Remove from the load-balancer poolShared dependencies (a DB blip would drain the whole fleet at once) or anything slow — the probe must be cheap and reflect this instance's own state: warmed up, not overloaded
Deep / syntheticDoes a real user journey still work end to end?Page a human, drive dashboardsNothing — this one is allowed to be expensive, run it from outside
⚠ The health check that takes down the whole fleet Readiness returns 500 whenever the database is unreachable. The database has a 20-second blip. Every instance simultaneously reports unready, the load balancer drains the entire pool, and now you are returning 503 to 100% of traffic including the requests that never touch that database. The rule: a health check should report on the instance, not on the world. If a dependency is down, fail the requests that need it and keep serving the ones that don't.

Graceful degradation: shrinking the blast radius on purpose

The most leverage in availability design is not making dependencies more reliable — it is making fewer of them hard. Every dependency you can convert from "request fails without it" to "page renders with a slightly worse experience" is removed from the multiplication.

Dependency downNaive resultDegraded result
Recommendation service500 on the home pageServe a cached or globally-popular list; page still renders
Personalized rankingEmpty feedFall back to reverse-chronological
Search clusterSearch page errorsFall back to a prefix lookup on the primary store, or show recent items
Cache tierOrigin collapses under 100% miss rateShed load: serve a smaller page, admit a fraction of traffic, keep the origin alive
PaymentsCheckout unavailableAccept the order into a queue, confirm asynchronously — only if the business allows it
The line to remember Availability is not "make each box reliable." It is "reduce the number of boxes that can individually say no." Timeouts, fallbacks and static defaults are availability features, and they cost engineering time rather than hardware.

Degradation only works with aggressive timeouts. A dependency that hangs for 30 seconds is worse than one that fails in 50 ms, because the hang consumes your own threads, connections and memory until you fall over too. Set every outbound call a timeout meaningfully tighter than your own SLO, and treat "no timeout configured" as a bug.

Hunting single points of failure

A single point of failure is any component whose loss takes the system with it. They are rarely the obvious boxes — nobody forgets to replicate the database. They hide in the parts of the diagram people don't draw.

  • The load balancer itself. Redundant app servers behind one LB instance is a SPOF with extra steps. Managed LBs are already redundant; self-managed ones need a floating IP or DNS-level failover.
  • DNS and TLS certificates. An expired cert is a total outage that no amount of replication prevents. So is a single authoritative DNS provider.
  • Shared configuration and feature flags. If every instance fetches config at startup from one service, that service is a SPOF for every deploy and every autoscale event. Cache config locally and serve stale on failure.
  • The deployment pipeline. If you cannot roll back because CI is down, your recovery time is now bounded by someone else's uptime.
  • Correlated failure. Three replicas in one rack, one AZ, or on one storage volume are one failure domain wearing a costume. Ask "what is the smallest event that kills all N of these at once?"
  • Shared state you forgot is shared. One Redis holding sessions for a stateless fleet makes that fleet stateful. One shared connection pool, one shared secret store, one leader-election service.
Say it like this → "Let me walk the request path and name every component that has exactly one of something — LB, DNS, cert, config service, and the primary database. For each one I'll say whether it is genuinely redundant, or just drawn as a single box because it's managed."

Multi-AZ vs multi-region, honestly costed

Multi-AZMulti-region
Protects againstRack, power, cooling, one datacenterRegional outage, regional network, fibre cut, regulatory isolation
Inter-node latency~1-2 ms — synchronous replication is fine60-230 ms round trip between distant regions (US east to west 60-70, to Western Europe 70-90, to Singapore or Sydney 200-230) — synchronous replication on every write is not fine
Data costCross-AZ transfer, on the order of 1 cent per GB each way on major cloudsCross-region transfer plus a full second copy of everything
Consistency impactEssentially none; you keep a single primaryYou must choose: single writer with slow remote writes, or multi-writer with conflict resolution
Realistic ceilingRoughly 99.95-99.99%, depending on shared dependencies (planning range)Can exceed that — and only if failover is genuinely automatic. Many outages are deploy- or config-induced, which a second region does not fix
Reach for this when…Almost always — it is close to free and the default answerThe business loses serious money per minute of downtime, or law requires data residency

Be blunt about the cost of multi-region: it is not "deploy twice." It is a second copy of your data with a replication strategy, a global traffic routing layer, a story for cross-region consistency, and an organisation-wide discipline that every new service is region-aware from day one. Teams frequently build it and then discover their failover has never been tested, giving them all the cost and none of the availability. Untested failover is decoration. Say you would run scheduled region-evacuation drills — that one sentence separates people who have operated multi-region from people who have read about it.

Limiting blast radius and recovering from disaster

Redundancy handles the failures you expected. These techniques limit the damage from the ones you did not.

  • Bulkheads. Give each dependency or customer tier its own thread pool, connection pool or queue, so a slow payments call exhausts only the payments pool, not every worker.
  • Load shedding and deadlines. Reject excess work early and cheaply (a 503 beats a 30-second queue), and propagate one absolute deadline down the call tree so no hop works on a request whose caller has already given up. Both are described in the Google SRE book chapter on cascading failures.
  • Cells. Partition the fleet into independent cells that each serve a slice of users with their own copy of the stack. A bad deploy or poisoned input then takes out one cell, say 5% of users, instead of everyone.
  • Safe change. Most outages follow a change, so ship through canaries and gradual rollouts with automatic rollback, and rehearse failure with game days and chaos experiments.
  • Measure and learn. Watch the four golden signals (latency, traffic, errors, saturation) and run blameless postmortems, so the fix targets the system and not the person.

For regional disaster recovery, two numbers set the design: the recovery point objective (how much data you can lose) and the recovery time objective (how long you can be down). AWS's disaster recovery guidance describes four tiers in rising cost and falling recovery time: backup and restore (restore from copies in another region, hours), pilot light (data replicated and databases running, with the application servers defined but switched off), warm standby (a scaled-down but fully running copy that can take traffic right away), and multi-site active-active (every region serves traffic). Pick the cheapest tier that meets the RTO and RPO the business signed up for, and test the restore; an untested backup is a hope.

SLA, SLO, SLI and the error budget

TermWhat it isAudienceExample
SLIThe measurement itselfEngineersFraction of requests returning 2xx/3xx in under 300 ms, per minute
SLOThe internal target for that SLIYour team99.95% of those requests, measured over 28 rolling days
SLAThe contractual promise, with a penaltyCustomers, lawyers99.9%, or service credits are owed

The SLA is always looser than the SLO — deliberately. You want to be breaching your internal target and fixing it long before you owe anyone money. If your SLO equals your SLA you have no warning zone.

The error budget (a Google SRE idea) is the inverse of the SLO, and it is the most useful idea here because it turns reliability from an argument into a number. A 99.95% SLO over 28 days permits about 20 minutes of failure. That budget is a resource: spend it on risky deploys, experiments and migrations. The operating rule is mechanical — budget remaining, ship features; budget exhausted, feature work stops and reliability work starts until the rolling window recovers. It also stops the opposite failure mode: if you finish every month with 100% of the budget unspent, you are over-invested in reliability and shipping too slowly.

⚠ Promising nines you have not measured Do not say "this design gives us five nines." You do not know that, and an experienced interviewer will ask how you'd verify it. Say instead: "I'd set an SLO of 99.95% on the checkout path measured as good-requests over total-requests per minute, because the dependency math supports roughly that and the business impact of checkout downtime justifies the cost. Non-critical paths get a looser SLO." Different SLOs per journey is itself a senior signal — a uniform target across every endpoint means nobody thought about it.

Recognizing it in an unseen problem

  • The prompt contains a reliability number ("99.99%", "always available", "cannot go down during Black Friday") — that is an invitation to do the dependency multiplication out loud before designing anything.
  • A naive design adds a second server, calls it highly available, and never mentions how failure is detected, how long detection takes, or what happens when the two servers disagree.
  • Distinguish from fault tolerance (the advanced sibling chapter): availability is about staying up during expected failures; fault tolerance is about correctness and recovery when things fail in unexpected, partial and byzantine ways.
  • Distinguish from consistency: if the prompt stresses "users must never see stale data", you are being asked a CAP question, not an availability question — and the two pull in opposite directions during a partition.
  • Anything with a hard dependency on a third party you don't control (payments, mapping, SMS) should trigger the degradation conversation: what does the product do when that vendor is down for 20 minutes?
  • Pitfall: treating redundancy as sufficient. Redundancy without automated, tested, ramped failover raises your cost and leaves your availability roughly where it was.
Before you move on
  • Can you compute the end-to-end availability of a chain of five 99.9% dependencies, and state it in hours of downtime per year?
  • Can you explain why a two-node cluster is often worse than one for split-brain purposes, and what fixes it?
  • Can you distinguish liveness, readiness and deep health checks, and say what each must never check?
  • Can you name three single points of failure that aren't the database, and where they hide?
  • Can you explain what an error budget is and what happens to feature work once it's exhausted?
  • Can you explain why two independent copies give 1 − (1 − a)2 on paper and why shared fate makes the real number lower?
Sources and further reading
  • Google SRE book: embracing risk — SLOs, error budgets and halting releases when the budget is spent
  • Google SRE book: addressing cascading failures — retry amplification (43 = 64), retry budgets, load shedding and deadline propagation
  • Google SRE book: monitoring distributed systems — the four golden signals
  • AWS: disaster recovery options in the cloud — backup and restore, pilot light, warm standby and active-active
  • The Raft paper — why a majority quorum prevents two leaders, and what that means for two-node clusters

Claims in this chapter were checked against these sources in October 2026. Figures marked "planning figure" or "planning range" are rules of thumb, not guarantees. Downtime figures use a 365.25-day year.

✓Practise

5 questions drawn from 8, scored out of 5. This is practice: it runs in your browser, saves nothing and does not change what you have marked read.

I7

Finished reading?

0 of 36 chapters read in this series

→Next stop

  1. I6Previous · I6Rate limiting & throttling
  2. I7you are here
  3. Next · I8Storage systemsBlock, file, object — and why the bytes of a user upload should never touch your app servers.19 min read

01On this page

11 sections · 0%

  1. 01Availability is arithmetic, and the arithmetic is unforgiving
  2. 02What the nines actually cost you in wall-clock time
  3. 03Redundancy: active-active vs active-passive
  4. 04Failover, and the three ways it betrays you
  5. 05Health checks are a design decision, not a checkbox
  6. 06Graceful degradation: shrinking the blast radius on purpose
  7. 07Hunting single points of failure
  8. 08Multi-AZ vs multi-region, honestly costed
  9. 09Limiting blast radius and recovering from disaster
  10. 10SLA, SLO, SLI and the error budget
  11. 11Recognizing it in an unseen problem

[] chapters · t top