"Design YouTube" is where two systems live in one product, pulling in opposite directions. The write side is a brutal batch-processing problem: someone uploads a multi-gigabyte file, and you have to turn it into a dozen resolutions and formats without melting your servers. The read side is a caching problem at planetary scale: billions of people press play and expect video to start in under a second, anywhere on Earth. The interview is about keeping those two apart — a queue-and-worker transcoding pipeline for the write, and a CDN for the read. This is the condensed walkthrough; the full guide (estimates, API, data model, and the full production .NET 9 code) is on my site 👇 Full guide: https://prepstack.co.in/blog/design-youtube-system-design The design at a glance Concern Decision Upload Resumable, chunked upload straight to blob storage Processing Transcoding pipeline — queue + stateless worker fleet, chunk → transcode → package Storage Object/blob storage for raw + renditions (petabyte scale, tiered) Delivery CDN at the edge — the origin sees only cache misses Playback Adaptive bitrate (HLS/DASH) — the player picks quality by bandwidth View counts Approximate + aggregated — never a DB increment per view It's two systems sharing a blob store The two sides scale very differently: Uploads: ~1M videos/day, raw files tens of MB-GBs -> petabytes of storage, huge transcode compute Views: billions/day -> ~tens of thousands of segment fetches/sec, served from the CDN edge Read:write ratio: enormous -> the CDN, not the origin, carries the traffic Storage and transcode compute dominate the write side; the CDN is not optional on the read side — it's the only way billions of views don't vaporize your origin. Uploader --> [ Upload service ] == resumable, chunked ==> [ Blob: raw upload ] | event: uploaded v [ Transcoding pipeline - queue + worker fleet ] chunk -> transcode (144p...4K, multi-codec) -> package (HLS/DASH) | v [ Blob: renditions ] --> [ CDN edge ] --> Viewers [ Metadata DB ] [ View counter (aggregated) ] The write pipeline (upload → transcode → store) and the read path (CDN → viewer) meet only at the blob store. Playback never waits on a transcoder; uploads never touch the CDN. The hard parts Resumable, chunked upload. A multi-gigabyte upload over mobile will drop mid-transfer; a single POST that restarts from zero is unusable. The client uploads fixed-size chunks (with offsets) directly to blob storage via a pre-signed URL, and a dropped connection resumes from the last good chunk. The app server issues the URL and reacts to the completion event — it never proxies the bytes. The transcoding pipeline — the write core. A raw upload must become many renditions (144p→4K) across codecs, packaged for streaming: Chunk the raw video into segments so they transcode independently. Transcode each segment to each target resolution/bitrate — an embarrassingly parallel fan-out across a stateless worker fleet (this is where the compute goes). Package the transcoded segments into HLS/DASH + a manifest. It's a DAG of jobs driven by a message queue: workers pull segment-transcode tasks, scale horizontally with queue depth, and are idempotent and retried (a failed segment re-runs; the pipeline dead-letters what won't). Adaptive bitrate streaming (HLS/DASH). The packaged video is a set of short segments at multiple bitrates plus a manifest. The player downloads the manifest, starts at a modest bitrate, and switches up or down per segment based on measured bandwidth. Player reads the manifest, then picks a rendition per segment by bandwidth: 4K ############ ~25 Mbps <- fast wifi 1080p ######## ~8 Mbps 720p ##### ~5 Mbps 480p ### ~2.5 Mbps 240p # ~0.7 Mbps <- weak mobile The crucial part: the server serves static segments — all the adaptation logic lives in the player. That's what makes delivery a pure CDN problem. CDN delivery — the read path. Views dwarf uploads and viewers are global. Serve every segment from a CDN: edge caches near the viewer hold popular content, so the origin (blob store) is hit only on a cache miss. This is what makes playback start fast worldwide and keeps billions of views from ever reaching your origin. The CDN is the read architecture. View counts at scale. Billions of views can't each UPDATE views SET count = count + 1 — a hot-row meltdown. Aggregate: buffer increments (in Redis or a stream), flush periodic batches to the metadata store, accept approximate, eventually consistent counts. Scaling gotchas Transcode compute is the write bottleneck — a big worker fleet autoscaled by queue depth, often on cheap spot instances (jobs are idempotent, so eviction is fine). CDN absorbs the read load; pre-warm popular videos at the edge. Storage is petabyte-scale — tier cold, rarely-watched videos to cheap storage. Pipeline resilience — idempotent jobs, retries, a DLQ for un-transcodable uploads. Never proxy the bytes through your app server — the client talks to blob storage directly; your server orchestrates. I shipped this shape in production (Mattrx) Mattrx isn't a video site, but its report generation runs on the exact same shape: a heavy artifact produced by an async worker fleet, stored in blob storage, and delivered from a CDN. Marketers export branded PDF reports of campaign performance — Mattrx renders about 1.2 million every 48 hours with PuppeteerSharp. V1 generated the PDF synchronously inside the request: a big report took 30–60 seconds, tied up a web worker, timed out under load, and was streamed back through the app server. We rebuilt it as this design — enqueue on Azure Service Bus, render on a worker fleet, drop the PDF in Azure Blob Storage, serve it from Azure Front Door (CDN) via a signed URL: Metric Before After Report request Blocked 30–60s in the request (timeouts under load) 202 Accepted, enqueue p95 ~90 ms Generation Synchronous, tied up a web worker Async worker fleet (PuppeteerSharp) Throughput Capped by the web tier ~1.2M reports / 48h Delivery Streamed through the app server Azure Front Door CDN (signed URL, edge) API write-path p95 Dragged down by report load 120 ms (fully decoupled) Worker cost Baseline ~$1,300/mo saved (right-sized async fleet) The API does no rendering — it writes a pending row, drops a command on Service Bus, and returns a job id in ~90 ms; the worker fleet does the expensive PuppeteerSharp render and stores the PDF in Blob Storage; and delivery is a signed URL fronted by Azure Front Door, so the finished report streams from the edge — the same decoupling that keeps YouTube's playback independent of its transcoders. (Full .NET 9 API + worker is in the post.) The model to carry forward A video platform is two decoupled systems sharing a blob store. The write side is a queue-and-worker batch pipeline — resumable upload, parallel transcoding across a stateless fleet, packaged into adaptive segments — and the read side is a CDN in front of static files that carries essentially all the traffic. Keep them apart: playback depends only on the blob store and the CDN, never on the transcoders; uploads never touch the read path. Three habits it teaches: split the write pipeline from the read path (batch-process on workers, serve static from a CDN, let them meet only at the blob store); never move big bytes through your app server (clients talk to storage directly); approximate what's expensive to be exact (view counts are aggregated and eventually consistent). That wraps my System Design Interview series — ten "Design X" walkthroughs on one framework. The full guide has the estimates, API, data model, all the hard parts in depth, scaling, the complete production .NET 9 pipeline, and the "when it's overkill" section: https://prepstack.co.in/blog/design-youtube-system-design Originally published on PrepStack.