Design YouTube / Netflix
Design YouTube / Netflix (upload, transcode, store, stream at scale)
The naive pitch sounds reasonable for about ten seconds: "the user uploads a video file, we save it to disk, and when someone hits play we stream that file back." Then reality arrives. The uploaded file is one encoding at one bitrate — but a viewer on a 4K TV wired to fibre and a viewer on a phone crawling along on 3G cannot both be served the same bytes: give the phone the 4K file and it buffers forever; give the TV a 480p file and it looks like mud on a 65-inch screen. And even if you solved the quality problem, serving every playback read straight from your origin storage means pushing the entire global viewing bandwidth — which we'll compute at ~125 Tbps average — out of your own data centre, from the same handful of servers that hold the hot files. Video is the canonical "the write is easy, the read will bankrupt you" system: one system to ingest and transcode, a completely different system to deliver. This lab derives both from first principles, with every number traced so you can defend it under drilling.
Movement 1 — The Trap: "store the file, stream it back"
Picture the simplest thing that could possibly work. A creator uploads a 2-hour 4K movie. You store that single file in object storage. When anyone presses play, your web server opens the file and streams the raw bytes to their player. Ship it. Here is exactly where it detonates, quantified:
- One 4K file is huge, and it's the wrong shape for most viewers. A 2-hour 4K stream at ~20 Mbps is
20 Mbit/s × 7200 s ÷ 8 = 18 GBfor a single rendition. To play it, the client must sustain 20 Mbps for the whole runtime. A phone on 3G has maybe ~1.5 Mbps — it can't pull 20 Mbps, so the player stalls every few seconds. There is no single bitrate that works: whatever one file you pick is simultaneously too heavy for the phone and (if you pick low) too ugly for the TV. - Serving every read from origin melts your bandwidth and your budget. Global viewing is ~1 billion hours watched/day (we size this below). At an average delivered ~3 Mbps that's an average egress of ~125 Tbps. A well-provisioned origin box pushes maybe 40 Gbps out its NIC, so you'd need
125,000 ÷ 40 ≈ 3,125origin servers doing nothing but re-reading the same hot files — all in your one region, so a viewer in Australia pulls every byte across the Pacific. The transatlantic/transpacific egress bill alone would dwarf every other cost in the company. - A single hot video concentrates the pain. When one video goes viral, millions of players hit the same file at once. Your origin storage now has thousands of concurrent range-reads on one object — the exact opposite of the evenly-spread load your storage was provisioned for.
The trap is treating video as a storage problem. It's a bandwidth problem wearing a storage costume, and the fix is two ideas the naive design lacks entirely: (1) transcode the one upload into a ladder of bitrates cut into small segments so the client can adapt, and (2) push those segments to edge caches so ~99% of bytes never touch your origin.
Movement 2 — Scope it like a senior (ask before you draw)
Do not touch the whiteboard yet. Each of these answers changes the design materially:
- Upload:view ratio? Video is astronomically read-heavy — a handful of uploads seen by millions. Confirm it, because it justifies spending almost all the design budget on the read path (CDN) and treating ingest as a background pipeline.
- Resolutions and devices? Do we serve 240p…4K? Phones, browsers, smart TVs, set-top boxes? This sets the size of the transcode ladder (how many renditions per upload) and therefore both storage and transcode-compute cost.
- Live or VOD? Live streaming needs a low-latency ingest+transcode pipeline (sub-few-seconds, chunked transfer) and can't precompute renditions. VOD (the default for "YouTube/Netflix") lets us transcode fully before anyone watches. Assume VOD for this lab; note the live delta in Defend.
- Playback-start latency target? "Time to first frame" — is <1 s the bar? That pushes us toward starting playback at a low rung and ramping up, and toward edge caches close to users.
- Storage / retention? Keep everything forever, or tier cold content off to cheap storage? Drives the storage BOTE and whether we need hot/warm/cold tiering.
- Scale — uploads/day and views/day? The two numbers that drive every estimate. Pin them down explicitly rather than assume.
Assume for this lab: 500 hours of video uploaded per minute; ~1 billion hours watched/day; VOD (not live); a rendition ladder of 240p / 360p / 480p / 720p / 1080p (4K for a subset); playback must start in <1 s; content kept indefinitely but cold content tiered to cheaper storage; global audience across mobile / web / TV.
Movement 3 — Reason to the design (derive it, don't recite it)
Step 1 — kill the "one file for everyone" problem: transcode into a bitrate ladder. The upload arrives in one format; we re-encode it into several renditions (240p…4K), each a different resolution/bitrate. Now a phone can request 360p and a TV can request 1080p — same video, different bytes. But a whole-file-per-rendition still can't adapt mid-stream: if the phone's bandwidth drops halfway through, it's stuck on whatever rendition it started.
Step 2 — cut every rendition into short segments so the client can switch on the fly. Chop each rendition into 2–10 second segments (HLS or MPEG-DASH). The client fetches a manifest (the list of renditions and their segment URLs), then pulls segments one at a time. Because each segment is an independent little file, the player can fetch segment 5 in 720p, notice bandwidth dropped, and fetch segment 6 in 480p — adaptive bitrate streaming (ABR). Blurry beats buffering: the client always picks the highest rung it can sustain without stalling. Crucially, transcoding is now embarrassingly parallel — different segments and different renditions are independent jobs, so a worker fleet chews through one upload in parallel (the data-parallel/map pattern).
Step 3 — decouple ingest from viewing with a queue + blob store. The upload service shouldn't block while a video transcodes (that can take minutes). It stores the raw master in a blob/object store, drops a job on a message queue (see Introduction to Kafka), and returns. A worker fleet consumes jobs, transcodes, and writes the rendition×segment files back to the blob store. The queue is what lets ingest survive a spike (a celebrity dump of 10,000 uploads) — work backs up in the queue instead of knocking the system over.
Step 4 — the crux for read scale: CDN edge caching. The segment blob store is the origin. If every play read from origin we're back in the 125 Tbps trap. Instead we push/pull popular segments to CDN edge servers in every region. A viewer's player fetches segments from the nearest edge; ~99% of bytes are served from the edge, so origin egress collapses from ~125 Tbps to ~1.25 Tbps. This single decision is what makes global video financially possible — it's not an optimization, it's the architecture of the read path.
Putting it together: upload → (chunked/resumable) upload service → raw blob store → transcode queue → worker fleet fans out the ladder & segments → segment blob store (origin) → CDN edges → client ABR player picks the rung. Two independent systems: a write-once ingest pipeline and a massively-cached read path. Now we size it.
Movement 4 — Build it: the design worksheet
This is the artifact you'd produce in a 45-minute round. Work it in order; every number below is traced, not asserted — recompute it yourself before an interview.
Functional requirements
- Upload a video (large files, must survive flaky connections → resumable/chunked).
- Transcode into a rendition ladder, segmented for HLS/DASH.
- Serve a manifest + segments for adaptive playback on any device.
- Basic metadata (title, owner, thumbnails) and view/like counters (separate read-heavy services).
Non-functional requirements
- Read-optimised & highly available playback (this is the money path); <1 s time-to-first-frame.
- Durable storage (never lose an uploaded master); ingest can be eventually ready (a new upload being watchable in minutes, not milliseconds, is fine).
- Cost-bounded egress — bandwidth is the dominant cost, so maximise CDN offload and tier cold content.
1. Back-of-envelope estimation (BOTE) — arithmetic visible
- Ingest volume: 500 hours/min × 60 × 24 = 720,000 hours of video uploaded/day.
- Storage/day (all renditions). Sum the ladder's bitrates (240p ~0.4 + 360p ~0.7 + 480p ~1.2 + 720p ~2.5 + 1080p ~5) ≈ ~10 Mbps of encoded video per source-hour, across all renditions. Per hour of video that's
10 Mbit/s × 3600 s ÷ 8 = 4,500 MB = 4.5 GB. So daily:720,000 hr × 4.5 GB = 3,240,000 GB ≈ 3.24 PB/day. Over a year:3.24 PB × 365 ≈ ~1,180 PB ≈ ~1.2 EB/yearof new encoded video. (Exabyte scale — this is why cold-tiering is non-optional.) - Transcode compute. The ladder is ~6 renditions; call it ~6 core-hours of CPU per source-hour (segment-parallel, so wall-clock is minutes even though core-hours are large). Daily:
720,000 hr × 6 = 4.32M core-hours/day ÷ 24 = ~180,000 coresrunning continuously. That's a serious fleet — and it's the number that says "transcode-on-upload for the head, on-demand for the cold tail" is a real cost lever (see Optimise). - Egress bandwidth (the beast). ~1 billion hours watched/day, average delivered ~3 Mbps (weighting mobile down, TV up):
1e9 hr × 3600 s × 3 Mbit/s = 1.08×10¹³ Mbit/day. Per second:1.08×10¹³ ÷ 86,400 = 1.25×10⁵ Mbps = ~125 Tbps average; with a ~2× prime-time peak, ~250 Tbps peak. As bytes:1.08×10¹³ Mbit ÷ 8 = 1.35×10¹² MB = ~1,350 PB egress/day. - Read vs write asymmetry.
1,350 PB egress/day ÷ 3.24 PB stored/day ≈ ~417×. We move ~400 times more bytes out than we add — the read path is the whole game. - CDN offload — why the edge is the design. At a 99% edge hit ratio, origin egress = 125 Tbps × (1 − 0.99) = ~1.25 Tbps — a 100× reduction, from "impossible" to "a large-but-buildable origin tier." Watch the sensitivity: drop the hit ratio to 95% and origin egress jumps to
125 × 0.05 = 6.25 Tbps— a 4-point ratio change is a 5× origin load change. Cache hit ratio is the single most load-bearing number in the system (see Break It #2).
2. API sketch
// --- Upload (resumable / chunked) ---
POST /videos -> 201 { video_id, upload_url } // create, get an upload session
PUT /upload/{video_id}?chunk=k -> 204 // idempotent chunk PUT (retry-safe)
POST /upload/{video_id}/complete -> 202 { status: "transcoding" } // assemble + enqueue transcode
// --- Playback ---
GET /videos/{video_id}/manifest.m3u8 -> 200 (HLS/DASH manifest: renditions + segment URLs)
GET /segments/{video_id}/{rendition}/{seg}.ts -> 200 (served from CDN edge, ~99% of the time)
// --- Metadata / social (separate read-heavy services) ---
GET /videos/{video_id} -> { title, owner, thumbnails, duration, view_count }
POST /videos/{video_id}/view -> 204 // async counter increment (its own classic problem)
3. Data model & partitioning
videos(metadata DB):video_id (PK), owner_id, title, status(uploading|transcoding|ready), duration, created_at. Partition byvideo_id(hash) — every access is a single-key lookup, no cross-video joins.renditions:(video_id, rendition) -> manifest_ref, segment_count, codec. The manifest itself (the.m3u8/DASH MPD) is small and cacheable at the edge like any other read.- Segment blob storage: objects keyed
{video_id}/{rendition}/{seg_index}.tsin an object store (S3/GCS-style). This is the origin; the CDN is keyed on the same URL path so an edge miss maps 1:1 to an origin fetch. - Partitioning the read path: segments are immutable and content-keyed by path, so they shard trivially across the object store and cache perfectly (no invalidation problem — a segment never changes, only gets evicted when cold). Hot videos naturally replicate to many edges; the cold long tail lives only at origin + maybe a regional mid-tier cache.
- Tiering: cold masters/renditions (unwatched for N days) move to archive/cold storage (see hot/warm/cold storage tiers); the head stays on hot storage close to the transcode fleet and CDN.
4. High-level diagram — ingest pipeline (top) vs cached delivery (bottom)
The ingest path (blue) is write-once: chunked upload → raw master → queue → worker fleet fans out the ladder and segments → segment blob store. The delivery path (green) reads almost entirely from CDN edges; only an edge miss pulls from the origin blob store. The queue (amber) is the shock absorber that keeps an upload spike from taking down transcode.
5. Rubric — what a strong answer hits
- States the scale assumptions and BOTE arithmetic out loud (uploads/day, storage/day, egress Tbps, CDN offload) before drawing a box.
- Separates the two systems — a write-once ingest/transcode pipeline vs a massively-cached read path — and justifies the CDN by the ~417× read:write asymmetry, not by habit.
- Explains ABR concretely: manifest → segment ladder → client measures its own throughput and steps rungs; "blurry beats buffering."
- Uses a queue + worker fleet for transcode and can say why (decouple ingest latency, absorb upload spikes, parallelize an embarrassingly-parallel job).
- Names CDN edge caching as the read-scale mechanism and can quantify the offload sensitivity (99% → ~1.25 Tbps origin; 95% → ~6.25 Tbps).
- Handles resumable/chunked upload for large files on flaky links, and can defend every row of the trade-off table.
- Model-answer reading: cross-check against the guide's Designing YouTube/Netflix — Transcode, CDN & Adaptive Bitrate, Traced and the classic Designing YouTube or Netflix write-up; the CDN mechanics live in What is CDN, Origin Server vs Edge Server, and Push CDN vs Pull CDN.
Movement 5 — Break it: three traced failures
1. Transcode-queue backlog on a viral upload spike. Steady state, ingest is ~720,000 hours/day = ~500 hours/min of work, and the worker fleet is sized for that plus headroom. Now a coordinated event (a game launch, thousands of creators uploading a reaction within the hour) triples upload rate for 30 minutes. If the upload service transcoded synchronously, every uploader would time out and the API would fall over. Because ingest drops jobs on a queue, the spike simply grows the queue depth: work that would have finished in 2 minutes now finishes in, say, 20 — new videos are watchable later, but nothing crashes, and no upload is lost. The lesson: the queue converts an availability failure (synchronous overload) into a latency degradation (delayed transcode), which is exactly the trade you want for a background job. Scale the fleet on queue depth (autoscaling), and prioritize the queue (a monetized/live upload jumps ahead of a hobbyist's).
2. Origin bandwidth blowup on a cache-miss storm. A video goes viral in a region where it isn't yet cached at the edge. Every one of the first wave of players misses the edge and the edge pulls the same segments from origin — a thundering herd on the origin blob store. Trace the sensitivity from the BOTE: origin egress scales with (1 − hit_ratio), so if a hot event drags the effective hit ratio from 99% to 95%, origin egress jumps from ~1.25 Tbps to ~6.25 Tbps (5×) — potentially past what the origin tier is provisioned for, which then fails more requests, which pushes more load to origin: a feedback spiral. Fixes: request coalescing at the edge (many identical misses collapse into one origin fetch, not thousands), a regional mid-tier cache (shield) between edge and origin so a miss hits the shield not the origin, and pre-warming (push a predicted-viral video to edges before the wave). This is why the cache hit ratio is the single number to defend — it's a multiplier on the most expensive resource in the system.
3. A 20 GB upload dies at 90% on a flaky connection. A creator on hotel Wi-Fi uploads an 18 GB 4K master; at 90% the connection drops. With a naive single POST, the whole upload is lost and they start from zero — and they'll rage-quit before it ever succeeds. The fix is chunked, resumable upload: split the file into (say) 5–10 MB chunks, PUT each independently and idempotently (re-PUT a chunk safely on retry), and track which chunks landed. On reconnect the client asks "which chunks do you have?" and resends only the missing ones. The complete call assembles them server-side and enqueues transcode. Lesson: for large files over the public internet, the upload itself is a distributed-systems problem — you design for partial failure and retries, not a happy-path single request.
Movement 6 — Optimise, with trade-offs vs named alternatives
| Decision | Option A | Option B | Pick when |
|---|---|---|---|
| When to transcode | Transcode-on-upload (precompute the full ladder eagerly) | Transcode-on-demand (lazily encode a rendition the first time it's requested) | On-upload for the head (popular content must be instantly streamable, and you want the ladder ready before the viral wave). On-demand (or a smaller eager ladder) for the cold tail — with ~180K cores of transcode cost, precomputing 4K for videos nobody watches is pure waste. Real systems do hybrid: eager for the common rungs, lazy for the rare ones. |
| Delivery format | HLS/DASH adaptive (segmented ladder) | Single-bitrate progressive download | Adaptive almost always at this scale — it's the only thing that serves a 3G phone and a 4K TV from one video and adapts mid-stream (blurry beats buffering). Single-bitrate only for a controlled environment where every client has known, stable bandwidth (an internal kiosk) — it can't handle the open internet's variance. |
| Getting content to the edge | Push-to-CDN (proactively replicate to edges) | Pull-through / origin-pull (edge fetches on first miss, then caches) | Pull-through as the default — you can't afford to push 1.2 EB/yr of the long tail to every edge; let popularity decide what caches. Push selectively for known hot content (a Netflix original's launch night, a scheduled premiere) to pre-warm edges and dodge the cache-miss storm (Break It #2). Most real CDNs combine both. |
| Rendition storage for the tail | Store all renditions for every video forever, all on hot storage | Transcode-popular-only + tier cold masters/renditions to archive storage | Tier + selective for the tail — at ~1.2 EB/yr, keeping every 4K rendition of every unwatched video on hot storage is unaffordable. Keep the master durable, keep popular renditions hot near the CDN, and push cold content to cheap archive; re-transcode-on-demand if a dormant video suddenly gets views. Store-all-hot only makes sense for a small catalog where storage is trivial next to engineering effort. |
| View-count accuracy | Exact, synchronous increment on every play | Async / eventually-consistent counter (aggregate off a stream) | Async almost always — a synchronous DB write on the hottest read path (every play) would bottleneck the money path for a number nobody needs to be exact in real time. Aggregate view events off a queue/stream; accept a slight lag. Exact-synchronous only where the count is transactional (e.g. paid-per-view billing). |
Movement 7 — Defend under drilling
Q1 — "Walk me through exactly what happens when I hit play."
The player first fetches the manifest (.m3u8/DASH MPD) — the list of available renditions and, per rendition, the ordered segment URLs. To hit the <1 s time-to-first-frame target it deliberately starts at a low rung so the first segment downloads fast, then requests segments from the nearest CDN edge. As it downloads, it measures its own throughput and buffer level and, for each next segment, requests the highest bitrate it can sustain without draining the buffer. ~99% of those segment bytes come from the edge; the origin is only touched on an edge miss.
Q2 — "How does adaptive bitrate actually decide which resolution to pick?"
The client (not the server) runs the ABR algorithm. Two signal families: throughput-based (estimate recent download bandwidth from how fast the last few segments arrived, pick the highest rung below that estimate with a safety margin) and buffer-based (if the playback buffer is draining toward empty, step down aggressively even if bandwidth looks fine; if the buffer is comfortably full, step up). Production players (e.g. BOLA-style, or hybrids) blend both. The design invariant is blurry beats buffering — a lower rung that plays smoothly always wins over a higher rung that stalls. This is why we segment: switching rungs is just "request the next 4-second chunk from a different rendition," no re-buffering the whole stream.
Q3 — "A video suddenly goes viral in a region where it's not cached. What happens, and what do you do?"
The first wave misses the edge, so edges pull from origin — a thundering herd (Break It #2). Three defenses, layered: request coalescing at the edge so N identical concurrent misses become 1 origin fetch; a regional shield / mid-tier cache so an edge miss hits the shield, not the origin; and predictive pre-warming — signals like a spiking view rate or a scheduled premiere trigger pushing the segments to edges ahead of the wave. Because segments are immutable, once they're at the edge the video serves from cache indefinitely with zero invalidation risk. The origin only ever sees the thin leading edge of the wave, not its bulk.
Q4 — "Why a queue for transcode? Why not just transcode in the upload request?"
Transcoding a video into the full ladder takes minutes and ~6 core-hours — you can't hold an HTTP request open that long, and doing it inline couples upload availability to transcode capacity, so an upload spike would take down the upload API itself. The queue decouples them: ingest returns immediately after durably storing the master and enqueuing; the worker fleet drains the queue at its own pace and autoscales on queue depth. It also lets us prioritize (monetized/live jumps the line) and retry failed transcodes without the uploader ever knowing. The cost is that a new video is watchable in minutes, not instantly — acceptable for VOD.
Q5 — "How would you handle live streaming instead of VOD?"
Live flips two things. First, you can't precompute — you transcode in near-real-time as the stream arrives, producing segments continuously with a target end-to-end latency of a few seconds (low-latency HLS/DASH shrink segments to sub-second and use chunked transfer). Second, the manifest is a sliding window that keeps appending new segments rather than a fixed list. Everything else survives: still a rendition ladder, still ABR on the client, still CDN edges (with very short cache TTLs on the live segments and manifest). The hard new constraint is the latency budget on the ingest+transcode path, where VOD had all the time in the world.
Q6 — The 100× escalation: "Now it's 100× the traffic — ~12,500 Tbps average egress. What breaks first, and what do you do?"
Storage and transcode scale horizontally almost linearly (more blob capacity, more workers off the same queue) — those aren't the wall. The wall is egress: 12,500 Tbps is beyond any single CDN's capacity and beyond what you'd want any one region's peering to carry. Answers: (1) multi-CDN — spread across several CDN providers with steering by cost/health/geography (also removes the single-CDN SPOF); (2) push harder on hit ratio — every point of hit ratio is a multiplier on origin egress, so at this scale even 99.9% vs 99% is enormous, justifying bigger edges and smarter pre-warming; (3) ISP-embedded caches (Netflix Open Connect / Google's edge nodes physically inside ISP networks) so the hottest content is served from inside the last-mile network and never crosses a peering link at all; (4) tier the cold long tail ruthlessly so hot storage and premium edge capacity are spent only on content that's actually watched. The through-line: at 100×, the problem is entirely "get bytes as close to the eyeball as possible and serve as few as possible from origin."
Movement 8 — You can now defend
- You can explain, with numbers, why video is a bandwidth problem not a storage problem — deriving ~125 Tbps egress and the ~417× read:write asymmetry rather than asserting "it's big."
- You can derive the rendition ladder + segmentation + ABR from the concrete failure of "one file for everyone," and explain how the client (not the server) picks a rung: blurry beats buffering.
- You can name CDN edge caching as the read-scale mechanism and quantify its leverage — 99% offload drops origin egress 100× — plus the sensitivity that makes hit ratio the most load-bearing number in the system.
- You can separate the write-once ingest/transcode pipeline (queue + worker fleet + blob store, resumable upload) from the cached read path, and defend each optimization (on-upload vs on-demand transcode, push vs pull CDN, tiering) as a trade-off against a named alternative — then survive the live-streaming and 100× escalations.
Re-authored/Deepened for this guide. Model-answer reading: Designing YouTube/Netflix — Transcode, CDN & Adaptive Bitrate, Traced and Designing YouTube or Netflix (System Design Problems). Mechanisms cross-referenced with What is CDN, Origin Server vs Edge Server, Push CDN vs Pull CDN, Introduction to Kafka (the transcode queue), and Storage Estimation for the BOTE.
🤖 Don't fully get this? Learn it with Claude
Stuck on Design YouTube / Netflix? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Build the mental picture, not memorization.
I just read a lesson on **Design YouTube / Netflix** (Hands-On Builds) and want to truly understand it. Explain Design YouTube / Netflix from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Socratic — adapts to where you're stuck.
Teach me **Design YouTube / Netflix** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Active recall exposes what you missed.
Quiz me on **Design YouTube / Netflix** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Intuition + hook + flashcards for long-term memory.
Help me remember **Design YouTube / Netflix** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.