~learn.

YouTube Video Platform, the whole design.

Turning 20 million+ uploads a day into playable video within minutes and counting billions of views honestly, by keeping every original forever and treating everything derived from it as a rebuildable pipeline.

6 diagrams · 8 sections · revise in ~10 min · Download PDF prints it in the light theme, diagrams included

SQ-19 · YouTube Video Platform · design sheethimanshukukreja.in/learn/sheets/bonus-19-youtube-video-platform

At a glance#

Number Value What it drives
Uploads 20M+ / day ≈ 231 / s design peak 1,000 / s (3× evening peak, assumed)
Video arriving 500+ h / min = 30,000 s of video every second average upload ~2.2 min; long videos carry the bytes
Originals ~2.6 PB / day · ~720 Gbps peak 8 Mbps average source (assumed); kept forever
Open upload sessions ~600,000 front ends keep no file in memory; state in a session table
Transcoding ~450,000 cores in software · ~18,000 with chips one codec; YouTube reports 20–33× from its VCU
Views 200B+ Shorts views / day · ~6M starts / s peak ~18M events / s; one viral video at 50,000 / s
Recommendations ~1.2M requests / s · 100 ms 20B+ videos → hundreds of candidates → 20 shown
Largest upload 256 GB or 12 hours resumable chunks, multiples of 256 KB

Architecture#

DiagramUpload, processing, views and recommendations on one page
%% title: Upload, processing, views and recommendations on one page
flowchart TD
  CR@{ icon: "sd:phone", label: "Creators<br/>phone · browser · studio apps" } ==>|"chunks, resumable"| UP@{ icon: "sd:gateway", label: "Upload front ends<br/>session table · 308 + Range" }
  UP ==> ORIG@{ icon: "sd:object-storage", label: "Originals<br/>kept forever" }
  ORIG -.->|"upload complete"| ORCH["Processing orchestrator<br/>one task graph per video<br/>tiers · caps · retries"]:::hot
  ORCH ==> TR@{ icon: "sd:cpu", label: "Transcode pool<br/>closed-GOP chunks in parallel" }
  ORCH --> META@{ icon: "tech:mysql", label: "Video metadata · sharded MySQL<br/>renditions ready · policy result" }
  TR ==> REND@{ icon: "sd:cdn", label: "Renditions + CDN<br/>segments · manifests · rebuildable" }
  VW@{ icon: "sd:users", label: "Viewers<br/>web · apps · TVs" } ==> API["Watch and feed API<br/>manifest lists only ready renditions"]:::core
  API --> REND
  API --> REC["Recommendations<br/>candidates → ranking → rules"]:::core
  VW -.->|"view_start · heartbeats"| EV@{ icon: "tech:kafka", label: "Kafka · view events<br/>partitioned by view_id" }
  EV --> CNT["View counter<br/>dedupe · quality filter · sharded"]:::core
  EV -.-> REC
  class CR,VW client
  class UP edge
  class ORIG,META store
  class TR service
  class REND edge
  class EV queue
DiagramOne video's processing task graph
%% title: One video's processing task graph
flowchart TD
  IN(["upload.complete<br/>v_7Hq2 · 12 min · 1080p"]):::client --> PROBE["Probe<br/>codec · length · GOPs"]:::service
  PROBE --> SPLIT["Split at closed GOPs<br/>180 chunks of ~4 s"]:::core
  PROBE --> CHK["Policy · copyright · thumbnails<br/>must pass before public"]:::service
  SPLIT --> T1["Tier 1 · high priority<br/>decode once → 144p–480p"]:::hot
  SPLIT --> T2["Tier 2 · normal<br/>720p · 1080p"]:::core
  SPLIT -.-> T3[["Tier 3 · low, by popularity<br/>4K · newer codec"]]:::queue
  T1 --> ASM["Assemble per rendition<br/>duration must match the original"]:::service
  T2 --> ASM
  T3 -.-> ASM
  ASM --> PUB(["Manifest updated tier by tier"]):::ok
  CHK --> PUB

Key flows#

DiagramA dropped upload resumes from the stored offset
%% title: A dropped upload resumes from the stored offset
sequenceDiagram
  autonumber
  participant A as Asha's app
  participant U as Upload front end
  participant S as Originals store
  participant O as Orchestrator
  A->>U: Start session · 1.8 GB · title · privacy
  U-->>A: Session URI
  A->>U: 8 MB chunks
  U->>S: Write part, then advance the stored offset
  Note over A,U: connection drops at 1.1 GB
  A->>U: Status? bytes */1,800,000,000
  U-->>A: 308 · Range bytes=0-1,099,999,999
  A->>U: The rest from byte 1,100,000,000
  alt checksum matches
    U->>S: Seal the original
    S-)O: upload.complete · tier 1 first
  else checksum mismatch
    U-->>A: 400 · upload failed, start again
  end
DiagramCounting a viral video's views
%% title: Counting a viral video's views
sequenceDiagram
  autonumber
  participant P as Player
  participant K as View events
  participant W as Stream worker
  participant C as Sharded counter
  participant Q as Quality filter
  P->>K: view_start · view_id v1 · video X
  K->>W: Partition by view_id, not video
  alt v1 already in the dedupe window
    W->>W: Drop · a player retry
  else new playback
    W->>W: Add 1 to X's one-second total
  end
  W->>C: Every second · X += 412
  Note over C: above 1,000 views/s · 64 shards
  W->>Q: Score · address · device · tabs · progress
  Q->>C: Cleared views → verified public count
  Q-->>C: Invalid later · subtract

Data model#

DiagramCore entities
%% title: Core entities
classDiagram
  class Video {
    +videoId : string
    +channelId : string
    +state : VideoState
    +durationS : int
    +originalKey : string · kept forever
    +popularity : hot / warm / long-tail
  }
  class UploadSession {
    +sessionId : string
    +size : bytes
    +stored : bytes · only durable parts
    +parts : part number to etag
    +state : string
  }
  class Rendition {
    +tier : 1 / 2 / 3
    +resolution : string
    +encoder : version
    +outputKey : video · chunk · tier · encoder
  }
  class ViewEvent {
    +viewId : UUID · dedupe key
    +videoId : string
    +type : start / heartbeat / end
    +at : timestamp
  }
  class ViewCount {
    +videoId : string
    +provisional : int64
    +verified : int64 · public
    +shards : 1 or 64
  }
  UploadSession "1" --> "1" Video : creates
  Video "1" --> "many" Rendition : derived from original
  Video "1" --> "many" ViewEvent : played as
  ViewEvent "many" --> "1" ViewCount : aggregated into

Lifecycle#

DiagramA video from upload to fully processed
%% title: A video from upload to fully processed
stateDiagram-v2
  [*] --> UPLOADING
  UPLOADING --> UPLOADING: chunk stored, offset advanced
  UPLOADING --> EXPIRED: session idle too long
  UPLOADING --> PROCESSING: last byte, checksum ok
  UPLOADING --> FAILED: checksum mismatch
  PROCESSING --> BLOCKED: policy or copyright check fails
  PROCESSING --> LIVE_LOW: tier 1 ready
  LIVE_LOW --> LIVE_HD: tier 2 ready
  LIVE_HD --> LIVE_FULL: tier 3, once watched enough
  LIVE_HD --> TAKEN_DOWN: deleted or ordered down
  LIVE_FULL --> TAKEN_DOWN: deleted or ordered down
  LIVE_FULL --> [*]
  TAKEN_DOWN --> [*]
  EXPIRED --> [*]
  FAILED --> [*]
  BLOCKED --> [*]
  class LIVE_FULL ok
  class FAILED,EXPIRED,BLOCKED,TAKEN_DOWN bad
  class PROCESSING queue

Decisions#

Decision Chose Over Because
Upload protocol Resumable session, offset after durable write One long POST phone uplinks drop; nothing stored is sent twice
Transcoding unit ~4 s closed-GOP chunks, decode once, encode a tier One job per video minutes not hours; a crash costs one chunk
What gets rendered Basic ladder now, tier 3 when watched Every rendition for every upload long tail is most videos; compute follows watch time
Publishing Manifest lists ready tiers only Wait for every rendition live in low quality within minutes
View counting Events by view_id, 1 s totals, sharded counters views = views + 1 hot key, retries and fraud
Public count Verified count, may pause or correct Raw count bots can't inflate it; eventually right
Ranking target Expected watch time + satisfaction Clicks clicks reward misleading thumbnails

When it breaks#

Failure What users see Contain it with
Connection drops mid-upload upload pauses, then resumes 308 + Range from the stored offset
Corrupt chunk crashes workers one rendition marked failed dead-letter after a few retries; others publish
Encoder bug ships for a day a flash in some videos encoder version in output keys; re-encode from originals
Transcode pool at 85 % at peak 4K arrives later tier 3 waits for the trough; tier 1 never does
Viral video, 50,000 views / s count keeps up partition by view_id, 64 counter shards
View stream lags minutes counts freeze, then jump async counting; playback never waits

Say it in the interview#