cswork / libraryOpen practice workspace

Interview guides · Disney

Disney Software Engineer Interview Guide

Disney Software Engineer interview guide: the online assessment and virtual meeting, reported API design questions, and a 7-day prep plan.

The 60-second brief

Candidates describe the Disney Software Engineer process in two stages. First comes a Technical Screening delivered as an online assessment of foundational technical skills. Then a Virtual Meeting covers your practical experience, engineering maturity and team fit. The reported questions focus on consuming an API: design a method that takes a genre and requests movie titles from an API, explain how you would handle latency and error states, and describe how you would keep high-frequency requests from overloading the backend service.

This guide covers the two stages candidates report: an online-assessment Technical Screening and a Virtual Meeting built around your resume. Rounds and questions come from candidate reports and change over time, so confirm the current format with your recruiter.

Focus areas

  • Design a bitrate ladder under stated constraints
  • Expire rights across cached and offline copies
  • Sessionise at-least-once heartbeats into exactly-once plays

Overview

The source notes describe the Disney Software Engineer role as building and maintaining software for both consumer-facing platforms and internal systems. The job also means working with cross-functional teams to turn requirements into maintainable code. The listed must-haves are proficiency in at least one object-oriented language, a strong grasp of data structures, and experience with RESTful APIs. Cloud infrastructure, containerization and exposure to high-traffic distributed systems are listed as nice-to-haves.

The responsibilities listed in the notes include owning features across their full lifecycle, writing production code, taking part in code reviews and design discussions, contributing technical documentation, and refining requirements with product managers. Prepare at least one feature you owned from conception to deployment that you can describe end to end.

For interview preparation, API work stands out. The reported questions ask you to design a method that takes a genre and fires a request to an API that returns movie titles, to explain how you handle latency and error states, and to keep high-frequency calls from overloading the backend. Treat them as one problem that grows in stages: first a correct call, then a resilient one, then one that protects the service behind it. Be ready to explain the trade-off at each stage.

The Disney interview process

Disney candidates report 2 rounds · ≈ 2-4 weeks. The stages below are what candidates describe, not a published process.

Technical Screening

Candidates describe the first stage as an online assessment of foundational technical skills. The source notes say to be ready for data structures and algorithms, time and space complexity analysis, effective use of standard libraries and API integration patterns, and error handling and edge cases. If the assessment has no live interviewer, you cannot ask clarifying questions. Read the constraints closely, pick an approach that the input size allows, and test boundary cases yourself before you submit.

What to demonstrate

  • Correct solutions to standard data-structure and algorithm problems, including edge cases the prompt does not spell out
  • Whether your time and space complexity suits the stated input sizes
  • Clean use of your language's standard library rather than hand-rolled replacements
  • Error handling in code that parses input or consumes external data

How to prepare

  • Work through the Disney-tagged bank items: BFS shortest path in a grid, an LRU cache (hash map plus doubly linked list, O(1) get and put), and a set on a resizable array upgraded to hashing
  • Before you submit any practice solution, run it on empty input, a single element, duplicates and the largest size the prompt allows
  • Practise in the language you will use in the assessment until you can use its deque, heap, hash map and ordered collections without looking anything up
  • After each problem, write one line stating its complexity and the input size that justifies it, so stating it becomes a habit

Virtual Meeting

The second stage is described as a virtual discussion of engineering maturity and team fit, focused on practical experience. According to the source notes, interviewers often pick specific points from your resume to test how deeply you understand them. The notes also advise thinking out loud and stating trade-offs whenever you propose a solution. The sources do not tie the reported API questions (the genre-to-movie-title method, latency and error handling, high-frequency requests) to a particular round, so prepare them for either stage. Be ready to defend your design choices and explain why you rejected the alternatives.

What to demonstrate

  • How deep you can go on the projects on your resume: what you built, why you chose that approach, and how it behaved once shipped
  • Whether you reason out loud and name trade-offs against the alternatives
  • Team fit, including how you describe working with cross-functional partners

How to prepare

  • Pick two or three projects with real technical hurdles and prepare the what, why and how of each decision, including the figures that must stay consistent every time you tell the story
  • Rehearse the genre API method aloud in layers (signature, request, response mapping, errors, then load protection) and state one trade-off at each layer
  • Prepare stories for the behavioral items tagged to this role in the bank: collaboration and stakeholders, your proudest project and interest in the role, and culture fit, including one where you and a partner wanted different things
  • List every tool on your resume (a Disney-tagged bank question covers Terraform, infrastructure as code and AWS) and prepare one level of detail beyond what the resume says

Where candidates lose points

Writing the genre-to-movie-title method as a single HTTP call that assumes a 200 response with well-formed JSON.

Start by stating the API contract you are assuming (endpoint, query parameter, pagination, auth) and the method's signature and return type. Then add the failure handling out loud: a request timeout; 4xx client errors such as a bad genre, which should not be retried, except 429 Too Many Requests, which should be retried after backoff and any Retry-After the server sends; 5xx or network errors, which are transient and can be retried; a malformed or missing body; and an empty list as a valid result that is different from a failure. Map the response into a typed model instead of passing raw JSON to the caller.

Answering the high-frequency-request question with 'add a cache' and stopping there.

Give a layered answer and name the cost of each layer. Use a per-genre cache with a TTL, accepting results that may be stale for up to the TTL. Coalesce identical in-flight requests so a burst for one genre produces one upstream call. Add a client-side rate limit or token bucket, and use retries with exponential backoff and jitter, capped, so failures do not multiply the load. Add a circuit breaker that fails fast while the backend is unhealthy. Finish by saying which of these you would build first and why.

Submitting an online-assessment solution that passes the sample cases but breaks on empty, single-element or maximum-size input.

The source notes list error handling and edge-case management as a preparation area for the technical fundamentals. If the assessment has no live interviewer, you have to find the boundaries yourself: before submitting, trace the smallest input, duplicates, the largest input the constraints allow, and any value that could overflow your integer type. Check that your complexity fits the stated bounds rather than hoping it does.

Stumbling on simple questions about your own resume in the Virtual Meeting.

The source notes warn that fumbling simple questions about your past work suggests a lack of depth, and that interviewers pick specific points from your background. For every project and tool you list, prepare what you built, why you chose that approach over the obvious alternative, what went wrong, and the figures involved (team size, timeline, scale). Keep them identical every time you tell the story.

Proposing a design or API approach without naming what it gives up.

Each time you make a choice (synchronous or asynchronous call, cache or fresh fetch, retry or fail fast), follow it with one sentence on the alternative and why you rejected it here. If the interviewer changes a constraint, re-derive the choice rather than defending the original.

Questions & practice

Deduplicate at-least-once heartbeats into per-session watch time

Easy · idempotency · hash aggregation · streaming

You receive a batch of up to 10^7 heartbeat records, each (session_id UUID, sequence INT, played_ms BIGINT, position_ms BIGINT, wall_clock). Delivery is at-least-once and out of order: a client that crashed and resumed replays earlier sequences, and an offline device can deliver days of buffered records in one burst. played_ms is a monotonically non-decreasing counter reported by the client; position_ms is the playhead. The batch spans roughly 10^6 sessions. For each session emit accumulated watch time and the count of distinct sequences received. Target O(n) time, and state your memory bound and what you do when it is exceeded.

Approach

  1. Split the output by idempotency before writing any code: max(played_ms) is idempotent under duplicate delivery, so watch time needs no deduplication at all. The distinct-sequence count is not idempotent, and it is the only figure that forces you to remember what you have already seen.
  2. One hash map keyed by session_id holding {max_played_ms, seen-sequence structure}, filled in a single pass. O(n) expected time, O(g) space for g distinct sessions.
  3. Bound the memory with real numbers rather than an asymptote: sequences are dense per session and start at zero, so a compressed bitmap per session costs order 100-200 bytes, and 10^6 sessions is roughly 100-200 MB. A bounded-reorder window would be cheaper but is wrong here, because a bulk offline sync arrives arbitrarily late.
  4. If the map does not fit, partition on hash(session_id) mod P and process shards independently. Every aggregate is per-session, so the partition is exact and no reduce pass is needed to merge across shards.
  5. Emit max_played_ms as watch time and the bitmap cardinality as the distinct count; a gap between cardinality and max_seq + 1 is a loss signal worth emitting alongside, not a reason to reject the session.

Worked solution (20 min)

  1. Classify each required output as idempotent or not under duplicate delivery, and write down which ones therefore need stored state.
  2. Write the single-pass loop: map lookup, max update, bitmap set, no sorting anywhere.
  3. Compute the memory figure out loud: 10^6 sessions times the per-session structure, and compare it against the box you expect to run on.
  4. Describe the hash-partition fallback and argue why it needs no cross-shard merge.
  5. Construct a seek-heavy session by hand (forward 40 minutes, backward 10, forward 5) and show that the maximum is unchanged while a delta-based sum is not.

Expected result: A per-session map of {max played_ms, exact distinct-sequence count} produced in one O(n) pass with O(1) amortised work per record, a stated memory figure of order 100-200 MB at 10^6 sessions, and a hash-partition fallback that requires no second pass.

Verify it yourself

  • Feeding the same batch twice produces byte-identical output.
  • A synthetic session that seeks backwards ten times reports the same watch time as one that plays the same content straight through.
  • Distinct-sequence count never exceeds max_seq + 1 for any session.

Follow-up

  • A session's records arrive split across two batches a day apart. What has to persist for the per-session maximum to stay correct, and what does that cost per open session?
  • played_ms decreases for 0.1% of sessions. Do you clamp, drop the record, or quarantine the session, and what does each choice do to payout counting downstream?

Audit segment boundary alignment across the catalogue without pairwise comparison

Hard · hashing · binary search · batch verification

Every video rendition of a content version must share identical segment boundaries. Per content version you have 6-12 renditions, each with a sorted int64 list of segment start times in milliseconds, up to 3,600 entries for a two-hour title. The audit runs over 10^4 content versions a day. The obvious check compares every rendition against every other element by element, and it is correct. Give a method fast enough for the daily run, localise the first divergence when a version fails, and state the complexity of both.

Approach

  1. Price the naive version first, because it is correct and the argument has to be about cost: C(12,2) = 66 pairs times 3,600 elements is about 2.4 x 10^5 comparisons per version, so 2.4 x 10^9 a day. Embarrassingly parallel, and still CPU you do not need to spend.
  2. Replace pairwise equality with a canonical fingerprint: one streaming 64-bit hash per rendition over the boundary sequence, O(R * S) per version, then group renditions by fingerprint. Do the arithmetic rather than asserting a win: 12 x 3,600 = 4.32 x 10^4 element hashes per version, so 4.3 x 10^8 a day against 2.4 x 10^9 comparisons. That is 5.5x, not an order of magnitude, and 5.5 is not a remembered constant - it is (R - 1) / 2, the R^2 to R factor, which falls to 2.5x on a six-rung ladder. Per element a hash mix also costs more than an int64 compare, so treat 5.5x as an upper bound on element touches, not on wall clock.
  3. The durable reasons to prefer the fingerprint are structural rather than that ratio: each list is read exactly once so renditions never need to be resident together, and 8 bytes per rendition persists, which is what lets the check run incrementally as rungs become ready instead of as a batch. Say which direction can lie: a mismatch is always a real difference, while equality is equality up to collision probability, which is what you are trading away.
  4. Localise a failure with prefix hashes: build a prefix-hash array per rendition in O(S), then binary search for the lowest index at which two renditions' prefix hashes differ. O(log S), about 12 probes instead of a 3,600-element scan.
  5. Check the stronger property at publish time, since equal lists do not prove the boundaries are legal. Frames per segment = segment_duration_ms * frame_rate / 1000, and gop_size_frames must divide it. At exactly 25 fps a 6,000 ms segment is 150 frames and GOP sizes 25, 50 or 75 divide it cleanly. Do the NTSC case in the rational rate, not in the stored column: the true rate is 24000/1001 fps, so a 6,000 ms segment is 144000/1001 = 143.86 frames and can never align, while 6,006 ms is exactly 144 frames. The frame_rate_milli_fps column stores 23976, a rounding that drifts about 1 ms per 1,000 s (roughly 7 ms over a two-hour title) and computes that same aligned segment as 143.999856 frames.

Follow-up

  • The audit is green but one player stalls switching from rung 4 to rung 5 at 00:41:12. What do you check that list equality cannot tell you?
  • Run the audit incrementally as each rendition reaches status ready rather than as a nightly batch. What do you persist per content version, and what does a rung that arrives a day late cost you?

Choose ladder rungs against a measured bandwidth distribution

Hard · dynamic programming · optimisation · cost modelling

You have 60 candidate encoding rungs sorted ascending by bitrate, and an audience bandwidth histogram of 1,000 buckets measured from delivered throughput. A viewer plays the highest rung whose bitrate is at most 80% of their sustained throughput, and cannot play at all if no rung qualifies. Choose exactly 12 rungs, always including the lowest candidate, maximising total delivered bitrate across the audience. Return the chosen rungs and the objective value, state the complexity, and say why a log-spaced ladder is not automatically the answer.

Approach

  1. Precompute for each candidate i the bucket at which viewers can first afford it, threshold_i = bitrate_i / 0.8, by binary search over the bucket edges: O(n log m). Build a prefix sum over bucket counts so any viewer-count range is O(1).
  2. Formulate the contribution: with the selected set sorted, if rung i is selected and j is the next selected rung, every viewer whose threshold falls in [threshold_i, threshold_j) plays rung i, contributing count * bitrate_i. Call it value(i, j), computable in O(1) from the prefix sums.
  3. Run the DP over (last selected rung, rungs used): f[j][t] = max over i < j of f[i][t-1] + value(i, j), with the lowest rung forced selected and a sentinel j = n+1 closing the final interval. O(n^2 k) = 60^2 * 12, about 43,000 transitions. The point is that the ladder is measured, not that the algorithm is clever.
  4. Say when the structure buys more: if value satisfies the concave quadrangle inequality, the inner maximisation admits divide-and-conquer optimisation at O(n k log n). That condition depends on the histogram's shape and must be tested on the real distribution rather than assumed.
  5. Name what the objective leaves out, because the interview point is the modelling: storage and cache footprint scale with rung count, device decode limits make some rungs unusable by whole client classes, and delivered bitrate is a crude proxy for perceived quality. Log spacing is a prior about a smooth unimodal distribution, and measured throughput is usually bimodal, which is exactly where the prior loses.

Follow-up

  • Add a per-device decode cap so one client class cannot use rungs above 1080p. Does the DP still decompose, and along which dimension?
  • Throughput varies during playback and the player switches rungs. What does that do to the highest-affordable-rung model, and what would you measure instead?
  • Storage and cache footprint become a hard budget rather than a fixed rung count. Which dimension of the DP state changes?

Make a monthly payout scan use an index at two billion rows

Medium · sargability · covering index · partitioning · query planning

qualified_play(id BIGSERIAL PK, session_id, content_version_id, rights_holder_id, account_id, territory_code, qualified_at TIMESTAMPTZ, status ENUM('pending','counted','reversed'), source ENUM('online','offline_sync'), dedupe_key UNIQUE, counted_in_period) holds about two billion rows, roughly 150 million per month, and is physically ordered by insert time. The monthly close runs SELECT rights_holder_id, count(*) FROM qualified_play WHERE date_trunc('month', qualified_at) = DATE '2026-08-01' AND status = 'counted' GROUP BY 1 and scans the entire table. Give three separate reasons it does, and the change you would make for each.

Approach

  1. Reason one is the expression. Wrapping the column in date_trunc makes the predicate non-sargable, so no b-tree on qualified_at is usable and the planner also falls back to a default selectivity guess instead of the column's histogram. Rewrite to the half-open range qualified_at >= DATE '2026-08-01' AND qualified_at < DATE '2026-09-01', which selects the same rows. An expression index is the alternative, but in PostgreSQL date_trunc(text, timestamptz) is STABLE rather than IMMUTABLE because it depends on the session TimeZone, so it cannot be indexed directly — you would index date_trunc('month', qualified_at AT TIME ZONE 'UTC') and write the predicate in exactly that form.
  2. Reason two is selectivity, and the fixed predicate does not remove it. One month is about 7.5% of the table; at roughly 50 rows per page the table is about 40 million pages, and 150 million rows scattered across it touch essentially every page. A plain index scan would pay random I/O for the whole table while a sequential scan reads it in order — the planner is right. Make the scan index-only instead: (status, qualified_at, rights_holder_id) covers every column the query touches, so the plan reads index pages alone. That holds only while the visibility map is current, which makes it a dependency on autovacuum keeping up with continuous inserts.
  3. Reason three is structural. status = 'counted' is most of the table and is not selective by itself, but a partial index WHERE status = 'counted' keeps reversed and pending rows out of the index entirely. At two billion rows the real fix is range partitioning by qualified_at month: the close reads one partition, the planner prunes the rest, and the partition boundary lines up with counted_in_period so a period close and a partition are the same object.
  4. Name the BRIN trap before someone reaches for it. BRIN is the obvious cheap index for an append-ordered table and it is tight on the arrival timestamp — but qualified_at is when the threshold was crossed, and offline_sync rows land days later, so physical order correlates with arrival rather than with qualified_at. Check pg_stats.correlation for the column instead of assuming; poor correlation makes every block range a candidate and the index worthless.
  5. Settle it with EXPLAIN (ANALYZE, BUFFERS) rather than argument, and look at estimated versus actual rows for the range predicate first. A plan that goes wrong at this size usually went wrong at the estimate, and a wrong estimate has a different fix from a wrong index.
  6. Sequence the work by cost and risk: rewrite the predicate first because it is free and takes no lock, measure, then build the covering index with CREATE INDEX CONCURRENTLY, and only then consider partitioning, which is a migration of the whole table rather than an addition to it.

Follow-up

  • The close also needs plays that qualified in August but arrived in September. Which of your changes still works, and which partition does a late row live in?
  • The same aggregate is now wanted per territory as well. Do you widen the covering index or accept the scan?
  • How would you prove the scan really is index-only in production rather than on a freshly vacuumed copy?

Collapse heartbeats into plays with a gaps-and-islands query

Medium · window functions · gaps and islands · deduplication

heartbeat(session_id UUID, sequence INT, received_at TIMESTAMPTZ, played_ms BIGINT, playhead_ms BIGINT, profile_id, device_id, content_version_id) is delivered at-least-once and out of order. played_ms is a monotonic counter the client restarts at zero for each new session, and a crashed player resumes under a fresh session_id. Collapse one day of heartbeats into one row per continuous viewing island — same profile, device and content version, no gap longer than 60 seconds — with start, end and watch time. Write the query and state its cost.

Approach

  1. Deduplicate before any window function runs. A heartbeat's identity is (session_id, sequence), so take DISTINCT ON (session_id, sequence) ordered by received_at, or enforce it at ingest with a unique index and ON CONFLICT DO NOTHING. Duplicates leave a MAX-minus-MIN watch time unharmed but corrupt any per-row delta and inflate row counts used as liveness signals.
  2. Island on (profile_id, device_id, content_version_id) ordered by received_at, not on session_id. The crash-and-resume case is precisely the one the report has to merge, and session_id changes across it — partitioning by it guarantees the split you are trying to avoid.
  3. Flag island starts with LAG — CASE WHEN prev_at IS NULL OR received_at - prev_at > interval '60 seconds' THEN 1 ELSE 0 END — then number islands with a running SUM(...) OVER (PARTITION BY profile_id, device_id, content_version_id ORDER BY received_at, sequence ROWS UNBOUNDED PRECEDING). Specify ROWS: with an ORDER BY and no frame clause the default frame is RANGE UNBOUNDED PRECEDING, which includes every peer row sharing the ordering value, so two heartbeats landing on the same instant would each absorb the other's flag.
  4. Accumulate watch time from the counter, grouped by session inside the island: take max(played_ms) - min(played_ms) per (island, session_id) and sum those. Never from playhead_ms deltas — a backward seek makes them negative and a forward seek makes them enormous. The naive version is correct for every session that never seeks, which is most of them, so it survives any test written from a happy-path trace.
  5. Give the cost as O(n log n), dominated by the sort behind the partition and ordering. A b-tree on (profile_id, device_id, content_version_id, received_at) lets the planner feed the window from an ordered index scan and drop the Sort node; without it, a day of heartbeats sorts, and at 10^5 events per second that sort spills to disk.
  6. Handle the open island at the query's upper bound: an island whose last heartbeat falls within the gap of the boundary is still running, and should be emitted with a null end rather than truncated, or the next day's run counts the same viewing a second time.

Worked solution (30 min)

  1. Seed one profile, device and content version with three bursts of heartbeats: two separated by 20 seconds, the third 5 minutes later, with the second burst carrying a new session_id and its played_ms restarted at zero.
  2. Duplicate three heartbeats verbatim and confirm the dedupe step removes exactly three rows.
  3. Add a backward seek: two heartbeats where playhead_ms decreases while played_ms keeps rising.
  4. Run the query and check each island's watch time against the counter arithmetic worked by hand.
  5. Re-run with watch time computed from playhead_ms deltas and record the difference.
  6. EXPLAIN with and without the composite index and compare the presence of a Sort node.

Expected result: Two islands. The first merges both bursts across the session_id change and reports watch time equal to the per-session counter spans summed, so the restart at zero costs nothing. The second island stands alone. The playhead-delta variant under-reports the first island by the length of the backward seek.

Verify it yourself

  • Island count is 2, not 3 — the session_id change inside the first island did not split it.
  • Watch time is non-negative for every island and, at 1x playback speed, does not exceed the wall-clock span between its first and last heartbeat.
  • Removing the duplicated heartbeats changes no island's watch time.
  • Total watch time across islands equals the total computed per session_id and then summed — islanding changes the grouping, not the arithmetic.

Follow-up

  • A device uploads four days of buffered offline heartbeats in one burst. Which islands are wrong, and does ordering by client timestamp instead of received_at fix it or make it worse?
  • Make the 60-second gap configurable per device class. Does the query shape survive, and what happens to the index you relied on?
  • How do you turn islands into qualified_play rows so that re-running the job writes nothing new?

Explain your approach to handling API latency and error states when fetching content.

Medium

Approach

  1. Work from the requirement backwards to the design.
  2. Say what you would check first and why it is the highest-information step.
  3. State your assumptions explicitly before working the problem.

Follow-up

  • How would you know your answer was wrong?
  • What assumption would you test first?

Design a method that takes genre and fires a request to an API which gives a response with movie title.

Medium

Approach

  1. State your assumptions explicitly before working the problem.
  2. Work from the requirement backwards to the design.
  3. Say what you would check first and why it is the highest-information step.

Follow-up

  • What assumption would you test first?
  • How would you know your answer was wrong?

Describe how you would ensure that your method handles high-frequency requests without overloading the backend service.

Medium

Approach

  1. Say what you would check first and why it is the highest-information step.
  2. Clarify what is being asked and what a complete answer contains.
  3. State your assumptions explicitly before working the problem.

Follow-up

  • How would you know your answer was wrong?
  • What assumption would you test first?

Hold the concurrent-stream limit with one atomic lease operation

Easy · leases · atomicity · ttl-tuning · hot-key

An account has a plan limit of concurrent streams. Session starts run 10,000 per second at peak against 1,000,000 concurrent sessions, each renewing its lease every 10 seconds. Two devices in one household can start within the same millisecond, and a client that crashes never releases its slot. Using one lease store, give the exact acquire operation, the data structure and key, the lease TTL with the arithmetic behind it, what a renewal returns when the lease has already expired, and what the client does with that answer.

Approach

  1. Pick a structure that makes expiry part of the read. A sorted set per account, key lease:{account_id}, member session_id, score expiry in epoch milliseconds, lets one operation drop the dead members and count the live ones. Storing a plain counter forces a second structure to know which session holds which slot, and the two drift.
  2. Make acquire one server-side operation: ZREMRANGEBYSCORE key 0 now, then ZCARD, then ZADD only if the count is under the limit, all inside a single Lua script. The script runs to completion without interleaving, so two simultaneous acquires at the limit cannot both observe the pre-add count. Any design where the application reads the count and then writes is a lost-update race that two devices starting together will win together.
  3. Make renewal its own script: return lease-lost when ZSCORE for that session is nil, and otherwise write the new expiry score. Expiry has already removed a member that missed too many heartbeats, so a renewal after a network gap fails and the client must re-acquire, which re-checks the limit. A blind ZADD re-adds the evicted session and quietly takes the account over its plan, which is the same lost-update bug as the naive acquire wearing different clothes.
  4. Derive the TTL instead of picking one. TTL = k x heartbeat interval sets both failure modes at once: a viewer is evicted only after k consecutive missed heartbeats, and a crashed client strands a paid slot for at most the TTL. At 10s heartbeats, TTL 30s tolerates two misses and caps the stranded slot at 30 seconds. Note that heartbeat losses are correlated rather than independent, since one access-network blip takes all three, so any p^k eviction estimate is optimistic.
  5. Bound the keyspace and the hot key. PEXPIRE the whole account key to TTL plus slack so accounts that stopped streaming do not accumulate forever, and release explicitly with ZREM on a clean stop so the common case does not wait out the TTL. Keying by account_id puts all of an account's leases on one shard, which is what makes the script atomic; at a limit of four, that key sees single-digit operations per second, while the fleet total is 1,000,000 / 10s = 100,000 renewals per second spread across shards by account hash.

Worked solution (20 min)

  1. Write the acquire script line by line and mark the point where a non-atomic version would interleave.
  2. Write the renewal script and state what it returns in the expired case and what the client does with that answer.
  3. Fix a heartbeat interval, derive the TTL from it, and state the false-eviction condition and the stranded-slot ceiling as numbers.
  4. Compute fleet-wide renewal operations per second and the per-key rate, then choose the shard key.
  5. Add the clean-release path and the key-level expiry, and say what each one prevents.

Expected result: A per-account sorted set scored by expiry, an acquire that prunes, counts and inserts inside one atomic script, a renewal script that reports lease-lost on an absent member and forces a re-acquire, a TTL of three heartbeat intervals with both failure modes quantified, explicit release plus a key-level expiry, and account_id as the shard key justified by the atomicity requirement.

Verify it yourself

  • Two acquires issued concurrently against an account already at its limit result in exactly one success and one refusal.
  • A session that misses three consecutive heartbeats finds its renewal returns lease-lost rather than silently succeeding.
  • Killing a client without a clean stop frees its slot within the TTL and no later.
  • An account that has not streamed for a day holds no keys in the lease store.

Follow-up

  • The lease store loses a shard for 30 seconds. Do you fail open and let sessions start unchecked, or fail closed and block them, and what does each cost?
  • A viewer switches from wifi to cellular and the app backgrounds for 45 seconds. Trace what they see, and change the design so it is not an eviction.
  • Downgrade a plan from four streams to two while four streams are playing. Which sessions stop, when, and who decides?

Origin egress quadruples while viewer demand stays flat

Easy · cdn caching · cache key · origin load

Overnight, origin egress for segments rose about fourfold while edge-delivered bytes, concurrent sessions and peak bitrate stayed flat. Segment hit ratio at the edge fell from 95% to 81%; manifest hit ratio is unchanged and every POP is affected equally. No playback alarm fired, so the first signal was a cost alert. The previous evening's release added a signed delivery token to segment URLs. Produce an ordered diagnostic checklist, name the change that explains the numbers, and show the arithmetic linking hit ratio to origin load.

Approach

  1. Establish that demand did not change before touching any cache config: compare edge bytes delivered, concurrent sessions and requests per second week over week. Origin volume is (1 - h) x edge volume, so h moving 0.95 to 0.81 takes the origin share from 0.05 to 0.19, a 3.8x rise. The observed numbers are already self-consistent, which rules out a traffic story.
  2. Slice hit ratio by path prefix, content type and POP. Segments down, manifests flat, all POPs equally affected points at a rule change applied to the segment path, not a regional failure, a purge storm or an eviction problem.
  3. Read the cache key the edge actually computes for a sampled segment request rather than the configuration you believe is deployed: which query arguments, headers and cookies are included. Diff it against the previous release.
  4. Count distinct cache keys per segment URL over one hour. A ratio near one key per viewer is proof the token is in the key; a ratio near one is proof it is not, and sends you to storage suppression instead.
  5. If the key is clean, check origin responses on segments for Set-Cookie and for a Vary on a per-request header, either of which stops a shared cache storing the object independently of the key.
  6. Fix by keeping the token in the URL for the edge to verify and excluding it from the key. Verification must be signature-only at the edge (HMAC or public-key over path plus expiry) so it needs no origin callback. State the trade-off out loud: one cached object now serves every holder of a valid token, so token lifetime and path scope become the only limits on URL sharing.

Follow-up

  • How do you rotate the token signing key without breaking sessions already in flight?
  • What token lifetime do you choose, given a segment stays cached for hours and a single playback can run three hours?
  • Why would a staging test with twenty clients never surface this, and what test would?

Suggested practice checklist

For someone who has spent the last few years shipping features and reading other people's code, and who has not solved a timed problem from a blank file in a long time. Five days rebuild the primitives and the patterns that sit on them, working from invariants rather than remembered solutions, and the last two attach that back to the rest of the loop.

Day 1 — Map the two stages and take a coding baseline

  • Write one page mapping this guide's two stages: what the online-assessment Technical Screening covers (DSA, complexity, standard libraries, error handling) and what the Virtual Meeting covers (practical experience, resume depth, team fit)
  • Solve BFS shortest path in a grid from a blank file, then list the edge cases you checked: blocked start or end, no path, a 1x1 grid
  • Solve one hash-map problem, such as returning the index of the first non-repeating character, and state its O(n) time and O(alphabet) space

Deliverable: A one-page stage map and two solved problems, each annotated with complexity and the edge cases you tested.

Day 2 — Data structures you may be asked to build

  • Implement an LRU cache with a hash map and a doubly linked list, with O(1) get and put, and test eviction order after repeated gets
  • Build a set on a resizable array, measure how contains performs as n grows, then rebuild it with hashing and explain the change in complexity
  • Write the rule-filter function from the bank's 'filter ads by a single rule' topic, then extend it to a list of rules and say how you would keep it readable

Deliverable: Three working implementations, each with tests for empty input, duplicates and the resize or eviction boundary.

Day 3 — The reported genre API method, built correctly

  • Write down the API contract you would confirm first: endpoint, genre parameter format, pagination, auth and response shape
  • Implement the method: signature, HTTP request, JSON parsing into a typed movie model, and a return type that separates an empty result from an error
  • Add a timeout, separate 4xx from 5xx and network errors (treating 429 as retryable after backoff), and handle a malformed body, then write one test for each failure path
  • Explain the finished method aloud in the order signature, request, mapping, errors

Deliverable: A working genre-to-movie-title method with failure-path tests and a recorded spoken walkthrough.

Day 4 — Latency, errors and high-frequency requests

  • Answer the reported latency and error-state question in writing: timeouts, retries with exponential backoff and jitter for transient failures and 429 only, and what the caller sees when the service is down
  • Answer the reported high-frequency question in layers: per-genre cache with TTL, coalescing identical in-flight requests, a client-side token bucket, and a circuit breaker, with the cost of each
  • Work through this guide's worked exercise on the concurrent-stream lease (drill-design-6) to practise reasoning about atomic limits under simultaneous requests
  • Sketch the bank's thread-safe buffered batch writer and name the lock or queue that makes it safe

Deliverable: A one-page answer to both reported questions that ranks the protections in the order you would build them.

Day 5 — Data mapping, serialization and design breadth

  • Work through this guide's SQL worked exercise (gaps and islands over heartbeats) and the coding worked exercise on deduplicating at-least-once records, focusing on how raw data turns into correct aggregates
  • Practise mapping a nested JSON response into typed objects and handling missing or unexpected fields without crashing
  • Sketch one Disney-tagged System Design bank prompt, such as e-commerce browse and search or the global multi-game leaderboard, down to the endpoints, stored data and the query pattern that determines the schema

Deliverable: Two worked exercises checked against their stated results, plus one design sketch with endpoints and schema.

Day 6 — Virtual Meeting: resume depth and behavioral stories

  • Choose two or three projects with real technical hurdles and write the what, why and how of each key decision, with the figures that must not change between tellings
  • Go through every tool on your resume, including any cloud or infrastructure-as-code work, and prepare one level of detail beyond what the resume states
  • Prepare stories for collaboration and stakeholders, your proudest project and interest in the role, and a feature you owned from conception to deployment
  • Prepare an honest answer for a question you cannot fully answer: say what you know, what you would check, and how you would reason toward it

Deliverable: A written project sheet and four behavioral stories, each rehearsed aloud with one follow-up question.

Day 7 — Full rehearsal of both stages

  • Solve one new coding problem in assessment conditions: no hints, edge cases tested before you submit, complexity written in a comment
  • Have a friend run a mock Virtual Meeting: a resume deep-dive, then the genre API question pushed from a correct call to load protection
  • Re-solve from a blank file the problem you were slowest on this week and compare your approach with your first attempt

Deliverable: A mock-interview debrief listing the weakest answer from each stage and the change you will make before the real interview.

The failure that was yours

The Virtual Meeting is described as a conversation about practical experience and team fit, where interviewers pick specific points from your resume. Prepare stories that reach the level of a decision: what you chose, what you rejected, and what happened next. Keep the facts (team size, timeline, your own part) the same every time you tell a story.

  1. Walk me through a project on your resume where you hit a significant technical hurdle. What did you choose, and what alternative did you reject?
  2. Tell me about a feature you owned from conception to deployment. What changed between the first design and what shipped?
  3. How do you manage collaboration with stakeholders who want different things from the same feature?
  4. Describe an integration with an external API that failed in production. How did you find the cause, and what did you change?
  5. What project are you proudest of, and how does it connect to your interest in this role?
  6. Tell me about a time you were unsure of the right technical answer. How did you get to a decision?

Turn an inherited offline licence expiry into a real decision

Medium · offline drm · rights expiry · decision records

license_grant.policy_expires_at for offline downloads is 30 days everywhere, inherited from a library default nobody chose. It is set at issue time and is the only enforcement that reaches a device which never reconnects. Some availability windows close sooner than 30 days. Describe turning an implicit behaviour into an explicit, owned decision: how you surfaced it, who had to be in the room, the rule you proposed and its cost to viewers, and how you verified it took effect.

Approach

  1. The probe is whether you can convert a default into a decision without theatre. Quantify first: count persistent grants whose policy_expires_at is later than the ends_at of the active availability_window for that content version and territory. That turns a feeling into a row count and a worst-case overrun in days.
  2. State the mechanism precisely, including its escape hatch, because overstating it costs you the room. The policy on an issued licence is fixed on the device, so a replacement or revocation lands only if the device reconnects. The 30 days is a worst case over the population that never comes back, not a claim about every download.
  3. Propose the rule as a computation at issue time rather than a smaller constant: policy expiry equals the minimum of the product default, the issuing territory's window ends_at, and the entitlement horizon such as the paid-through date. Name the viewer cost in the same breath - a download taken the night before a window closes now expires the next day, which reads as a bug to someone on a flight - and say what the client shows them.
  4. Name who must agree and what each of them is actually deciding: the rights owner on the bound, product on the experience of a short expiry, and engineering on whether the window lookup is correct and available on the licence path at its request rate.
  5. Write it where it survives you. A decision record naming the rule, its owner, the three inputs to the minimum, and the null case: ends_at NULL means perpetual, and a comparison that treats NULL as a value yields either no bound or no window at all depending on the expression you wrote.
  6. Verify by re-running the count rather than reading the diff. After rollout the query from step one should return no new overrunning grants, and the pre-existing overhang should be a decaying population you can plot to the date it reaches zero.

Follow-up

  • A window is extended after you issued a short licence. What does the viewer see, and what do you do about it?
  • ends_at is NULL for a perpetual window. Show me exactly what your minimum computes.
  • The licence path now depends on the availability service at roughly 10^3 QPS. What happens when it is unavailable?

Own the outage where a token entered the cache key

Medium · cdn cache key · incident response · blast radius · origin load

A client release began appending a per-session delivery token to segment URLs, and the edge included the full query string in its cache key. Segment hit ratio fell from 95% to near zero and origin absorbed the whole read load at peak. Take the on-call role. Describe an incident you owned of comparable blast radius: what you saw first, how you sized it, what you changed to mitigate, and why a client rollback was not the fix. Give wall-clock timings and the metric that sized the damage.

Approach

  1. The probe is whether you convert offload into origin load under time pressure. Open with the invariant that broke - a cache key contains only what changes the bytes returned - because that is what tells the listener where to look, and the symptom does not.
  2. Size it arithmetically rather than in percentages: at 200,000 concurrent viewers averaging 4 Mbps, aggregate demand is 800 Gbit/s, and a 95% hit ratio leaves origin serving 40 Gbit/s. A per-viewer cache key drives the miss ratio to nearly 1 and asks origin for the full 800 Gbit/s - a 20x step with no increase in viewers.
  3. Say how it presented. The edge keeps serving, so this is origin saturation and 5xx on segment fetches rather than slow pages. Name the two metrics that sized it in the first five minutes: hit ratio broken down by cache-key pattern at the shield, and origin request rate and egress.
  4. Separate mitigation from fix and say why rollback was the wrong lever: the offending behaviour is in a player on devices you do not control, updating on their own schedule over days to weeks. The only change with a minutes-long time constant is edge configuration - verify the token signature, then exclude it from the key. The precondition is that the token must not change which bytes are returned, or stripping it serves the wrong object.
  5. State how the edge verifies without help: the token is a signature over path and expiry with the verification key distributed to the POPs, because a callback to origin on every miss calls the service you are trying to protect.
  6. Close on the prevention change and one error you made inside the response window. The durable guard is a cache key built from a whitelist, checked before deploy, plus a hit-ratio alert whose threshold is stated in origin load - at 95%, one point is 20% more origin traffic.

Follow-up

  • The edge now strips the token from the key. What stops a viewer pasting one segment URL on a forum?
  • You are at 95% and want 97%. Where do you look, and what does each point buy in origin bandwidth?
  • How would you have caught this in staging, where three people are watching?

Unblock an engineer whose stream-limit check keeps racing

Easy · mentoring · atomicity · lease

An engineer on your team implemented the concurrent-stream limit as a count query followed by an insert into a leases table. It passes every test and fails in production when a household starts two devices together. They have spent two days adding retries. Describe a time you unblocked someone iterating in the wrong direction: how you diagnosed it without taking the keyboard, what you taught as opposed to what you fixed, and how you knew they could handle the next one alone.

Approach

  1. The probe is whether you diagnose the class of bug rather than patching the instance. Name it as check-then-act across two statements: under READ COMMITTED each transaction's snapshot excludes the other's uncommitted insert, so with a limit of two both count one, both insert, and the account ends with three leases. Retries cannot help, because neither transaction ever sees an error.
  2. Teach the test before the fix. The test that fails is two acquires started concurrently with exactly one success asserted; a test that calls acquire twice in sequence passes against the broken code, which is precisely why their suite is green. Getting them to write the concurrent one first is the part that transfers to the next problem.
  3. Offer the fix as options with costs and let them choose. One atomic operation in the lease store - a single script doing ZREMRANGEBYSCORE to drop expired leases, ZCARD to count, then a conditional ZADD, atomic because the script runs to completion without interleaving. Or a fixed slot table with a UNIQUE key on (account_id, slot_index) so the collision surfaces as a constraint violation you can catch. Or SERIALIZABLE with a bounded retry on SQLSTATE 40001, the one variant where the retry loop they already wrote earns its place.
  4. Make them state the TTL trade-off in both directions, because it has no comfortable setting: with a ten-second heartbeat and a thirty-second lease, a viewer must lose three consecutive heartbeats to be evicted mid-title, and a crashed client strands a paid slot for up to thirty seconds. Push back on any per-heartbeat independence assumption - losses are correlated, since one handover takes out a whole run - so the thing to reason about is the distribution of gap durations, not a loss rate cubed.
  5. Say how you stayed out of the keyboard: the anomaly named, one question they had to answer themselves about what the second transaction sees, and a review of their test before their fix.
  6. Close on the evidence it worked - what they did unprompted on the next lease-shaped problem, such as the renewal path or releasing a slot on a clean stop.

Follow-up

  • They choose the atomic script. What happens to leases when that store fails over and loses a second of writes?
  • How does a slot get freed for a client that stops reporting with no end event?
  • What test fails if someone later replaces the script with two round trips?