Designing Data-Intensive Applications — Field Guide

Part I — Foundations

Reliability, Scalability, Maintainability

The three concerns Kleppmann uses to evaluate every data system decision in the rest of the book.

Almost every design decision in a data-intensive system comes down to trading off between three concerns. They sound like generic engineering virtues, but each one has a precise meaning worth pinning down before anything else.

Reliability

A system is reliable if it keeps working correctly even when things go wrong: hardware fails, software has bugs, humans make mistakes. Reliability isn’t “no faults happen” — it’s “the system tolerates faults without the user noticing.” That distinction matters: you design for fault tolerance, not fault avoidance, because faults are inevitable at scale.

Scalability

Scalability is the system’s ability to cope with increased load. But “load” isn’t one number — you have to describe it with load parameters specific to your system: requests per second, the ratio of reads to writes, the number of simultaneously active users, cache hit rate.

Once you can describe load, you can ask the real scalability question: if load grows by 10x, what has to change? And you measure the answer with response time percentiles, not averages.

Fig. 1 — Same distribution, viewed as average vs. percentiles
avg: 96msresponse time →

The average (96ms) sits right in the thick of the distribution and looks perfectly healthy — it tells you nothing about the long tail of slow requests hiding to the right.

function percentile(sortedLatenciesMs, p) {
  const index = Math.ceil((p / 100) * sortedLatenciesMs.length) - 1;
  return sortedLatenciesMs[Math.max(0, index)];
}

const latencies = [12, 15, 14, 19, 400, 13, 16, 900, 14, 15].sort((a, b) => a - b);

percentile(latencies, 50); // p50 — typical request
percentile(latencies, 99); // p99 — the tail your worst-off users feel

Maintainability

Most of the cost of software isn’t building it — it’s the years of people working on it afterward: fixing bugs, adapting it to new use cases, operating it. Three sub-properties make a system maintainable:

  • Operability — make it easy for operations teams to keep the system running smoothly (good monitoring, predictable behavior, sane defaults).
  • Simplicity — manage complexity so new engineers can understand the system; fight accidental complexity (complexity that isn’t inherent to the problem, just how it happened to be built).
  • Evolvability — make it easy to adapt the system to new requirements as needs change. This is the property that everything in the “Encoding & Schema Evolution” topic exists to protect.