Chapter 14 · System Design Fundamentals

Design YouTube

A video platform is many systems wearing one trench coat. The trick is to split it: a light control plane for titles, comments and view counts, and a heavy blob plane that turns one raw upload into dozens of adaptive streams and pushes them through storage tiers and a global CDN. Read-heavy, write-light, and planet-scale.

Open the companion slides
Reading time ~13 min Prerequisites Ch 2 · Estimation, Ch 6 · Key-Value Store Audio 🔊 Hinglish read-aloud Next Design Google Drive

Strip the product to its two verbs — upload and watch — and the asymmetry jumps out. A handful of creators push in enormous files; a billion viewers pull small chunks back out. So the whole design bends toward reads: the write path is an expensive, one-time factory that manufactures cheap, cache-friendly artifacts, and the read path is a hierarchy of caches that keeps origin storage from ever seeing the crowd.

Primary source

Alex Xu, System Design Interview (Vol. 1), Chapter 14. Companion deck: slides — jump to any slide with the Slide N chips. Go deeper: the HTTP Live Streaming spec (RFC 8216), which defines the .m3u8 manifests every player reads.

What we are actually building Slide 2

Pin the scope before drawing boxes. The functional surface is small — upload a file, watch it on any device, and a thin layer of social state around it. The hard requirements are non-functional, and one number decides the architecture: reads outnumber writes by roughly two orders of magnitude. That single fact is why the read path gets a CDN and the write path gets a factory.

Functional

Creators upload from web or mobile. Viewers watch on phones, laptops, TVs and slow links. Comments, likes and subscriptions. Search and "up next". Live streaming, monetisation and copyright matching are out of scope — each is its own design.

Non-functional

Billions of registered viewers, hundreds of millions daily. Playback start under ~2 s at p95. Eleven nines of durability on the stored masters. And graceful degradation: a comment or search outage must never stop a video from playing.

The workload has a distinctive shape: a tiny set of recent, popular videos drives almost all bandwidth (the hot tail), while the enormous back-catalogue, watched rarely, dominates storage cost (the long tail). The two pull in opposite directions, and the storage design has to serve both.

How big is "big" here? Slide 3

A back-of-the-envelope pass (the method from Chapter 2) sets the order of magnitude the architecture must respect. These are sizing sketches, not benchmarks — the point is the shape, not the digits.

egress/day  =  sessions × bytes/session  ≈  1 B × 240 MB  =  ~240 PB/day
of which the CDN absorbs > 95% → origin serves only the misses
ingest
~5 PB
raw / day
store
~EB
net / year
reads
~1 B
sessions / day
egress
~240 PB
from edge / day
peak
~22 Tbps
global bandwidth

Two takeaways drive everything downstream. First, storage grows with hours of video and never shrinks, so bytes must move to cheaper tiers as they cool. Second, egress is so large that it cannot come from the origin — a CDN with a very high hit ratio is not an optimisation here, it is the only way the numbers close.

High-level architecture — two planes Slide 4

Read the diagram as two independent lanes. The blob plane (top) is the video factory: an upload lands a raw master, a transcoder fans it into renditions, and the CDN serves those to viewers. The control plane (bottom) is a normal web backend: an API over a metadata database, plus an async pipeline for counts. The only thing joining them is a key — a metadata row stores the object path, never the bytes.

BLOB PLANE — move the pixels CONTROL PLANE — move the facts Creator uploads a file Viewer watches Upload Svc resumable chunks Raw Master object store Transcoder DAG of jobs Renditions HLS / DASH segs Global CDN edge POPs Metadata API titles · users Metadata DB sharded SQL/NoSQL View Pipeline async counts The planes share one object key — never a join. Either can scale or fail on its own clock.
The blob plane manufactures and serves bytes; the control plane serves facts. Splitting them lets each use the right storage, durability budget and scaling axis.

Upload flow — chunked, resumable, idempotent Slide 5

An hour of 4K is gigabytes crossing flaky hotel Wi-Fi. Treat the upload as many small commitments, not one big bet. The client splits the file into parts, sends each straight to object storage, and on any failure asks "which offsets did you already get?" so it re-sends only the gaps. Nothing goes through the API tier's memory — it just hands out signed URLs and tracks a cursor.

1 · init
client calls upload service → gets an uploadId + pre-signed part URLs
2 · chunk
split into ~8 MB parts; upload each direct-to-blob with its own checksum
3 · resume
on retry, query received offsets and re-send only the missing parts
4 · commit
server finalises multipart → one immutable object → enqueue a transcode job
Idempotency is the whole point

Keying every part by (uploadId, partNumber) makes re-sends harmless: uploading the same part twice overwrites identically, and the final commit is a single atomic stitch. A duplicate commit is a no-op, so a client that times out and retries can never create two videos or a half-written one.

Transcoding pipeline as a DAG Slide 6

The master is probed, split on GOP boundaries, and fanned out to many encoders that run in parallel — one per resolution-and-codec target — plus side jobs for thumbnails and captions. A packager wraps the outputs into HLS/DASH segments and manifests, and a final step flips the "ready" flag. Model it as a directed acyclic graph of pure, retryable jobs: each node is a function of its inputs, so a lost output is simply rebuilt, and encoders scale out horizontally.

Raw master Probe fps · dur Chunker GOP-cut Encode 240p · H.264 Encode 720p · H.264 Encode 1080p · VP9 Encode 1080p · AV1 Encode 4K · AV1 Thumbs · Captions Packager HLS · DASH Manifest .m3u8 / .mpd Publish ready flag
One master fans out into many encodes running in parallel, then fans back in through a packager and manifest build. Every node is idempotent, so a failed job is just re-run.

One source, many renditions Slide 7

Why manufacture so many copies? Because a flagship phone on fibre and a five-year-old tablet on café Wi-Fi cannot use the same file. The transcoder builds a bitrate ladder — the same content at rising resolutions and bitrates, in several codecs — and the player picks the highest rung it can currently afford. Newer codecs (AV1, VP9) cut bytes 30–50% over H.264 but cost more CPU to decode, so shipping multiple codecs lets each device choose its own trade-off.

RenditionBitrateWho it serves
2160p · AV1~16 MbpsBig screens on fast, stable links; modern decoders only.
1080p · H.264~5 MbpsThe safe default — decodes everywhere, good on broadband.
720p · H.264~2.5 MbpsMobile on decent cellular; the common fallback rung.
480p / 360p~1 / 0.5 MbpsCongested or metered networks; keeps playback alive.
240p · H.264~0.2 MbpsThe floor. Ugly but watchable when the link collapses.

Renditions are derived data: they can always be rebuilt from the master. That is why we pay eleven-nines durability only on the source, and can freely re-encode the long tail into cheaper codecs later to shrink its footprint.

Hot, warm, cold — long tail to deep archive Slide 8

A tiny slice of videos earns almost all views in their first week; everything else becomes long-tail, watched rarely but kept forever. The cost equation only closes if bytes move to match their access pattern. An access-frequency tracker promotes and demotes objects across tiers asynchronously, so a viewer never blocks on a tier migration — a cold read just takes a beat longer.

TierWhereServesRead latency
HotSSD/RAM at the edgeTrending, last 24 hsub-ms
WarmRegional object storePopular renditions< 100 ms
ColdMulti-region, erasure-codedLong-tail watchesseconds
ArchiveDeep/glacial storageMasters & rare originalsminutes

The master lives in archive at top-tier durability; the renditions people actually watch flow up toward the edge on demand. Because renditions are rebuildable, a cold tier can trade some availability for cost without ever risking the source.

HLS & DASH — manifests over plain HTTP Slide 9

We never stream one giant file. Each rendition is sliced into short segments (2–6 seconds), and a manifest tells the player where every segment lives. A master manifest lists the available renditions; each rendition has its own playlist of segment URLs. Everything is a plain HTTP GET of a static object — so it caches cleanly at every layer and rides existing CDN machinery for free.

master.m3u8 #EXTM3U 240p — 0.2 Mbps 720p — 2.5 Mbps 1080p — 5 Mbps 2160p — 16 Mbps 720p.m3u8 #EXT-X-TARGETDURATION:6 seg-0001.ts seg-0002.ts seg-0003.ts … SEGMENTS ON THE CDN 720p · ~2.5 Mbps · 6 s each 1080p · ~5 Mbps · 6 s each 240p · ~0.2 Mbps · 6 s each
A manifest is just a list of URLs. Small, static segments cache everywhere and let the player switch quality between segments without reloading the stream.

HLS uses .m3u8; DASH uses .mpd. They differ in syntax, not shape — both expose a master manifest pointing at per-rendition playlists — so a modern packager emits both side by side.

Adaptive bitrate selection — the player decides Slide 10

Quality is not chosen by the server — it is chosen on the device, segment by segment. The server just publishes the menu (the manifest); the client orders whatever fits its current appetite. That is deliberate: only the device sees real-time radio strength, contention and throttling, and only it can react inside a single 6-second segment.

Signals it watches

Times each segment download for a rolling bandwidth estimate; watches its own playback buffer (a draining buffer means "drop quality now"); and folds in device hints — screen size, decoder load, battery.

Why hysteresis

It adds a deadband so it does not flap up and down on every wobble. Ramp up cautiously when the buffer is healthy; drop fast when it is starving. Smoothness beats chasing the last megabit.

CDN — edge POPs, mid-tier shield, origin Slide 11

A three-layer cache hierarchy puts bytes seconds away from every viewer while protecting the origin from the herd. Viewers hit a nearby edge POP; on a miss the edge asks a regional origin shield; only if the shield also misses does the origin get touched. The shield's job is request collapsing: when a new video goes viral, a thousand edge misses for the same segment become one origin fetch.

viewers Edge · Mumbai disk + RAM cache Edge · Frankfurt disk + RAM cache Edge · São Paulo disk + RAM cache Origin Shield collapses misses Origin source of truth ≥ 95% of bytes served at the edge
Solid arrows are cache hits at the edge; dashed arrows are the rare misses. The shield turns a viral thundering herd into a single origin read.

Metadata and blobs live apart Slide 12

Titles, comments, subscriptions and view counts are small and reached by rich queries; video bytes are huge and reached by primary key alone. Mixing them is the fastest route to a crashed database. So the metadata plane is an indexed, transactional, cached store (the key-value / NoSQL and sharded-SQL patterns from earlier chapters), while the blob plane is flat, immutable, key-only object storage. The link between them is just a path string.

metadata
rows
title · owner · views
blob
objects
master + segments
join
a key
object path only
payoff
isolation
fail on separate clocks

Failure isolation falls out for free: a metadata outage might hide a title or comment thread, but the CDN keeps feeding segments to every in-flight watch. And swapping the blob backend later becomes a one-column migration, because the row never held bytes — only a pointer.

View counts — async aggregation Slide 13

A trending video can take a million plays a minute. Writing one row per view into a relational table would incinerate the database — and it is the wrong model anyway. Counts are eventually-consistent metrics, not transactions. Play events stream through a log into a real-time aggregator that feeds a fast, approximate live counter, while a slower batch reconciler recomputes the authoritative number, stripping bots and duplicates.

Player play events Edge Collector validate · dedup Event Log Kafka topic Stream Aggregator per-minute counts Batch Reconciler hourly truth Live Counter approx views_agg canonical reconcile The UI shows the live counter for snappiness; the batch path later corrects bots, duplicates and partial fan-out.
Two paths, two speeds: a fast approximate counter for the UI and a slow exact reconciliation for the record. The dashed arrow heals drift.

Six principles Slide 14

Split the byte plane from the control plane

Metadata and blobs scale and fail on different curves. Glue them with a key, not a join.

Heavy work is a DAG of small, retryable jobs

Transcoding, packaging and reconciliation are idempotent steps that can be rebuilt on demand.

Push intelligence to the edges

The CDN absorbs the bytes, the player picks the quality, the origin only sees the misses that survive both.

Move bytes with their access pattern

Hot at the edge, warm regionally, cold and archived below. Pay top-tier durability only on the master.

Counts are streams, not transactions

Views, likes and trending are async aggregates. Show a fast approximation now, reconcile the truth later.

No component takes the watch down with it

Comments, recommendations, search may fail — playback must keep rolling. Design every dependency to be sheddable.

Active recall

Cover the answers. Say each one out loud before you tap to check.

Why split the system into a control plane and a blob plane?
Different sizes, different access.
Metadata is small and query-rich; video bytes are huge and key-only. They need different stores, durability budgets and scaling axes — and the split gives failure isolation. They are joined by an object key, never a join.
What makes an upload resumable and idempotent?
Small commitments, keyed parts.
The file is chunked and each part keyed by (uploadId, partNumber). On retry the client asks which offsets landed and re-sends only gaps; a re-sent part overwrites identically and a duplicate commit is a no-op.
Why produce many renditions instead of one file?
Devices and networks vary.
A bitrate ladder lets each device pick the highest rung it can afford right now. Multiple codecs trade decode cost against bytes. Renditions are derived data, rebuildable from the master.
Where does adaptive bitrate selection happen, and why?
Who sees the radio?
On the client/player, segment by segment. Only the device sees real-time bandwidth, buffer level and radio conditions, and can react within one segment. The server only publishes the menu.
What are the CDN's three tiers, and what does the shield do?
Edge, mid, origin.
Edge POPs → origin shield → origin. The shield does request collapsing: many edge misses for the same segment become one origin fetch, so a viral video never stampedes the origin.
Why are view counts async aggregates, not one row per view?
A million plays a minute.
Row-per-view would incinerate the database, and counts are metrics, not transactions. A stream aggregator drives a fast approximate counter; a batch reconciler produces the authoritative number, removing bots and duplicates.

Check yourself

Q1 Where is the adaptive-bitrate decision made?
Why: Only the device sees live bandwidth, buffer level and radio conditions, so it picks the rung segment by segment. The server just publishes the menu.
Q2 A new video goes viral. What keeps the origin from being stampeded?
Why: The mid-tier shield performs request collapsing — a thousand edge misses for the same segment become a single origin read, not a million.
Q3 Why is the upload chunked with parts keyed by (uploadId, partNumber)?
Why: Keyed parts make re-sends harmless (identical overwrite) and let the client resume from the last received offset; the final commit is one atomic, repeatable stitch.
Q4 The comment service is down. What should a viewer still be able to do?
Why: Graceful degradation is a hard requirement. Playback rides the blob plane and CDN; a control-plane outage may hide comments but must not stop the watch.
Q5 Why store view counts through async aggregation instead of a row per view?
Why: A trending video can hit a million plays a minute. Counts are eventually-consistent metrics: a stream aggregator shows a fast approximation, a batch job reconciles the truth.