Skip to the notes
JSGroundwork
JSGroundwork handwritten · web dev
✎Playground→⌘Problems↻Review🔥Progress

Chapters

24 chapters
⌕
Beginner7›
B1The mental modelB2Client-server basicsB3Vertical vs horizontalB4Databases in designB5Caching fundamentalsB6APIs & communicationB7Walkthrough: URL shortener
Intermediate9›
I1Load balancing in depthI2CDNsI3Message queues & asyncI4Consistency modelsI5Rate limiting & throttlingI6Designing for availabilityI7Storage systemsI8Search systemsI9Walkthrough: feed/chat
Advanced8›
A1CAP theorem in depthA2Sharding at scaleA3Distributed consensusA4Fault toleranceA5Observability at scaleA6Capacity estimationA7Case studiesA8Tradeoff thinking
/ search[ ] chaptert top

System Design levels

1Beginner2Intermediate3Advanced

Ready to read

JSJavaScript⑂Git◎Interview prepΣDSA in JSSDSystem Design
More topics15›
</>HTML{ }CSS⚛ReactNNext.jsNeNest.jsTSTypeScriptNoNode.js🐳DockerDBSQL & Databases✓Testing🔒Web Security☁Cloud & DevOps◈GraphQL◆Redis☸Kubernetes
100%
I6

Designing for availability

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

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.8 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.

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).

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 30 s to 5 min of unavailability 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
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, which 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, every caller retries three times, and the dependency now receives 4× the traffic at exactly the moment it can least handle it. 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 poolAnything slow — the probe itself must be cheap
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 fine30-150 ms cross-continent — synchronous replication is not fine
Data costCross-AZ transfer, roughly 1-2 cents per GBCross-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 ceiling99.95-99.99%99.99%+ — and only if failover is genuinely automatic
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.

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 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.
←previousRate limiting & throttling↑ CovernextStorage systems→