A video platform is two loosely coupled systems: a heavy asynchronous **upload and transcode pipeline, and a read path that is essentially a CDN with a database attached**. Keep them separate in your answer and the design stays clean.
Requirements
Functional
- Upload a video; watch a video.
- Search, recommendations, view counts, comments and likes.
Non-functional
- Playback starts in under a couple of seconds and does not buffer.
- Upload may take minutes and processing may take longer — that is acceptable and should be
asynchronous.
- Extremely read-heavy, and extremely bandwidth-heavy.
500 hours of video uploaded per minute
1B hours watched per day
Storage: 500 h/min × 60 × 24 = 720,000 h/day
~1 GB/hour raw, ×5 for multiple encodings ≈ 3.6 PB/day
Bandwidth dominates every other cost in this system.That last line is the point: **the CDN bill, not the servers, is what this design is optimising.**
Upload
Never a single HTTP POST — a dropped connection at 95% of a 4 GB file must not restart.
1. Client asks for an upload URL; metadata row created as status=UPLOADING
2. Client uploads in chunks (5-10 MB) DIRECTLY to blob storage via a
presigned URL — the application servers never touch the bytes
3. Each chunk is retried independently; the client can resume
4. Storage fires a completion event → enqueue a transcoding jobUploading straight to blob storage is the detail to volunteer: routing petabytes through your own servers would be both a bandwidth cost and a scaling ceiling for no benefit.
Transcoding
The heart of the system. One source file becomes many renditions, and the work is a
DAG of independent tasks so it parallelises:
┌→ 1080p ┐
raw → split into ├→ 720p ├→ segment into 4-10s chunks → package (HLS/DASH) → CDN
GOP segments ├→ 480p │
└→ 240p ┘
├→ thumbnails
└→ audio tracks, captions (ASR)Two things make this fast. Split the source first — segment the video and transcode segments in parallel across many workers, so a two-hour film is not one serial job. And fan out the renditions — each resolution is independent work.
Jobs go through a queue with retries and a dead-letter queue; workers are stateless and autoscaled, ideally on spot instances since the work is interruptible. Publish the video as soon as the first (lowest) rendition is ready, and add higher qualities as they finish.
Adaptive bitrate streaming
The client does not download a file; it fetches a manifest listing renditions and segments, then requests segments one at a time, switching quality as measured bandwidth changes.
master.m3u8
├─ 240p/playlist.m3u8 → seg0.ts, seg1.ts, …
├─ 480p/playlist.m3u8
├─ 720p/playlist.m3u8
└─ 1080p/playlist.m3u8HLS (Apple) and MPEG-DASH (standard) are the two protocols. The consequences to mention: playback starts fast because only the first segment is needed; a bandwidth drop degrades quality instead of buffering; and segments are static files, so **everything is CDN-cacheable**.
Serving
Client → nearest CDN edge
hit → serve (this is the overwhelming majority)
miss → origin blob storage → cache → servePopularity is extremely long-tailed: a small fraction of videos is most of the traffic. Push popular content to edges proactively, and let the tail be pulled on demand. Regional caching matters too — a video trending in one country need not be resident everywhere.
Metadata and view counts
videos video_id, uploader_id, title, description, duration,
status, created_at, manifest_url, thumbnail_url
views aggregated separately — NOT a counter on the video rowIncrementing a row per view would make a viral video's row a write hotspot and put database writes on the playback path. Instead, emit a view event to Kafka, aggregate in a stream processor, and write periodic rollups. Exact real-time counts are not required — YouTube famously freezes counts near 300 while verifying, which is a good example to cite of choosing throughput over immediate accuracy.
Follow-ups to expect
- Search — a separate inverted index over title, description, captions and transcripts.
- Recommendations — candidate generation from watch history and collaborative filtering,
then a ranking model; precomputed per user, refreshed periodically.
- Live streaming — the same pipeline with much shorter segments and low-latency HLS;
the trade-off is latency versus resilience.
- Copyright matching — perceptual fingerprints of audio and video compared against a
reference database at upload.
- Resumable upload state — track received chunks so a resumed upload skips them.
- Cost — storing five renditions of everything forever is expensive; delete unwatched
high-resolution renditions of cold videos and re-encode on demand.
Track this problem on the System Design sheet.
Frequently asked
How does video transcoding scale on a platform like YouTube?
By splitting the source into segments and transcoding them in parallel across many stateless workers, and by treating each output resolution as an independent task — the whole thing is a DAG of small jobs pulled from a queue. Publishing as soon as the lowest rendition is ready means the video goes live long before every quality is finished.
What is adaptive bitrate streaming?
The video is encoded at several qualities and cut into short segments, with a manifest listing them. The player requests segments one at a time and switches rendition as measured bandwidth changes, so a slow connection degrades quality instead of buffering. Because segments are static files, HLS and DASH streams are fully CDN-cacheable.
How are view counts handled at scale?
Not as a counter on the video row — that would put a database write on the playback path and make a viral video's row a hotspot. Views are emitted as events to a stream, aggregated by a stream processor, and written back as periodic rollups. The count is eventually consistent, which is an acceptable trade for the throughput.