What a Distributed System Is, and Why It's Hard
Hook #
You already operate distributed systems — you just may not have called them that. The moment your application talks to a database on another machine, a Redis cache, a background-job queue, a third-party API, or a read replica, you have a system whose parts run on separate computers connected by a network that can drop, delay, duplicate, and reorder messages, and where any part can fail independently while the others keep running. That last property — partial failure — is what makes distributed systems a fundamentally different and harder discipline than single-machine programming. On one machine, things work or the machine crashes; in a distributed system, half of it can be broken, or merely slow, and you often can't even tell which. This course is where the operational pain you've felt — replication lag, jobs running twice, mysterious timeouts — gets its theory, and where you learn to design systems that stay correct when the network and other machines betray you.
What you'll be able to do by the end of this lesson #
- Explain what a distributed system is and the real reasons to build one (scale beyond one machine, fault tolerance, geographic distribution) — and the cost you pay for those benefits.
- Explain partial failure and why it's the defining difficulty: parts fail independently, and you often can't distinguish "failed" from "slow."
- Explain the system models (synchronous, asynchronous, partially synchronous) and why the async model — no timing guarantees — is the realistic and pessimistic one.
- Recognize the fallacies of distributed computing (the network is reliable, latency is zero, bandwidth is infinite, ...) as the false assumptions that break naive designs.
A quick try before we start #
Before reading: your service calls another service over the network, and the call times out. List everything that could have happened. (The request never arrived; it arrived and the other service is still processing; it processed successfully but the response was lost; the other service crashed mid-way; it's just slow and will answer eventually.) Now the hard part: from your side, you cannot tell which. That single uncertainty — a timeout tells you nothing definite — is the seed of nearly every hard problem in this course, and sitting with it is the right way in.
Why this matters here #
This is the year the curriculum promised: building real systems that scale, and real systems are distributed. Every architecture conversation about scaling — add replicas, split into services, shard the data, put a queue between components — is a decision to distribute, and each one buys scalability or resilience at the cost of the failure modes this course is about. The operational problems you've already hit are distributed-systems problems in disguise: a read that doesn't reflect a just-completed write (replication lag), a background job that runs twice (exactly-once delivery is impossible, so you need idempotency), a cache that serves stale data, a "thundering herd" when many clients retry at once. You've handled these tactically — a retry here, a sleep there, switching a read to the primary. This course replaces those band-aids with understanding: why they happen, and how to design so they don't bite. The engineers who architect reliable systems at scale think in these terms; this quarter is where you join them.
Within the path, this course is the direct sequel to two you just finished. The databases course ended on CAP, replication, and sharding — the data side of distribution; this course generalizes to distributed computation and coordination. The networking course established that the network is unreliable, latency is distance-bound, and routing is agreement-among-independent-nodes — the substrate distribution runs on. And the theory course gave you the instinct to recognize fundamental limits — which you'll need immediately, because distributed systems have their own impossibility results (lesson 4). This lesson sets the stage; the rest of the course builds the tools (clocks, ordering, consensus) for coordinating machines that can't fully trust each other or the network between them.
The engineer's lens #
The foundational insight is that partial failure changes everything — the core difficulty of distributed systems is not that things fail, but that things fail independently and undetectably, so you can never be sure of the state of a remote component. On a single machine, a function call either returns or the whole process dies; there's no in-between, and you have a consistent view of memory. Across a network, a remote call can fail in a dozen partial ways, and crucially, a slow response and a failed response look identical — a timeout doesn't tell you whether the work happened. This is why so much distributed-systems design is about coping with uncertainty rather than preventing failure: you make operations idempotent (safe to retry, because you'll retry things that may have already succeeded), you use timeouts and retries with backoff (accepting you might do work twice), you design for at-least-once rather than exactly-once delivery (because exactly-once is provably impossible over an unreliable network). Recognizing that a timeout is ambiguous — that "I didn't get a response" is not "it didn't happen" — reframes your whole approach: you stop writing code that assumes remote calls behave like local ones, and start writing code that stays correct even when it can't know what happened. That shift is the single most important mental upgrade this course delivers, and it's why "make it idempotent" is the most repeated advice in distributed engineering.
The second lens is the fallacies of distributed computing as a checklist of the comfortable single-machine assumptions you must consciously abandon. The eight fallacies — the network is reliable, latency is zero, bandwidth is infinite, the network is secure, topology doesn't change, there's one administrator, transport cost is zero, the network is homogeneous — are each an assumption that's true enough on one machine that you internalized it, and false in ways that break naive distributed designs. "Latency is zero" is why a chatty service that makes 50 sequential remote calls is slow no matter how fast each service is (you learned the latency physics in the networking course — now it compounds across service hops). "The network is reliable" is why every remote call needs a failure path. "Topology doesn't change" is why service discovery and load balancers exist. The practical value is diagnostic: when a distributed system misbehaves, ask which fallacy did someone assume? — and you'll usually find the bug. And it connects to a broader engineering maturity: much of good distributed design is removing hidden assumptions, the same discipline as removing hidden coupling or hidden global state in code. The system that survives contact with production is the one built by someone who assumed the network would betray it — because it will.
What to focus on in DDIA Chapter 8 (+ the fallacies) #
- Partial failure and the ambiguity of timeouts. This is the load-bearing idea. Internalize that you cannot reliably distinguish a crashed node from a slow one, and that a lost response means an operation's outcome is unknown. Everything else in the course is coping with this.
- The system models (sync / async / partially synchronous). Understand why the asynchronous model (no bounds on message delay or clock drift) is the honest one for real networks, and why algorithms proven correct in that model are the ones you can trust. Partially synchronous is the pragmatic middle most real systems assume.
- The fallacies as a design checklist. Read the eight, and for each, recall a time you (or a system you've used) assumed it. This turns them from a list into an instinct.
- Skip on first pass: the detailed clock-drift and network-delay measurements, and the formal model definitions. Get partial failure, timeout ambiguity, the models, and the fallacies — the conceptual frame for the whole course.
Explain it back #
Explain to a colleague why "make the operation idempotent" is such common advice in distributed systems, tracing it back to partial failure. A strong answer: over an unreliable network, a request can succeed on the server but have its response lost, so the client sees a timeout and can't tell whether the work happened — its only safe recovery is to retry, which means the operation might execute more than once. Making it idempotent (safe to apply repeatedly with the same effect) means those unavoidable retries don't cause damage (double charges, duplicate records). So idempotency is the direct consequence of the fact that a timeout is ambiguous and retries are unavoidable. Bonus: connect this to Sidekiq job idempotency or a retry you've written.
Where this connects #
Backward: The databases course's CAP/replication (the data side of distribution — this course generalizes it) and the networking course's unreliable-network reality and latency physics (the substrate — now compounded across service hops). The theory course's instinct for recognizing fundamental limits is about to be needed for FLP and CAP (lesson 4).
Forward: Lesson 2 goes deep on the failure models this lesson introduced (crash-stop, crash-recovery, Byzantine). Lesson 3 tackles the impossibility of a shared clock and how logical clocks order events without one. Lesson 4 hits the impossibility results (FLP, CAP). And the whole course builds toward Course 9.2's consensus algorithms (Raft) — how machines agree despite everything this lesson says can go wrong.
That's the free preview. Sign in to continue this course.
Sign in to continueNew here? Make a desk →