◐ system-design/fundamentals.md
1. Fundamentals: Reliability, Scalability, Maintainability
Three properties define a "good" production system. Every architectural choice is a trade-off between them.
~5 min read·updated 5/29/2026
1. Fundamentals: Reliability, Scalability, Maintainability
Three properties define a "good" production system. Every architectural choice is a trade-off between them.
1.1 Reliability
Reliability = the system continues to work correctly, even when things go wrong.
"Things going wrong" = faults. A fault is a component deviating from spec. A failure is the system as a whole stopping doing what users need. The goal is fault tolerance: prevent faults from causing failures.
You cannot eliminate faults. You can only design so faults don't escalate to failures.
Categories of faults
- Hardware faults. Disks fail (~2-5% AFR), RAM has bit flips, networks drop packets, datacenters lose power. Mean Time To Failure (MTTF) of a single disk is ~10 years; with 10,000 disks you lose ~3/day. Solution: redundancy (RAID, replication, multi-AZ deployments).
- Software faults. Bugs, runaway processes, cascading failures, leap-second bugs (Linux kernel ~2012). Harder to detect because they correlate across nodes — one bad config or one poison message kills everything. Solution: process isolation, circuit breakers, gradual rollouts, careful monitoring.
- Human errors. ~80% of major incidents are human-triggered (bad deploy, wrong config, accidental delete). Solution: minimize blast radius (staging, canary), recovery (point-in-time restore), automated checks (lint, tests, type systems), good UX in tooling.
Designing for reliability
- Redundancy: N+1 (one extra) or N+2 for critical components.
- Graceful degradation: when ML scoring is down, show recency-ordered feed instead of a 500.
- Bulkheads: isolate failures (per-tenant connection pools so one bad tenant can't drain the pool).
- Retries with backoff and jitter: don't herd; don't retry idempotency-unsafe ops blindly.
- Health checks and circuit breakers: stop sending traffic to broken instances.
- Chaos engineering: Netflix's Chaos Monkey kills random instances in prod to force the team to build for failure.
1.2 Scalability
Scalability = the system's ability to cope with increased load.
Not a yes/no property. Always ask: scalable in what dimension? (users, requests/sec, data volume, response time at percentile P).
Load parameters
Pick the right number to track. For Twitter, "tweets/sec" is fine for write load — but the real load problem was fan-out on read: one celebrity's tweet hitting 100M followers. The load parameter for the home timeline read isn't "reads/sec," it's "average followers per posting user."
Performance under load
Two questions:
- When you increase a load parameter and keep system resources constant, how does performance degrade?
- When you increase a load parameter, how much resource do you need to add to keep performance constant?
Latency vs throughput
- Latency = time for one request (a duration). Always quote percentiles, never averages — averages hide the long tail.
- Throughput = requests per unit time (a rate).
A system can have great average latency and terrible P99. The P99 customer is your loudest customer (they're a power user — many requests, so they hit tail latencies often).
Percentiles & tail latency
- P50 (median): half of requests are faster than this. Useful baseline.
- P95, P99, P99.9 ("three nines"): the slow ones. Amazon found 100ms extra latency dropped sales 1%; Google found 500ms extra dropped traffic 20%.
- Tail amplification: if one backend call has P99 = 1s and your request fans out to 100 backends, the probability all finish in 1s is ~37%. P99 of the parent = ~much worse than 1s.
Computing percentiles: don't average percentiles across machines (mathematically wrong). Use approximate algorithms like t-digest or HDR Histogram and merge.
Scaling axes (the AKF Scale Cube)
- X-axis: horizontal duplication. Run more identical instances behind a load balancer. Easy. Limited by stateful services and shared resources.
- Y-axis: functional decomposition. Split by service/domain (auth, payments, search). Microservices. Each service scales independently.
- Z-axis: data partitioning (sharding). Split data by a key (user_id mod N). Each shard handles a slice. Required for systems too big to fit on one node.
Most systems use all three.
Vertical vs horizontal
- Vertical (scale up): bigger machine. Simple, no code change. Caps at hardware limit. Single point of failure.
- Horizontal (scale out): more machines. Theoretically unlimited. Requires designing for distribution (statelessness, sharding, consensus).
1.3 Maintainability
The cost of software is dominated by ongoing maintenance, not initial development.
Three sub-properties:
- Operability: easy for ops to keep it running. Good monitoring, runbooks, predictable behavior, automated routine tasks.
- Simplicity: easy for new engineers to understand. Fight accidental complexity (Brooks: "no silver bullet" — there's essential complexity in the problem, but most complexity is accidental, our fault). Abstractions are the main weapon.
- Evolvability (or extensibility): easy to change. Tied to TDD, refactoring, simple architecture, agile.
1.4 Availability
Reliability over time. Usually expressed in nines:
| Availability | Annual downtime |
|---|---|
| 99% (two nines) | 3.65 days |
| 99.9% | 8.76 hours |
| 99.95% | 4.38 hours |
| 99.99% (four nines) | 52.6 minutes |
| 99.999% (five nines) | 5.26 minutes |
Five nines is extremely expensive. Most B2B SaaS targets four nines. Internal tools, two nines.
SLI / SLO / SLA
- SLI (Service Level Indicator): a measurement. "Fraction of HTTP requests with status 200 in the last 5min."
- SLO (Service Level Objective): a target. "SLI ≥ 99.9% over 30 days."
- SLA (Service Level Agreement): a contract with consequences. "If SLO is missed, customer gets 10% credit."
Google's SRE rule: SLOs are looser than your real performance. The gap is your error budget — if you're under it, you can ship features faster (more risk). If you're over, you must slow down and stabilize.
Availability math
For independent components in series (request must traverse all): multiply.
- 3 services each at 99.9% → 99.7% combined.
For redundant components in parallel: 1 - product of failure probs.
- 2 replicas each at 99% → 1 - (0.01 × 0.01) = 99.99%.
In practice failures correlate (shared power, shared DNS, shared region) so independence is a fiction. Plan for correlated failures.
1.5 The hardest trade-off: reliability vs cost vs speed of delivery
There is no "100% reliable" system. There is "reliable enough that the marginal cost of more 9s exceeds the marginal value." For most products, 99.9% is the right target.
For Google L3/L4: when designing, state your availability target up front, then justify everything (replication factor, multi-region, ack policy) against that target. Don't over-engineer. Don't under-engineer.
Key takeaways
- Reliability = tolerate faults. Hardware fails, code has bugs, humans err. Build for all three.
- Scalability = define your load parameter, then measure performance at percentiles (not averages).
- Maintainability = operability + simplicity + evolvability. Most code-cost is post-launch.
- Availability = nines. State the target before you design.
- Trade-offs are explicit: reliability costs money; speed costs reliability; simplicity costs flexibility.
// 2 views