7. Video Streaming (YouTube) Advanced

A. Realistic Interview Prompt

"Design a video streaming platform like YouTube: users upload videos, the platform processes and stores them, and other users can watch them smoothly regardless of device or network conditions."

The defining shape of this problem is that upload and playback are almost entirely separate systems sharing only a video ID — upload is a slow, resource-intensive, asynchronous pipeline (transcoding into many formats can take longer than the video's own runtime); playback is a latency-critical, massively-fanned-out read path that must adapt to wildly varying device and network conditions. Conflating the two into one synchronous flow is the single most common mistake.

B. Difficulty

Advanced — there's a simple-sounding naive answer ("store the file, serve the file"), but it fails for a reason most candidates haven't personally encountered (files formatted for one device stutter or fail entirely on another), making the core insight — adaptive bitrate streaming — genuinely non-obvious rather than a scale-driven afterthought like most of this document's other V1-to-V2 transitions.

C. What This Problem Tests

D. Requirements

Functional

  1. Users can upload a video; the platform processes it into a form suitable for streaming
  2. Users can watch any processed video, with playback quality adapting to their device and network conditions
  3. Users can search for videos by title/metadata and see basic engagement info (view count)

Non-Functional

Out of scope: live streaming (a materially different, lower-latency real-time pipeline), monetization/ads, detailed content moderation.

E. Clarifying Questions

#QuestionWhy It MattersDesign Decision Affected
1Is live streaming in scope, or only pre-recorded, on-demand video?Live streaming has fundamentally different (much lower) latency tolerances and can't use the same batch-transcoding pipelineAssumptions (F)
2How quickly must a video become watchable after upload?Directly bounds how much transcoding must complete before "publish," versus continuing in the background for additional quality levelsDeep Dive 2 (N)
3Do we need exact view counts in real time, or is an approximate, slightly-delayed count acceptable?Exact real-time counting at billions of views/day is a much harder problem than approximate, batched countingDeep Dive 6 (R)
4Is personalized recommendation in scope, or just search/browse?A recommendation engine is effectively its own large ML systemDeep Dive 7 (S)
5What's the range of target devices/networks — mobile on cellular, desktop on fiber, both?Confirms adaptive bitrate streaming (multiple quality levels) is necessary, not optionalDeep Dive 3 (O)

F. Assumptions

G. Capacity Estimation

MetricValue / CalculationWhy It Matters
Video uploaded/minute500 hours/minute (illustrative, YouTube-scale)Base input for the upload/transcoding pipeline's throughput requirement
Views/day5 billionBase input for playback/delivery scale — dwarfs upload volume by orders of magnitude, confirming this is a read-optimize-first problem
Views/sec (average)5B ÷ 86,400s ≈ ~58,000/sec (peak several hundred thousand/sec)Sets the scale the CDN/delivery layer must sustain — far beyond what any origin server fleet alone could serve directly
Raw upload storage/day500 hours/min × 60 × 24 ≈ 720,000 hours/day of raw footage; at ~1GB/hour (compressed source) ≈ ~720TB/day of raw uploadsConfirms a blob store, not a database, is mandatory for raw video (K)
Post-transcode storage multiplierEach video is re-encoded into ~5-10 quality levels/resolutions (O) — roughly a 5-10× storage multiplier over the raw uploadThe real, often-underestimated storage cost of adaptive bitrate streaming — a direct, quantified consequence of the O deep dive
CDN cache-hit requirementTo sustain ~58,000+ views/sec, the overwhelming majority must be served from edge caches, not originDirectly motivates the CDN deep dive (P) — origin-only serving cannot come close to this throughput

H. Core Entities

EntityPurposeKey Fields
VideoA logical video, independent of any specific encoded formatvideo_id, uploader_id, title, status (uploading/processing/ready), created_at
VideoVariantOne transcoded rendition (a specific resolution/bitrate)video_id, resolution, bitrate, storage_ref, codec
ManifestThe adaptive-streaming index listing all available variants/segments for a videovideo_id, list of VideoVariant references + segment URLs (O)
ViewEvent (ephemeral/aggregated)Raw signal feeding the view-count pipelinevideo_id, viewer_id, timestamp — aggregated asynchronously (R), never read directly at serve time

I. API Design

POST /v1/videos/upload-url        -> { upload_url, video_id }   (pre-signed direct-to-blob-store URL, M)
POST /v1/videos/{id}/complete     -> triggers transcoding pipeline (N)
GET  /v1/videos/{id}/manifest     -> { variants: [...], segment_urls: [...] }  (O)
GET  /v1/videos/{id}               -> { title, view_count (approx), status }
GET  /v1/search?q=                -> [{ video_id, title, ... }]

Why a pre-signed upload URL directly to blob storage, not through the application server: a video file can be gigabytes; routing that entire payload through an application server adds a slow, expensive hop for zero benefit — a pre-signed URL lets the client upload directly to the blob store, with the application server only involved before (issuing the URL) and after (receiving a "complete" notification) the actual byte transfer. The same "don't put large binary payloads through your application tier" principle used for media in News Feed and Chat System, just applied to the upload direction instead of only the read/serve direction.

J. Basic / Naive High-Level Design (V1)

The simplest thing that satisfies the functional requirement in isolation: store the uploaded file as-is, and serve that exact same file directly to every viewer on request.

flowchart LR U(["Uploader"]) -- "raw video file" --> Server["App Server"] Server --> Store[("Blob Storage
one file per video")] V(["Viewer"]) --> Server Server -- "serves the same raw file
to every viewer" --> Store

V1 — one file, one format, served identically to every viewer regardless of device or connection.

Why this breaks immediately, not eventually: a single fixed-resolution, fixed-bitrate file cannot serve both a phone on a weak cellular connection and a desktop on fiber well — the phone will stall/buffer trying to download a high-bitrate file faster than its connection allows, or the desktop viewer gets an unnecessarily low-quality experience if the file was encoded low to accommodate weak connections. Unlike most of this document's other V1s (which work fine at small scale and degrade only under load), this V1 delivers a visibly broken experience for a large fraction of real-world viewers on day one, regardless of traffic volume — the core problem here is heterogeneity, not scale.

K. Data Model & Store Choice

Raw uploads and transcoded variants: blob storage (S3-class object storage) — video files are large, write-once (per variant), and read via range requests during playback, exactly the access pattern object storage plus a CDN in front of it (P) is built for.

Video/variant/manifest metadata: a relational or wide-column store keyed by video_id, holding titles, status, and pointers to each variant's blob location — small, structured, frequently queried data that belongs in a proper database, kept entirely separate from the large binary payloads it points to, the same metadata/blob split used throughout this document (News Feed's media, Chat System's attachments, Web Crawler's page content).

L. End-to-End Flow — Upload to Watchable

sequenceDiagram participant U as Uploader participant API as API Server participant Blob as Blob Store participant MQ as Message Queue participant TC as Transcoding Pipeline participant CDN as CDN U->>API: request upload URL API-->>U: pre-signed URL + video_id U->>Blob: direct upload (raw file) U->>API: mark upload complete API->>MQ: publish "process_video" event MQ->>TC: consume event TC->>TC: transcode into variants (N) TC->>Blob: store each variant + generate manifest TC->>API: mark video status = ready Note over CDN: On first viewer request, variants populate into CDN edge caches (P)

M. Deep Dive 1 — The Upload Pipeline

Requirement this addresses: the upload-durability NFR (D) — this is the write path, and it must never lose a user's uploaded content even though the file is large and the subsequent processing is slow.

Why direct-to-blob-store upload via a pre-signed URL, not through the API server: already introduced in I — worth restating here as the actual mechanism, not just the API shape. The API server's only job is issuing a short-lived, scoped credential (the pre-signed URL) and later receiving a lightweight "upload finished" notification; the multi-gigabyte transfer itself never touches application-tier compute at all, meaning the upload path's scalability is entirely the blob store's problem, not the API fleet's.

Chunked/resumable upload: for large files over unreliable networks, the upload itself is typically chunked (multipart upload, a standard blob-store feature) so a dropped connection mid-upload only requires resuming the missing chunks, not restarting a multi-gigabyte transfer from zero — directly serving the durability NFR by making partial-failure recovery cheap.

N. Deep Dive 2 — The Transcoding Pipeline

Requirement this addresses: the adaptive-playback NFR (D) — a single uploaded file must become many device/network-appropriate variants before it's genuinely watchable everywhere.

flowchart LR Raw[("Raw upload")] --> Queue["Transcode Job Queue"] Queue --> W1["Transcode Worker:
360p"] Queue --> W2["Transcode Worker:
720p"] Queue --> W3["Transcode Worker:
1080p"] Queue -.-> WN["... additional resolutions"] W1 --> Manifest[("Manifest generation (O)")] W2 --> Manifest W3 --> Manifest

Each resolution/bitrate variant is an independent, parallelizable transcoding job off the same raw source.

Why this must be async and queue-driven, not a synchronous step in the upload request: transcoding a video into multiple quality levels is CPU-intensive and can take longer than the video's own runtime — holding an upload request open for that duration is a non-starter, the same "never put slow, heavy work on the critical path of an acknowledgment" principle used for fanout in News Feed and Chat System, just applied to a far heavier per-item workload (minutes of compute per video, versus milliseconds per fanout write).

Why parallelizing across resolutions, not just across videos: each resolution/bitrate variant is an independent transcoding job over the same raw source, so a video's variants can all be processed concurrently by different workers rather than serially — directly shortening the time-to-first-watchable-quality, which matters because the lowest-resolution variant finishing first is typically what unblocks "publish" (F's assumption) while higher resolutions continue in the background.

O. Deep Dive 3 — Adaptive Bitrate Streaming

Requirement this addresses: directly fixes V1's fatal flaw (J) — this is the actual real-world mechanism (HLS/DASH) that makes smooth playback across heterogeneous devices and networks possible at all.

How it actually works: each transcoded variant (N) is further split into short segments (typically a few seconds each); a manifest file (H) lists every available variant and its segment URLs. The video player downloads the manifest first, then continuously monitors its own current network throughput and switches which variant it's pulling segments from, in real time, between segments — dropping to a lower-bitrate variant if the network degrades mid-playback, and stepping up if it improves — all without interrupting playback or requiring a page reload.

sequenceDiagram participant Player participant CDN Player->>CDN: GET manifest.m3u8 CDN-->>Player: list of variants + segments Player->>CDN: GET segment_1 (720p, initial guess) Note over Player: network throughput measured alt network degrades Player->>CDN: GET segment_2 (360p, adapts down) else network improves Player->>CDN: GET segment_2 (1080p, adapts up) end

Why this is the actual answer, not just "compress the video better": no single fixed encoding can be simultaneously right for a phone on congested cellular and a desktop on fiber, and a viewer's network conditions can change mid-playback (walking out of WiFi range) — adaptive bitrate directly solves both the heterogeneous-audience problem and the changing-conditions-mid-session problem, which a single static file, no matter how well compressed, structurally cannot.

P. Deep Dive 4 — CDN Delivery at Scale

Requirement this addresses: the global low-latency delivery NFR (D) and the ~58,000+ views/sec throughput from G — no origin server fleet can serve this volume directly, and every viewer being served from one region would add unacceptable latency for everyone else.

Why a CDN, not scaling the origin fleet directly: video segments are immutable once transcoded (a given variant's given segment never changes) — precisely the profile a CDN's edge-caching model is built for. Once a segment is cached at an edge node near a viewer, that edge node can serve it directly with no origin round-trip at all, the same cache-aside-at-the-edge principle used for redirects in URL Shortener, just applied to video segments at a much larger payload size.

The long-tail problem this creates: a small number of viral videos get requested constantly (naturally staying hot in every relevant edge cache), while the vast majority of uploaded videos are watched rarely — meaning most content is effectively "cold" at most edges most of the time, and the first viewer in a given region for a given video always pays a real origin round-trip (a cache miss) before that segment becomes locally cached. This is an accepted, bounded cost — a design choice, not a flaw — since caching every video everywhere unconditionally would be a far worse trade than occasionally eating one origin round-trip for genuinely rare content.

Q. Deep Dive 5 — Metadata & Search

Requirement this addresses: the search functional requirement (D) — a genuinely different access pattern (full-text over titles/descriptions) from the manifest/segment delivery path in O and P.

Why this is a separate system from video delivery, sharing only the video_id: the same reasoning as the Twitter/News Feed problem's search deep dive — an inverted index (Elasticsearch/OpenSearch-class) populated asynchronously off the same "video published" event already used to trigger notifications/availability, decoupled entirely from the manifest-serving path so a search-indexing lag never affects playback, and a playback issue never affects search.

R. Deep Dive 6 — View Counting at Scale

Requirement this addresses: the view-count functional requirement (D), under the approximate-counting assumption from F — 5 billion views/day (G) makes a strongly-consistent, exactly-accurate counter incremented synchronously on every single view an unnecessary and expensive goal.

Why counting can't happen synchronously on the playback path: incrementing a durable counter on every single segment/manifest request would add write load directly onto the hottest, most latency-sensitive path in the entire system — exactly backwards, mirroring the same "don't put expensive work on the highest-frequency path" lesson from URL Shortener's analytics deep dive and News Feed's fanout reasoning.

The fix: emit a lightweight ViewEvent (H) asynchronously (e.g. once playback crosses a minimum-watched threshold, to avoid counting accidental clicks) onto an event stream; a separate, async aggregation pipeline batches and periodically updates each video's displayed count. The displayed number is allowed to lag real-time by seconds-to-minutes — an explicitly accepted trade, not a bug, that removes counting entirely from the playback critical path.

S. Deep Dive 7 — Recommendations (Conceptual)

Discussed at the level this interview typically expects, per F's assumption — the conceptual shape and how it plugs into the existing pipeline, not a full ML recommendation system derivation.

Why this is a layer on top of the existing metadata/view-event streams, not a new data-collection system: the same ViewEvent stream already powering R's approximate view counts is also the natural raw signal for a recommendation model (what did this viewer watch, for how long, after watching what else) — recommendations consume the same async event stream through a different, independent aggregation/model-serving path, rather than requiring new instrumentation. This mirrors how ranking was layered onto News Feed's existing fanout/store mechanism without changing its plumbing.

T. Full Solution at Scale — Final Architecture

flowchart TB U(["Uploader"]) -- "pre-signed URL (M)" --> Blob[("Blob Store — raw upload")] U --> API["API Server"] API --> MQ["Message Queue"] subgraph TRANSCODE [" Transcoding Pipeline (N) "] direction LR W1["360p worker"] W2["720p worker"] W3["1080p worker"] end MQ --> TRANSCODE TRANSCODE --> VariantStore[("Blob Store — variants + Manifest (O)")] V(["Viewer"]) --> CDN["CDN Edge (P)"] CDN -- "cache miss" --> VariantStore CDN -- "adaptive segment requests (O)" --> V API --> SearchIdx["Search Index (Q)"] V -.-> ViewEvents["ViewEvent stream (R)"] ViewEvents --> Aggregator["Async Count Aggregator"] ViewEvents -.-> Recs["Recommendation Pipeline (S)"]

Everything above is the composition of M through S — no new component introduced here.

U. Design Evolution

What stays stable throughout: the video_id-keyed manifest contract from I — every version underneath changes without the player needing to change how it fetches or plays a video.

V. Consistency Model

Deliberately eventually consistent almost everywhere, matching the same overall posture as News Feed and Chat System's non-ordering-critical paths: view counts (R) lag real-time by design; search results (Q) lag publish by an indexing delay; CDN edge caches (P) can briefly serve a stale variant list until propagation completes. The one place closer-to-immediate correctness matters is upload durability itself (M) — once the platform acknowledges an upload as complete, that raw file must not be lost, since unlike a delayed view count, a lost upload is unrecoverable and directly visible to the user who created it.

W. Failure Scenarios

X. Bottlenecks & Scaling Boundary

StageActual BottleneckFix Applied
V1 single-file servingFundamentally wrong for heterogeneous devices/networks, independent of traffic volumeAdaptive bitrate streaming via manifest + variants (O)
Synchronous transcoding on uploadHolds the upload request open for minutes of CPU-intensive workAsync, queued transcoding pipeline (N)
Origin-only video servingCannot sustain tens of thousands of views/sec globally with acceptable latencyCDN edge caching (P)
Synchronous view-count increments on the playback pathAdds write load directly onto the hottest, most latency-sensitive pathAsync, threshold-based, batched view-event aggregation (R)

Y. Design Trade-Offs

DecisionOption AOption BTrade-off
Playback formatSingle fixed-quality fileAdaptive bitrate (manifest + variants, chosen)Single file is simpler and cheaper to store; adaptive streaming is the only approach that actually serves heterogeneous devices/networks well, at a 5-10× storage multiplier
Transcoding timingFully synchronous before publishBaseline-quality-first, rest async (chosen)Fully synchronous guarantees every quality level is ready before publish but delays time-to-watchable significantly; baseline-first gets content watchable sooner at the cost of a brief window with fewer quality options
View countingSynchronous, exact counterAsync, approximate, batched (chosen)Synchronous is exact but adds load to the hottest path in the system; async trades a seconds-to-minutes lag for removing counting from the critical path entirely
DeliveryServe from origin directlyCDN edge caching (chosen)Origin-only is simpler operationally but cannot approach the required global throughput/latency; CDN caching is mandatory at this scale, at the cost of cache-miss handling for cold/rare content
Upload pathThrough the application serverDirect-to-blob-store via pre-signed URL (chosen)Routing through the app server is simpler to reason about but puts multi-gigabyte transfers on application-tier compute for no benefit; direct upload keeps that compute free entirely

Z. Common Bad Designs

AA. Interviewer Follow-Ups

Follow-UpDesign ChangeComponents ChangedComponents Unchanged
"A video suddenly goes viral — what happens?"Directly exercises P's CDN model — the video naturally becomes hot at every relevant edge, requiring no special-case handlingNone — this is exactly what edge caching already handlesEverything
"Support live streaming, not just on-demand."A genuinely different, much lower-latency pipeline (segment durations shrink dramatically, no batch transcoding) — named as materially out of this design's scope (F)Effectively a new, parallel systemOn-demand upload/transcode/delivery for pre-recorded content
"Reduce storage costs from all these transcoded variants."Transcode less-popular resolutions lazily/on-demand instead of eagerly for every upload, or age out rarely-requested variantsTranscoding trigger policy (N)Manifest format, CDN delivery mechanics
"Add closed captions/subtitles."An additional async processing job off the same publish event (speech-to-text), stored and served like another manifest trackNew async pipeline consumerCore transcoding, delivery, view counting
"How would you personalize the homepage feed?"Directly extends S's conceptual recommendation layer into an actual ranked feed — acknowledge it's a large ML system, describe the event-stream input, don't design the model liveNew consumer of the existing ViewEvent streamDelivery, transcoding, upload pipeline

AB. Interviewer Evaluation Rubric

AC. Interview Simulation

Interviewer: Design a video platform like YouTube — upload, and smooth playback for anyone watching.
Candidate: I'd split this into two mostly-separate systems that share only a video ID: uploading and processing, and watching. They have very different requirements — upload cares about durability and can tolerate being slow; playback cares about latency and needs to work well across wildly different devices and network conditions.
Interviewer: Why can't you just store the uploaded file and serve that directly?
Candidate: Because one fixed encoding can't serve both a phone on weak cellular and a desktop on fiber well — and a viewer's network can change mid-playback anyway. The real answer is adaptive bitrate streaming: transcode the video into several quality levels, split each into short segments, and have the player continuously monitor its own network throughput and switch which quality level it's pulling from, segment by segment, without interrupting playback.
Interviewer: Where does the transcoding happen, and when?
Candidate: Asynchronously, off a queue, triggered right after the raw upload finishes — never on the upload request itself, since encoding multiple quality levels can take longer than the video's own runtime. I'd let the lowest quality level unblock "publish" so the video becomes watchable quickly, while higher resolutions keep processing in the background.
Interviewer: How do you serve this at real scale — tens of thousands of views a second globally?
Candidate: Through a CDN, not the origin directly. Video segments are immutable once transcoded, so they're a great fit for edge caching — once a segment is cached near a viewer, there's no origin round-trip at all. The trade-off is that most videos aren't viral, so the first request for a rarely-watched video in a given region always eats one real origin round-trip before it's cached locally — that's an accepted cost, not a flaw.
Interviewer: Last one — view counts. Does every playback request update a counter?
Candidate: No — that would put write load directly on the hottest path in the system for a number that doesn't need to be exact or instant. I'd emit a lightweight event once playback crosses some minimum-watched threshold, and aggregate those asynchronously into the displayed count, accepting a small lag.

AD. Online Solution Comparison

SourceApproachStrengthWeaknessWorth Learning
Hello InterviewUpload/watch-path separation framing, explicit adaptive-bitrate-streaming-as-the-core-insight discussion, staff-level CDN/long-tail treatmentClearest articulation of adaptive bitrate as the genuine non-obvious insight of this problem, not just a scale afterthoughtLighter on the specific view-counting pipeline mechanicsThe upload/watch separation framing adopted as the structural spine of this whole section
ByteByteGoDetailed transcoding-pipeline and CDN-architecture breakdown with concrete component diagramsThe most thorough treatment of the transcoding worker pool's internals of any source reviewedLess depth on the recommendation-layer conceptual tie-inThe parallel-per-resolution transcoding worker pattern adapted into N
donnemartin/system-design-primer (GitHub)General large-scale media-serving architecture applicable directly to video, covering CDN and blob-storage patternsGood grounding in the general "immutable large binary content behind a CDN" pattern reused throughout this sectionNot video-specific — requires translating general media-serving advice into the adaptive-bitrate-streaming domainThe general CDN-cache-aside reasoning, consistent with the same pattern already established for URL Shortener redirects and News Feed media
HLS / MPEG-DASH specifications (industry standards)The actual real-world manifest+segment protocols underlying adaptive bitrate streamingGround-truth confirmation that the manifest/segment/adaptive-switching mechanism described in O is a real, deployed standard, not a hypotheticalWritten as technical protocol specs, not interview-oriented walkthroughs — needs translation, which O providesUnderstanding that "the manifest" and "adaptive switching" in O map directly onto real HLS .m3u8/DASH .mpd files used in production today

AE. Interview Cheat Sheet

ProblemVideo Streaming (YouTube)
DifficultyAdvanced (the core insight — adaptive streaming — is genuinely non-obvious, not just a scale-driven refinement)
Core requirementsUpload + async processing, adaptive on-demand playback, search, approximate view counts; 500hrs/min uploaded, 5B views/day, ~58K views/sec
Central decisionUpload and playback are separate systems sharing only a video_id — different latency, throughput, and durability needs entirely
Core mechanismAdaptive bitrate streaming — manifest + segmented multi-quality variants, with the player switching quality in real time based on measured network throughput
TranscodingAsync, queue-driven, parallelized per-resolution; baseline quality unblocks publish while higher resolutions finish in the background
DeliveryCDN edge caching is mandatory at this scale — origin-only cannot approach the required global throughput/latency; long-tail cold content accepts occasional cache misses
View countingAsync, threshold-based, batched aggregation — never a synchronous increment on the playback path
Common mistakesSingle fixed-quality file; synchronous transcoding on upload; origin-only serving; synchronous exact view counters; day-one full recommendation engine
What NOT to over-engineerLive streaming's low-latency pipeline, or a full ML recommendation system — name both as separate/extension systems, don't design them in depth