Design YouTube

Design a video sharing platform. Creators upload videos from phones and desktops, anything from a ten-second clip to a three-hour livestream recording. Viewers around the world press play and expect the picture to start almost immediately and keep playing smoothly on a shaky mobile connection. Every watch page shows a view count.

The hard parts pull in different directions: uploads are huge and must survive dropped connections, raw files have to be turned into many formats before anyone can watch them, playback traffic is measured in terabits per second, and a single viral video can attract millions of viewers within an hour.

In 45 minutes, agree on scope, size the system, then walk through the upload and processing pipeline, how playback is served, and how you count views at this scale.

Sketch on the HLD board, write your answer in the Design doc, and size it with the Estimator. Start the mock interview from the timer for a 45-minute run.

Design Path · Media & Storage · problem 7 of 18

Ask before you design. Try answering each yourself, then compare.

  1. Which flows matter most: upload, playback, or discovery?
    Typical answer

    Upload, processing and playback. Search and recommendations exist but can be black boxes.

  2. What scale should we plan for?
    Typical answer

    About 1 billion daily active viewers, 500 hours of new video uploaded per minute, and roughly 5 billion views per day.

  3. How long can processing take before a video is watchable?
    Typical answer

    A few minutes for a typical 10-minute upload is fine. A low-quality rendition can appear first and higher ones later.

  4. What devices and networks do viewers use?
    Typical answer

    Everything from 4K TVs on fibre to phones on 3G, so we need several resolutions and bitrates.

  5. Do view counts need to be exact and instant?
    Typical answer

    No. Counts can lag by a minute or so and must filter obvious bot or replay inflation, but they should never go backwards.

  6. Is live streaming in scope?
    Typical answer

    No, only uploaded video on demand. Live is a follow-up.

  7. Maximum upload size?
    Typical answer

    Up to 256 GB or 12 hours per file.

Should a multi-gigabyte upload pass through your application servers at all?
Transcoding a two-hour video on one machine is slow. What could you split it into?
Viewers do not download a file; they fetch small pieces. What does that do for caching and for switching quality mid-play?
Where do most bytes leave your network, and how close to the viewer can that be?
A counter row updated millions of times per minute is a hot spot. How do you reduce the number of writes?

Requirements

Functional

  • Creators upload a video with title, description and visibility; uploads resume after a dropped connection.
  • The system transcodes each upload into several resolutions and codecs and generates thumbnails.
  • Viewers stream any public video with adaptive quality and can seek anywhere.
  • Watch pages show metadata and an approximate view count.

Non-functional

  • Playback start time under 2 s at p95; rebuffering under 0.5% of watch time.
  • 99.99% availability for playback; uploads may degrade but must never lose an accepted file.
  • Scale: ~5 B views/day, 500 hours uploaded per minute, peak egress in the hundreds of Tbps.
  • A 10-minute upload is watchable in at least one quality within 5 minutes.

Out of scope

  • Live streaming, comments, subscriptions and monetisation.
  • Recommendation and search ranking internals (treat as services we call).
  • Copyright matching (mention it as a pipeline stage only).

Back-of-the-envelope estimates

  • Uploads: 500 hours/minute ≈ 720,000 hours per day. At an average 6 minutes per video that is about 7 M new videos a day, roughly 80 per second and 250 at peak.
  • Views: 5 B per day ≈ 58,000 play starts per second, about 175,000 at peak.
  • Raw storage: 720 k hours × ~1 GB/hour of source ≈ 720 TB per day.
  • Encoded storage: a ladder of six renditions in two codecs roughly doubles the source size, so ~1.5 PB per day before replication, about 2.7 EB over five years at 3× replicas. Old raw files move to cold storage or are deleted after encoding.
  • Egress: if 5 B views average 5 minutes at ~3 Mbps, that is ~100 Tbps averaged over the day, with peaks near 300 Tbps.
  • Hot set: a few percent of videos draw most views, so the CDN can serve well over 95% of bytes.

Egress bandwidth drives the design: it decides CDN placement, chunk sizes and the bitrate ladder. Transcoding compute is the second biggest cost.

API

POST /v1/uploads                {title, sizeBytes, mime}   -> {uploadId, partUrls[], partSize}
PUT  {partUrl}                  <bytes of part n>          -> 200 {etag}
POST /v1/uploads/{id}/complete  {etags[]}                  -> 202 {videoId, status: PROCESSING}
GET  /v1/videos/{videoId}                                  -> {title, status, manifestUrl, views}
GET  {cdn}/v/{videoId}/master.m3u8                         -> playlist of renditions
POST /v1/videos/{videoId}/views {sessionId, watchedSec}    -> 204

Upload parts go straight to object storage through pre-signed URLs, so app servers only handle small control calls and a retry only resends the part that failed.

Data model

videos(video_id PK, owner_id, title, description, status, duration_s, created_at, renditions[]) in a sharded relational or wide-column store keyed by video_id; a secondary index on owner_id serves a channel's video list.

upload_jobs(video_id, stage, attempt, updated_at) tracks pipeline progress so failed stages restart where they stopped.

Raw files and encoded segments live in object storage at paths like /v/{video_id}/{rendition}/{seg_no}.m4s; objects are immutable, which makes CDN caching trivial.

view_counts(video_id, shard, count) in a wide-column store, with a cached total per video. Metadata is small and read-heavy, so it sits behind a Redis cache.

High-level design

Upload path: the creator's app calls the gateway to start an upload. The upload service creates a videos row in PROCESSING state and returns pre-signed URLs, and the client pushes 8–16 MB parts directly into the raw uploads bucket, retrying any part that fails. When the client calls complete, the service emits a job onto the transcode queue.

Processing: a coordinator splits the source into GOP-aligned pieces of a few seconds each and fans them out to transcode workers, which encode every piece into each rung of the bitrate ladder in parallel. Finished segments land in the segment store, a packaging step writes HLS/DASH manifests, and the worker flips the video to READY in the metadata DB.

Watch path: the watch page fetches metadata through the metadata service, which reads Redis first. The player downloads the manifest and then short segments from the nearest CDN edge; on a miss the edge pulls from a regional shield and then the segment store. The player measures throughput and switches renditions between segments.

Views: the player sends a view beacon, which goes onto a stream. Counter workers dedupe and aggregate per minute and add the totals to sharded counters, keeping all of this off the playback path.

Open the HLD board and press Reference to see the architecture diagram.

Deep dives

Parallel transcoding pipeline

Encoding a two-hour 4K source serially can take longer than the video itself. Cut the source at keyframe boundaries into chunks of roughly 10 seconds, then treat each (chunk, rendition) pair as an independent task in a DAG: probe, split, encode, then a join stage that verifies all pieces, packages them and writes manifests. A video of 700 chunks and 8 renditions becomes 5,600 tasks that a large fleet finishes in minutes. Order the work so the 360p rendition finishes first, publish as soon as it is ready, and add higher qualities later. Tasks are idempotent because the output path is derived from the input, so a crashed worker's task is simply retried. Popular videos can be re-encoded later with a slower, more efficient codec where it pays for itself in bandwidth.

Adaptive streaming and the CDN

Store each rendition as 2–6 second segments with a manifest listing them. The player starts on a modest rung for a fast first frame, measures how quickly segments arrive and how full its buffer is, then steps up or down between segments. Because segments are immutable files with stable URLs, CDN edges cache them with long TTLs. Put a regional shield tier between edges and origin so a cold video triggers one origin fetch per region rather than one per edge. For the long tail, accept a miss; for videos trending upward, pre-warm edges in the regions where views are growing. Most of the cost lives here, so cache hit ratio is the metric to watch.

Counting views at billions per day

Incrementing one row per view would mean 175,000 writes per second at peak, concentrated on a few viral rows. Instead the player sends a beacon after a minimum watch time, the gateway drops it on a partitioned stream keyed by video, and counter workers keep per-video tallies in memory for a short window. They discard repeats from the same session and suspicious bursts, then write one increment per video per window. A viral video's counter is split into N shard rows to spread writes, and readers sum the shards and cache the total for a few seconds. A separate offline job recomputes trusted counts and corrects drift.

Resumable uploads at scale

A 20 GB upload over a home connection takes hours and will be interrupted. Split it into fixed-size parts, each with its own pre-signed URL and checksum. The client records which parts were acknowledged and, after a crash, asks the service for the list of received parts and sends only the rest. The service never proxies bytes, so a few hundred stateless instances can coordinate millions of concurrent uploads. Abandoned multipart uploads are garbage-collected after a few days.

Failure modes

  • If a transcode worker dies mid-task, the task's lease expires and another worker reruns it; output keys are deterministic so duplicates overwrite harmlessly.
  • If a CDN region degrades, DNS or the player's manifest steers traffic to another provider or region, at the cost of higher latency and origin load.
  • If the segment store origin is overloaded by a cold-start surge, shield caches collapse duplicate requests so each segment is fetched once per region.
  • If the view stream lags, counts freeze briefly but playback is untouched; workers catch up from the stream's retained offsets.
  • If the metadata DB is unavailable, cached watch pages keep playback working for popular videos while uploads queue up.

Core algorithm in code

import random
from collections import defaultdict


class ViewAggregator:
    """Buffers view events per video and flushes them as one increment per video.

    The durable counter is split into shards so a viral video's increments
    spread over several rows instead of hammering one.
    """
    def __init__(self, shards: int):
        self.shards = shards
        self.buffer = defaultdict(int)
        self.seen = set()                      # (video, viewer) dedupe for this window
        self.store = defaultdict(int)          # (video, shard) -> count
        self.writes = 0

    def record(self, video_id: str, viewer_id: str) -> None:
        key = (video_id, viewer_id)
        if key in self.seen:                   # replays inside the window count once
            return
        self.seen.add(key)
        self.buffer[video_id] += 1

    def flush(self) -> dict:
        added = dict(self.buffer)
        for vid, n in added.items():
            shard = random.randrange(self.shards)
            self.store[(vid, shard)] += n
            self.writes += 1
        self.buffer.clear()
        self.seen.clear()
        return added

    def total(self, video_id: str) -> int:
        return sum(self.store[(video_id, s)] for s in range(self.shards))


random.seed(7)
agg = ViewAggregator(shards=8)
events = 0
for window in range(5):
    for _ in range(20000):
        vid = "viral" if random.random() < 0.7 else "v%d" % random.randrange(50)
        agg.record(vid, "u%d" % random.randrange(30000))
        events += 1
    agg.flush()
print("raw events:", events)
print("store writes after batching:", agg.writes)
print("viral views (deduped per window):", agg.total("viral"))

Trade-offs

  • Encoding every upload into the full ladder gives instant quality choice but wastes compute on videos nobody watches; encoding higher rungs on demand saves money but delays the first HD view.
  • Short segments (2 s) give faster startup and quicker quality switches but cost more requests and slightly worse compression than 6–10 s segments.
  • Batched, approximate view counts scale cheaply but lag by a minute and need an offline job to correct them.
  • Pre-warming CDN edges cuts first-view latency for trending videos but spends bandwidth and cache space on guesses that may not pay off.

What a strong answer shows

  • Moves upload and playback bytes off the application tier (pre-signed uploads, CDN delivery).
  • Designs transcoding as a parallel, retryable pipeline and publishes a low rendition early.
  • Explains adaptive bitrate streaming with segmented, immutable files and why that caches well.
  • Uses estimates to show that egress bandwidth, not QPS, dominates the cost and design.
  • Keeps view counting asynchronous, batched and resistant to hot keys and inflation.
  • Covers failure handling for long-running jobs and uploads.

Mistakes to avoid

  • Routing video bytes through application servers, which then need enormous bandwidth.
  • Transcoding a whole file on one machine and ignoring how long that takes.
  • Serving one large MP4 per quality, which breaks seeking, adaptation and caching efficiency.
  • Updating a view counter row synchronously on every play.
  • Spending the interview on the metadata database while the real scale problem is bandwidth.

Follow-ups to expect

  • How would you add live streaming? (Ingest servers, real-time segmenting with low-latency HLS, much shorter CDN TTLs.)
  • How would you cut storage cost for old videos? (Tiered storage, drop rarely-watched high rungs, re-encode on demand.)
  • How do you take down a video quickly worldwide? (Flip status in metadata, revoke signed URLs, purge CDN paths.)
  • How would you detect copyrighted uploads? (A fingerprinting stage in the pipeline that blocks publish on a match.)

Practice next

Next Post Previous Post
No Comment
Add Comment
comment url