Skip to content

11.9 — Video Platform

Someone uploads a two-hour film. Ten minutes later a person on a train, on a phone, with a connection that swings between four megabits and four hundred kilobits, presses play and sees a picture within two seconds — and keeps seeing one as the train enters a cutting.

Three separate systems have to be true for that to happen. The upload has to survive a flaky connection, which 11.2 already solved. The two-hour source has to become a dozen different encoded versions in ten minutes, which is impossible serially and routine in parallel. And the bytes have to reach a phone in another country without travelling from your servers, because your servers cannot possibly carry them.

That last point is the one that shapes everything. This is the first study in this Part where the honest answer to "how do you scale it" is mostly "you do not serve the bytes yourself", and where the dominant number on the whole design is a network bill.

1. Requirements

Functional. Upload video up to 10 GB. Transcode into several resolutions and bitrates. Stream adaptively to any device. Thumbnails and preview scrubbing. View counts. Search by title.

Non-functional, with numbers.

  • Playback starts within 2 seconds.
  • Rebuffering ratio under 0.5% — the fraction of watch time spent staring at a spinner. This is the single metric a video product lives on.
  • Upload to watchable within about 10 minutes for a typical video.
  • 99.95% availability for playback. Being unable to upload for an hour is a bad day; being unable to watch is the product being down.

Out of scope today: recommendations (11.20), digital rights management specifics, comments, and live streaming — although section 8 says plainly why live is a different pipeline rather than a setting on this one.

The clarifying questions, and what each answer changes

"How much is watched versus how much is uploaded?" This ratio decides where the money goes. If a video is watched a million times, the encoding cost is irrelevant and the delivery cost is everything. If most uploads are watched twice, encoding five renditions of each is waste and you should encode lazily on first view.

"What is the longest video, and the largest single file?" Ten minutes of phone footage and a three-hour feature need the same pipeline but very different chunking, and the storage arithmetic changes by an order of magnitude.

"Which devices must play it?" This decides the codec question, which decides the bandwidth bill. A codec is the compression scheme the video is encoded with; newer ones make smaller files but only play on newer devices, so supporting old devices means storing an extra copy in an older format.

"Is a video private, unlisted or public?" Public content caches beautifully at the edge because everyone gets the same bytes. Private content needs signed access on every request, which is compatible with edge caching only if you are careful, and section 6.5 covers how.

"Can a video be watchable before it is fully processed?" If yes, publishing the lowest quality first turns a ten-minute wait into a one-minute one, and that is a real product win that costs one extra field in the data model.

2. Estimation

Storage. 500 hours uploaded per minute × 60 × 24 = 720,000 hours a day. At roughly 1 GB an hour for the source, plus about 2.5 times that for all the encoded versions together, that is ~2.5 PB a day. What that forces: object storage with lifecycle tiering, and a real decision about how long to keep the original source file after encoding — which is a surprisingly large lever, because the source is often the single biggest object and is only needed for re-encoding.

Egress, which is the number that dominates every other consideration. 1 billion watch-hours a day at an average 3 megabits a second: 1e9 hours × 3,600 seconds × 3 Mbps = 1.35 exabytes a day, which is about 125 terabits a second sustained. What that forces: no origin infrastructure serves 125 Tbps. More than 95% of bytes must come from content delivery network edge caches, and the design's central engineering job is maximising that percentage. Every other decision on this page — immutable segment URLs, tiered caching, codec choice — exists to serve that one number.

Transcoding capacity. 720,000 source-hours a day × 5 renditions = 3.6 million encode-hours a day. If a modern encoder runs at roughly real time per rendition, that is 3.6M ÷ 24 = 150,000 concurrent encoder cores. What that forces: a fleet sized by a queue rather than provisioned for peak, and — because that is an enormous compute bill — a strong reason to make every encoding task independently retryable so the fleet can run on interruptible capacity at a fraction of the price.

Cost balance, which decides the trade-offs. Storage at 2.5 PB a day is expensive. Egress at 1.35 EB a day is roughly five hundred times more bytes moved than stored. What that forces: whenever you can spend storage to save egress, do it. This is why storing an extra copy in a more efficient codec is obviously right, and why storing a redundant rendition nobody watches is only mildly wrong.

Latency budget for playback start. Two seconds covers: resolving the manifest (~100 ms), fetching the first segment from an edge (~200 ms if cached, ~800 ms if it has to come from origin), and decoding enough to render (~300 ms). What that forces: the first segment must be small and must be cached. Starting at the lowest rendition deliberately is not a compromise, it is how the budget is met.

3. API

http
POST /videos
{ "title": "Tunnel timelapse", "visibility": "public", "sizeBytes": 4294967296 }
201 Created
  { "videoId": "v_7431", "uploadId": "up_01J…", "parts": [  ] }   # the 11.2 flow
http
POST /videos/v_7431/publish
→ 202 Accepted
  { "videoId": "v_7431", "status": "processing" }
http
GET /videos/v_7431
→ 200 OK
  { "videoId": "v_7431", "status": "partially_ready", "duration": 7241,
    "renditions": [
      { "profile": "240p", "status": "ready" },
      { "profile": "480p", "status": "ready" },
      { "profile": "1080p", "status": "encoding", "percent": 62 },
      { "profile": "2160p", "status": "queued" } ],
    "manifestUrl": "https://cdn.example/v/7431/master.m3u8",
    "playable": true }
http
GET /v/7431/master.m3u8            # the list of available qualities
GET /v/7431/720p/seg-00412.m4s     # one 4-second chunk of video

POST /videos/v_7431/views          # → 204, fire-and-forget, batched by the client

Status is a first-class field, not an afterthought. The gap between "upload finished" and "anyone can watch this" is minutes, so this is a long-running operation exposed as a status resource rather than a request somebody holds open (9.6.1). The states are uploading → processing → partially_ready → ready, with failed reachable from any of them.

playable: true alongside status: partially_ready is the product win. The video can be watched at 480p while 4K is still encoding. Expressing that requires per-rendition status, which is why section 4 tracks renditions as rows rather than as a column on the video.

Segment URLs contain no query string, no token and no user identifier. That is deliberate and it is worth stating as a rule: anything that varies per user in a segment URL splits one cached object into millions of cache entries and destroys the hit ratio the whole design depends on. Section 6.5 explains how access control is done without putting anything user-specific in the path.

4. Data model

videos
  video_id     UUID PRIMARY KEY
  owner_id     UUID NOT NULL
  title        TEXT, description TEXT
  duration_s   INT
  status       SMALLINT NOT NULL       -- uploading | processing | partially_ready | ready | failed
  source_key   TEXT                    -- object storage key of the original
  visibility   SMALLINT NOT NULL
  created_at   TIMESTAMPTZ

renditions
  video_id     UUID, profile TEXT      -- '240p', '480p', … or 'av1-1080p'
  codec        TEXT, width INT, height INT, bitrate_kbps INT
  segment_prefix TEXT                  -- where its segments live
  status       SMALLINT NOT NULL       -- queued | encoding | ready | failed
  PRIMARY KEY (video_id, profile)

encode_tasks                            -- the workflow's unit of work
  task_id      UUID PRIMARY KEY
  video_id     UUID, profile TEXT, chunk_index INT
  status       SMALLINT, attempts SMALLINT
  output_key   TEXT

views                                   -- append-only, time-partitioned
  video_id, bucket_minute, count        -- produced by aggregation, never incremented per view

Access patterns:

QueryFrequencyReturns
Fetch a segment~10M/s, 95%+ at the edgeone file
Fetch a manifest~50,000/sone small file
Read video metadata for a page~50,000/sone row plus renditions
Claim the next encode task~2,000/sone row
Update a task's status~4,000/sone row

Metadata is small and relational; media is object storage. This is the same split as 11.2, and it is worth noticing that it appears again for the same reason — the two things differ by many orders of magnitude in size and by everything in access pattern.

Renditions are separate rows because they finish at different times. A single status column on the video cannot express "watchable at 480p, still encoding 4K", and that state is the difference between a creator waiting one minute and waiting ten.

Views are aggregated, never incremented. A row-per-view counter on a popular video is millions of writes to one row, which no partitioning scheme fixes. Client batches events, they land on a stream, a consumer rolls them into per-minute buckets, and the displayed number lags by seconds and is approximate at the top end (11.19).

5. Architecture

uploadresumable① sourceobject storage② transcode farmsplit the source atsafe cut pointsencode chunks in parallelacross many workersfive qualities:240p · 480p · 720p · 1080p · 4Kinterruptible machines —any chunk can be redone③ packagesegments + manifestimmutable URLs④ edge caches>95% of all bytesserved from here⑤ the player decidesmeasures throughputand buffer level, thenpicks the nextsegment's qualityThe client chooses quality. The server offers a menu and never decides.Chunked parallel encoding turns a two-hour serial job into a few minutes of wide fan-out.
Figure 1 — Upload to playback. Two structural ideas carry the whole design. Transcoding is chunked and parallel, so completion time depends on how wide you can fan out rather than on how long the video is. And quality selection happens on the client, so the network's actual condition decides, segment by segment.

The caching structure is worth its own panel, because the hit ratio is the budget.

edge · Londonedge · Parisedge · Madridedge · Milanregional shieldone miss per objectper region, not per edgeoriginsees a trickleWithout the shield, forty edges each pull the same object from origin — forty times the origin loadfor the same content, and forty times the exposure when an edge is cold.
Figure 2 — Tiered caching. The shield exists because a cache miss at one edge should not become a request to the origin. It collapses origin load by roughly the number of edges in a region, and it is what makes an edge failure survivable rather than an origin event.

6. Deep dives

6.1 Chunked parallel transcoding

Encoding a two-hour video serially takes hours, and there are five renditions to produce. The fix is to stop treating the video as one job.

Split the source at safe cut points. Video compression stores most frames as differences from earlier frames, so you cannot cut anywhere — you can only cut at the frames that are encoded independently, which occur every couple of seconds. Those are the boundaries, and finding them is what the probe step at the start of the pipeline does.

Encode every (chunk × rendition) pair as an independent task. A two-hour video split into 30-second chunks is 240 chunks; times five renditions is 1,200 independent tasks. With a thousand workers free, wall-clock time is one task's duration plus queue wait — minutes, not hours. Then concatenate per rendition, which is fast because the pieces are already encoded, and package into segments and a manifest.

Three properties this depends on, and the second one is the subtle one.

Determinism and idempotency. The same chunk with the same settings produces the same bytes, so a task can be retried anywhere with no coordination. That is what allows the fleet to run on interruptible machines — capacity that can be taken away at any moment, sold at a fraction of the normal price — which turns the largest compute cost in the system into the cheapest kind of capacity.

Consistent rate control across chunks. If each chunk's encoder independently decides how many bits to spend, the quality visibly pulses at chunk boundaries — every 30 seconds the picture gets slightly better or worse, and viewers notice even if they cannot name it. The fix is to constrain rate control: either a fixed quality target rather than a fixed bitrate, or a first pass that analyses the whole video and distributes its statistics to the chunk workers. This detail is what separates a design that works from one that looks broken, and it almost never comes up unprompted.

Orchestration that survives failure. Twelve hundred tasks with retries, partial completion and a final assembly step is a durable workflow (11.18), not a script. It must resume from where it stopped rather than restarting, or a failure at task 1,199 costs you the whole video.

And the product refinement: encode the lowest rendition first and publish it. The video becomes watchable in about a minute while the rest continues, which is what per-rendition status exists for.

6.2 Adaptive streaming, and why the client decides

The video exists as segments — short chunks of 2 to 6 seconds — at each quality level, plus a manifest, which is a small text file listing the available qualities and where their segments are.

The segments are time-aligned across qualities: segment 412 covers the same four seconds of the film in every rendition. That alignment is what makes switching mid-playback seamless, because the player can fetch segment 412 at 480p after fetching 411 at 1080p and the picture continues without a gap.

The player runs the loop. It downloads a segment, measures the throughput it actually achieved (bytes divided by elapsed time) and looks at how much buffered video it is holding, then chooses the quality for the next segment. Throughput dropping or buffer draining means step down. Both healthy for a while means step up, cautiously.

Why the client and not the server. Only the client knows its real conditions. The server sees one connection and cannot distinguish congested home Wi-Fi from a slow last mile from a phone that just handed over to a cell tower. A server-side decision would be a guess made with worse information and a round trip of lag.

The segment-length trade. Shorter segments start faster and adapt more quickly, and they compress slightly worse and cost more requests. Longer segments compress better and adapt sluggishly. Four seconds is the usual compromise; two seconds for cases where startup latency matters more than efficiency.

Why this is cheap to serve. Segments are immutable: segment 412 of the 720p rendition never changes. That means an infinite cache lifetime and no invalidation protocol at all — the ideal caching case, and the reason a video platform's serving cost is dominated by a network bill rather than by origin infrastructure.

6.3 Cache hit ratio is the budget

At 125 terabits a second, the hit ratio is not a performance metric, it is the finance model. Going from 92% to 98% is a four-fold reduction in origin egress, because the miss rate went from 8% to 2%.

The levers, in order of how much they move the number.

Immutable URLs with long lifetimes. Nothing about a segment ever changes, so nothing ever needs invalidating. A re-encode produces new URLs and a new manifest rather than replacing bytes at an existing address.

Tiered caching, as in Figure 2. Without a shield, a popular video that is cold at forty edges produces forty identical origin requests. With one, it produces one.

Pre-warming. A release everyone will watch at 9pm is pushed to edges at 5pm. This converts a predictable miss storm into background traffic.

Nothing user-specific in the URL. A token, a session identifier or a query parameter in a segment URL splits one cached object into as many entries as there are users, and the hit ratio collapses to zero for that content. This is the single most common self-inflicted cache disaster in this domain.

Accepting the long tail. Rarely watched videos will miss, and that is fine — by definition they are a small share of total bytes. Trying to keep everything hot is spending money to improve a number that does not matter.

6.4 Codecs and the bandwidth bill

A codec is the compression scheme. Newer ones produce the same visual quality in substantially fewer bits — commonly 30–50% fewer than the older scheme most devices support — at the cost of far more CPU to encode and support only on newer devices.

That produces an unusual-looking trade: to send fewer bytes you store more copies, because you keep the old format as a fallback for older devices. Given that this system moves roughly five hundred times more bytes than it stores, that trade is strongly favourable, and it is worth stating as arithmetic rather than as a preference.

And it only applies where the bytes are. Encoding the long tail in an expensive modern codec spends a great deal of CPU to save bandwidth nobody is using. Encode the popular decile efficiently and leave the rest alone — which requires knowing which decile is popular, which the view pipeline already tells you.

6.5 Private video, without destroying the cache

Access control and edge caching pull against each other: caching wants every user to request the same URL, and access control wants every request to prove who it is.

The resolution is to keep the authorisation in a signature rather than in the path. The player fetches the manifest through your API, which checks permission and returns manifest URLs carrying a short-lived signed token as a separate credential — a cookie scoped to the content path, or a signature the edge validates — while the segment paths themselves stay identical for every viewer. The cache key is the path; the authorisation is the signature; the edge validates the signature on every request even when the object is already cached.

Get this wrong in the obvious way — putting the token in the query string and letting it form part of the cache key — and every viewer creates their own copy of every segment in every edge. Get it wrong in the other direction — caching without validating the signature — and private videos become public.

6.6 View counts, honestly

Never increment a row per view. On a video getting a million views an hour that is 280 writes a second to one row, and no sharding fixes it because it is one video.

The pipeline is: the client batches view events and sends them occasionally, they land on a stream, a consumer aggregates them into per-minute buckets, and the displayed number is the sum of buckets. It lags by seconds and is approximate at the top end, and both of those are fine for a view count.

What matters is saying so. The alternative — an exact, synchronous, real-time count — is a hot-row write problem with no good solution at this scale, and a candidate who promises it has not done the arithmetic.

7. Decision Ledger

DecisionAlternativesWhy thisWhat it costs
Chunked parallel transcodingencode each rendition seriallyminutes rather than hours; every task is retryable anywheremust cut at safe points; rate control must be constrained or quality pulses
Interruptible machines for encodingreserved capacitythe largest compute cost becomes the cheapest capacity classtasks must be idempotent and the orchestrator must resume
Quality chosen by the clientserver-side selectiononly the client knows its real bandwidth and screena more complex player; the server cannot optimise what it does not decide
Immutable segment URLsmutable URLs with invalidationunlimited cache lifetime and no invalidation protocol at alla re-encode creates new URLs and a new manifest
Tiered caching with a regional shieldedges pull straight from originorigin load falls by roughly the number of edges per regionone more layer to operate and reason about
Per-rendition statuspublish all or nothingwatchable at 480p in one minute instead of tenmore states in the model and in the interface
Modern codec for popular content onlyone codec everywhere30–50% fewer bytes where the bytes actually aremore storage, more encode CPU, a fallback copy to maintain
Approximate, lagging view countsexact synchronous countersa per-view row increment is an unsolvable hot-row problemthe number is a few seconds behind and approximate at the top

8. Scale and failure

The transcode backlog is the common incident. An upload spike or an encoder regression grows the queue, and videos take hours to publish instead of minutes. Three defences: priority tiers, so a paying creator's upload is not behind a bulk re-encode of the back catalogue; autoscaling on queue age rather than on queue depth; and encoding the lowest rendition first, so that even a backlogged system makes videos watchable quickly.

At 10× the answer is not more origin. It is more edge capacity, a better hit ratio, and a more efficient codec on the popular content. The origin is deliberately small and should stay that way; if origin capacity is your scaling lever, the caching design has already failed.

What breaksBlast radiusHow you find outWhat keeps it runningRecovery
Transcode backlognew videos publish lateage of the oldest queued task, not depthpriority tiers; lowest rendition firstautoscale; drain with age as the exit condition
Encoder worker lostone chunktask retry countchunks are idempotent, so retry anywherethe chunk re-encodes; the workflow resumes
Corrupt or unsupported sourceone uploadprobe-stage failure ratevalidate at probe, before 1,200 tasks are createdfail fast with a clear creator-facing message
Video stuck in processingone creator, badlytasks with no terminal state past a deadlinea workflow timeout that forces failednever leave it ambiguous — a stuck video is worse than a failed one
One edge failsthat region's viewersedge health; origin pull volumetraffic shifts to neighbouring edgesthe shield absorbs the miss storm, if it is sized for it
Shield undersized for edge lossorigin, suddenlyorigin egress spikesize shields for a fraction of edge loss, not steady stateshed long-tail content first
Cache key fragmented by a bad URLhit ratio collapses; the bill does nothit ratio by content classno tokens or query strings in segment pathsfix the URL scheme; the cache refills
View aggregation stalledcounts freeze; playback unaffectedconsumer lagevents buffer durably in the streamreprocess; counts catch up

The row worth dwelling on is "stuck in processing". It is the worst failure in this system because it is indistinguishable from slowness: the creator refreshes, sees "processing", and waits — for hours, then gives up. Every workflow needs a deadline after which it declares failure with a reason, because an honest failed with "we could not read this file, here is what to try" is a far better outcome than an optimistic status that never resolves.

Live streaming, honestly. It is a different pipeline and saying so is the correct answer. Encoding happens in real time, so there is no chunk parallelism available — you cannot encode the future. Latency targets are seconds rather than minutes. There is no opportunity to re-encode a chunk that failed, because the moment has passed. And the caching story changes, because the newest segment is by definition not yet in any cache. Reusing this architecture for live is a common interview mistake; the right move is to name the four differences and propose a separate ingest and packaging path.

What the interviewer will push on

"How do you transcode a two-hour video in ten minutes?" They want the chunking insight and then the detail underneath it. Split at the frames that can be decoded independently, dispatch every chunk-and-quality pair as a task — 1,200 of them for a two-hour video at five qualities — and let wall-clock time become one task plus queue wait. Then volunteer the rate-control problem: if each chunk decides its own bitrate independently, quality pulses visibly at every boundary, so rate control has to be constrained or informed by a global analysis pass. That detail is the tell.

"Why does the client pick the quality rather than the server?" Because only the client can measure its own conditions. The server sees one connection and cannot tell congested Wi-Fi from a slow last mile from a handover to a cell tower. Add the two inputs the player uses — achieved throughput and buffer level — because candidates who only say "bandwidth" have not thought about why a player steps down before it runs dry.

"Your cache hit ratio is 92%. Is that good?" No, and the arithmetic is the answer: moving to 98% is a four-fold reduction in origin traffic, because what matters is the miss rate, not the hit rate. Then name what usually causes a low ratio — tokens or query strings in segment URLs splitting one object into millions of entries, missing tiered caching so every edge pulls independently, and lifetimes shorter than necessary on content that is immutable by construction.

"How do you serve private videos from a shared cache?" The tell is separating the cache key from the authorisation. Segment paths stay identical for every viewer so there is one cached object; the credential travels separately as a short-lived signature the edge validates on every request, cached or not. The two ways to get this wrong are worth naming: putting the token in the path destroys the hit ratio, and skipping validation on cached objects makes private videos public.

"A video has been 'processing' for four hours. What do you tell the creator?" They are testing whether you have thought about the worst failure rather than the most common one. The answer is that this should be impossible: every workflow carries a deadline after which it declares failure with a reason. An honest failure with a next step beats an optimistic status that never resolves, because the second one costs the creator hours and gives them nothing to act on.

"Would you reuse this pipeline for live streaming?" No, and listing why is the whole answer: real-time encoding forecloses chunk parallelism, latency targets are seconds not minutes, a failed chunk cannot be redone because the moment has gone, and the newest segment is by definition not cached anywhere. Proposing a separate ingest path shows you understand what the parallelism was buying.

Volunteer this, because nobody asks: in a system where the client makes the quality decision, the client holds the diagnostic data. Server-side metrics can tell you that segments were delivered quickly; only player telemetry can tell you that the buffer emptied, which rendition was playing when it did, and what throughput the player had actually measured. A video platform without rich player telemetry is blind to its own primary quality metric, and no amount of server-side monitoring substitutes for it.

Next: 11.10 — from bytes at rest to objects in motion. A video's location never changes; a driver's changes every four seconds, and the query "who is near me right now" turns out to need a completely different way of indexing space.

Recall

  • Egress dominates everything. ~125 Tbps means more than 95% of bytes must come from edge caches, and the design's real job is maximising the hit ratio. 92% → 98% is a four-fold cut in origin traffic.
  • What makes segments perfectly cacheable: they are immutable, so unlimited lifetime and no invalidation protocol. Nothing user-specific may appear in a segment URL, or one object becomes millions of cache entries.
  • Chunked parallel transcoding: split at independently-decodable frames, encode every chunk × quality pair as its own task (1,200 for a two-hour video at five qualities), concatenate, package. Completion time = one task + queue wait, not video length.
  • Because chunks are idempotent, the fleet runs on interruptible machines — the biggest compute cost becomes the cheapest capacity. The catch nobody mentions: rate control must be constrained across chunks, or quality visibly pulses at every boundary.
  • Adaptive streaming is client-driven. Time-aligned 2–6 second segments plus a manifest; the player picks the next segment's quality from achieved throughput and buffer level. The server offers a menu and never decides.
  • Per-rendition status makes a video watchable at 480p in about a minute while 4K continues. Status is a first-class field: uploading → processing → partially_ready → ready | failed.
  • Tiered caching (edge → regional shield → origin) collapses origin load by roughly the number of edges per region, and it is what makes an edge failure survivable.
  • View counts are batched, streamed and aggregated — approximate and lagging by construction, because a per-view row increment is an unsolvable hot-row problem.
  • Live streaming is a different pipeline: no chunk parallelism, seconds not minutes, no second chance at a failed chunk, and the newest segment is never cached.

Self-test: Why must 95% of bytes come from the edge, and what property makes that possible? Give the latency formula for chunked transcoding and the quality bug it introduces. What two inputs drive the player's choice? How do you serve private video from a shared cache? Why is "stuck in processing" the worst failure state?

Quiz Bank

FoundationalExplain adaptive bitrate streaming end to end.

The encoding side. The video is encoded into several renditions — 240p, 480p, 720p, 1080p, 4K, each at a target bitrate — and every rendition is cut into segments of 2 to 6 seconds. The segments are time-aligned: segment 412 covers the same few seconds of the video in every rendition. That alignment is the whole trick, because it is what lets a player fetch segment 411 at 1080p and segment 412 at 480p and have the picture continue without a seam.

The manifest is a small text file listing the available renditions with their bitrates and resolutions, and where each one's segments live. It is the menu.

The player drives the decision. It downloads a segment, measures the throughput it actually achieved — bytes divided by elapsed time, not an advertised connection speed — and checks how many seconds of video it currently holds buffered. From those two numbers it picks the quality for the next segment: step down when throughput falls or the buffer is draining, step up cautiously when both have been healthy for a while. Startup deliberately begins at a low rendition so the first frame appears fast, then ramps.

Why the client and not the server. Only the client knows its real conditions. The server sees a single connection and cannot distinguish congested home Wi-Fi from a slow last mile from a phone that has just switched cell towers. A server-side choice would be a guess made with worse information, arriving a round trip late.

The trade-off to name. Shorter segments mean faster startup and quicker adaptation, at the cost of slightly worse compression and more requests. Longer segments compress better and adapt sluggishly. Four seconds is the common compromise.

And why this is cheap to serve: segments never change. Immutability means unlimited cache lifetime and no invalidation protocol at all, which is why the byte-serving cost of a video platform lands on the content delivery network rather than on origin infrastructure — and why the origin can be surprisingly small.

InterviewHow would you transcode a two-hour 4K video in under ten minutes?

Not serially. A serial encode of one rendition of a two-hour source runs for hours, and there are five renditions.

Chunk it. Probe the source to find the frames that can be decoded independently — you cannot cut in the middle of a run of frames that are stored as differences from each other — and split into chunks of roughly 10 to 60 seconds. Dispatch every (chunk, rendition) pair as an independent task. A two-hour video at 30-second chunks is 240 chunks × 5 renditions = 1,200 independent tasks. With 1,200 workers available, wall-clock time becomes one task's duration plus queue wait: minutes rather than hours. Then concatenate per rendition — fast, because the pieces are already encoded compatibly — and package into segments and manifests.

Three properties the design depends on.

Determinism and idempotency. The same chunk with the same settings produces the same output, so a task can be retried on any worker with no coordination. That is precisely what allows the fleet to run on interruptible capacity at a fraction of the normal price, which turns the largest compute cost in the entire system into its cheapest capacity class.

Consistent rate control across chunks. This is the detail that separates a working design from one that looks broken. If each chunk's encoder independently decides how many bits to spend, quality visibly pulses at every chunk boundary — the picture subtly improves and degrades every thirty seconds. The fix is either a fixed quality target rather than a fixed bitrate, or a first analysis pass over the whole video whose statistics are distributed to the chunk workers.

Orchestration that survives failure. Twelve hundred tasks with retries, partial completion and a final assembly step is a durable workflow (11.18), not a shell script. It must resume from where it stopped, or a failure near the end costs the entire video.

The product refinement worth adding unprompted: encode the lowest rendition first and publish it. The video becomes watchable within about a minute while the higher renditions continue in the background, which turns a ten-minute wait into a one-minute one. Per-rendition status exists exactly to make that expressible.

InterviewHow do you serve private videos from a shared edge cache without destroying the hit ratio?

The tension. Caching wants every viewer to request the identical URL so there is one cached object. Access control wants every request to prove who is making it. Naively satisfying both puts the credential in the URL, which makes every viewer's URL unique and gives you one cache entry per viewer per segment — a hit ratio of effectively zero on exactly the content you are paying most to serve.

The resolution is to separate the cache key from the authorisation. The path is identical for every viewer: /v/7431/720p/seg-00412.m4s. That path is the cache key, so there is one cached object serving everyone. The credential travels separately — as a short-lived signed cookie scoped to the content path, or as a signature the edge validates — and it is not part of the key.

The flow. The player asks your API for the manifest. The API checks that this viewer may watch this video, and returns the manifest together with a signed credential valid for a few minutes and scoped to this video's path. The player then fetches segments directly from the edge, presenting the credential. The edge validates it on every request, including when the object is already in cache, and serves the bytes.

The two ways to get this wrong, both of which have shipped. Putting the token in the query string so it becomes part of the cache key: the hit ratio collapses and the bill quadruples, with no functional symptom at all, which is why it survives so long. Or caching without validating the signature on hits: the first authorised viewer warms the object and every subsequent request is served from cache without a check, which makes private videos publicly readable to anyone who guesses a path.

One more detail worth volunteering: the credential must expire, and it must be scoped to the content rather than to the viewer, because a viewer-scoped credential is one more thing that varies per user. Short expiry is what limits the damage from a shared link, and the manifest request — which does go through your API — is where revocation can actually be enforced.

StaffYour delivery bill is 70% of infrastructure spend and growing faster than revenue. Find the levers.

Measure first, and refuse to act before you have. Break bytes down by content, by rendition, by geography, by device class, and — most importantly — by cache hit ratio per class. Every lever below is worthless without that breakdown, and in practice one of them is usually responsible for a disproportionate share.

Lever one: encoding efficiency, the biggest structural win. Modern codecs deliver comparable quality at 30–50% fewer bits than the older scheme most devices support. The cost is far more encode CPU and the need to keep the older format as a fallback, so you store more to send fewer. Because this system moves roughly five hundred times more bytes than it stores, that trade is strongly favourable. And it applies only to the content that actually generates bytes — encode the popular decile efficiently and leave the long tail alone.

Lever two: per-title encoding ladders. A fixed bitrate ladder wastes bits on simple content. A static talking head at 1080p needs far fewer bits than an action sequence at the same resolution. Analysing each title and assigning a custom ladder is a well-documented double-digit percentage saving and costs only encoder analysis time.

Lever three: device and viewport policy. Serving 1080p to a five-inch screen where nobody can see the difference is pure waste. Cap by viewport and device class, default conservatively, and let adaptive selection step up only when both the screen and the conditions justify it. This is usually the fastest lever to pull and the most immediately measurable.

Lever four: cache hit ratio. Every miss costs origin egress on top of edge delivery. Audit for the fragmenting mistakes: tokens or query strings in segment URLs, inconsistent request-varying headers, cache lifetimes shorter than necessary on content that is immutable by construction, and missing tiered caching so that every edge independently pulls from origin. Moving from 92% to 98% is a four-fold reduction in origin egress, which is why this number deserves its own dashboard.

Lever five: commercial. Multiple providers with traffic steered by cost and measured quality, committed-volume agreements, and direct interconnection in the heaviest markets. At this spend, negotiation is engineering work.

Lever six: the honest product question. Autoplay, aggressive preloading and background playback generate bytes nobody watches. Measuring "bytes delivered but not viewed" frequently finds a startling number, and reducing it costs nothing but a product decision.

The framing for leadership: delivery cost is (bits per second of quality) × (seconds delivered) × (price per byte), and there are levers on all three. Codec and per-title encoding attack the first. Device policy and waste reduction attack the second. Hit ratio and contracts attack the third. Attacking only the third is the common mistake, because it is the only one that looks like a cost problem rather than an engineering one — and it is also the smallest of the three.

Flashcards

FlashThe cost model

Egress dominates: ~125 Tbps, roughly 500× more bytes moved than stored. So spend storage to save bandwidth. More than 95% of bytes must come from edge caches; 92% → 98% hit ratio is a four-fold cut in origin traffic.

FlashChunked transcoding, and its hidden bug

Split at independently-decodable frames → encode every chunk × rendition in parallel → concatenate → package. Time = one task + queue. Idempotent chunks allow interruptible machines. Constrain rate control, or quality pulses at every chunk boundary.

FlashAdaptive streaming

Time-aligned 2–6 second segments per rendition, plus a manifest. The player picks the next segment's quality from achieved throughput and buffer level. The server offers a menu and never decides, because only the client can measure its own conditions.

FlashWhat makes segments cacheable

They are immutable, so unlimited lifetime and zero invalidation. Nothing user-specific in the path — a token in the URL splits one object into one entry per viewer and silently quadruples the bill.

FlashPer-rendition status

Watchable at 480p in a minute while 4K encodes. Statuses: uploading → processing → partially_ready → ready | failed. And every workflow needs a deadline, because "stuck in processing" is worse than "failed".

FlashLive is a different pipeline

Real-time encoding means no chunk parallelism, seconds rather than minutes of latency budget, no second chance at a failed chunk, and the newest segment is by definition not cached anywhere.

Scenario Drill

DrillRebuffering has risen to 3% against a 0.5% target — but only in one country, only on mobile, and only in the evening. Investigate.

The three-way correlation is the diagnostic engine, because each dimension eliminates whole classes of cause before you look at anything.

Only one country rules out the encoding pipeline, the manifests and the player build, all of which are global. It points at delivery in that geography.

Only mobile points at last-mile bandwidth, or at a device-specific rendition or codec path.

Only evening points at congestion — either the local network operator's peak-hour saturation, or your own edge capacity in that region.

Hypothesis one: edge capacity or interconnection saturated at peak. Check that region's per-location metrics: cache hit ratio, origin pull volume, and delivered throughput during the affected hours. A hit ratio that collapses at peak means the edge is evicting under load and pulling from origin across a congested path, which produces exactly this pattern. Fixes: more edge capacity, a regional shield, or pre-warming popular content before the peak.

Hypothesis two: network-operator congestion that is not yours to fix directly. It shows up as reduced achieved throughput per session across all content and all providers in that market. Fixes: direct interconnection or an embedded cache inside the operator's network, which major platforms do, plus a more conservative quality ladder for that market so the player starts lower and steps up rather than overshooting into a rebuffer.

Hypothesis three, and the one most often missed: the ladder does not go low enough. If the lowest rendition is 480p at 1.5 megabits and typical evening mobile throughput there is 1 megabit, the player has nowhere to step down to and must rebuffer. Adding a 240p rung fixes it outright and is the cheapest possible fix. This is missed because the ladder was designed for a different market's conditions and nobody revisited it.

Hypothesis four: a codec fallback path. If devices in that market disproportionately lack hardware decoding for your efficient codec, they fall back to the older, larger format — consuming more bandwidth exactly where there is least. Check rebuffering segmented by the codec actually served.

The measurement to demand before acting on any of them: player-side telemetry carrying achieved throughput, buffer level at the moment of each rebuffer, the rendition and codec being played, the serving location, and the network operator. Every hypothesis above is confirmable from that single event schema, and none of them is confirmable from server-side metrics alone (10.10).

The general point, which is worth putting in the design document: in a system where the client makes the quality decision, the client holds the diagnostic data. A video platform without rich player telemetry is blind to its own primary quality metric, and no amount of server-side monitoring substitutes for it — the servers will report that every segment was delivered promptly while users watch a spinner.