A Software Engineer at Spotify does not just write code; they build the technologies that connect millions of artists with hundreds of millions of fans worldwide. Working within Spotify’s famous decentralized, autonomous "squad" model, engineers own their products end-to-end. This means you will have a direct hand in designing, developing, deploying, and maintaining systems that operate at a massive global scale, handling petabytes of data and millions of concurrent audio streams.
The impact of this role is felt across various critical domains, from optimizing the core streaming playback engine and perfecting personalization algorithms to building robust platform infrastructure (Developer Experience/DX) that empowers other internal teams. Whether you are working on the client-facing mobile and web applications, backend microservices, or data engineering pipelines, your work directly influences how the world experiences audio.
Spotify looks for engineers who are not only technically excellent but also highly collaborative, adaptable, and aligned with a strong engineering culture. The environment is highly iterative, valuing rapid experimentation, continuous learning, and a healthy approach to failure. To succeed here, you must be comfortable navigating ambiguity, driving alignment across cross-functional teams, and maintaining a relentless focus on user experience.
Application Review
reportedThe title covers product work, platform work, infrastructure, mobile and frontend, and those are different jobs with different loops behind them. A screening call is the cheapest place to find out which one the seat is, and asking reads as experienced rather than fussy. The questions that separate them: what the team is on call for, what the last three projects were, and whether any round happens inside an existing repository instead of a blank file. Then say which of that you have done and which you have not. Claiming the whole posting is the fastest way to be found out one round later.
What to demonstrate
- Whether you can locate your experience inside one flavour of the role honestly instead of claiming the entire requirements list
- Whether you name what you have not done, which an experienced screener reads as a level signal and can plan the loop around
- Whether what you want next matches what the seat is: someone who wants greenfield work landing on a team that mostly operates an existing system is a hire that leaves within the year
How to prepare
- Mark every line of the posting as done, adjacent or new, and write one sentence for each adjacent line naming the closest thing you actually built
- Split your last two years into rough percentages across feature work, operating and debugging live systems, and design or review, so a question about scope gets numbers rather than adjectives
- Bring three questions that discriminate between seats: what the team is paged for, how much of the work is changing existing code versus standing up something new, and what shipped in the last quarter
Online Assessment
reportedMost of the time lost in this format is not lost to thinking. It goes to a standard-library call you half-remember, an off-by-one in a loop bound, and a debugging loop that mutates code at random until something passes. When output is wrong, stop re-reading the whole function: take the smallest input that reproduces it and walk the state through by hand, printing intermediates if the environment allows. Guessing at a fix without a failing case you understand is how a five-minute bug becomes twenty, and the clock does not pause while you do it.
What to demonstrate
- Whether you reach the right structure without a detour, and can write it from memory rather than only recall that one exists
- Whether overflow is considered where the language has fixed-width integers, since a signed 32-bit value stops at 2,147,483,647 and then wraps in Java, is undefined behaviour in C++, and does not arise in Python, whose integers grow instead
- Whether recursion depth is treated as a constraint on large inputs, given that CPython's default limit is 1000 frames and a deep recursion can exhaust the stack in any language where an iterative version would not
- Whether a failing case is isolated and explained before any edit is made to the code
How to prepare
- From an empty file and with no references open, implement the pieces you lean on most: a heap push and pop, an iterative DFS with an explicit stack, and a binary search whose midpoint is written lo + (hi - lo) / 2, which avoids the overflow that (lo + hi) / 2 can hit in a fixed-width integer type
- Time yourself on the ten library calls you look up most, such as sorting with a custom comparator, splitting and joining strings, and finding the next key at or above a value in an ordered map, until the lookup is gone
- Take a solution you know is broken and, before touching it, write one sentence naming the input, the expected value and the actual value. Repeat until you do it without deciding to.
Recruiter Call
reportedHalf of this call is the part candidates treat as small talk: start date, notice period, work authorisation and its timing, location and time zone, on-call, and the number. Those are what kill offers late, after several engineers have each spent a day. Surfacing a hard constraint now costs you nothing and occasionally buys you something, since a loop compressed to fit a competing deadline can usually only be arranged if it is asked for early. The common failure is deflecting the compensation question twice, then discovering at offer stage that the band never reached your number.
What to demonstrate
- Whether your hard constraints are compatible with the role before a loop gets booked: earliest start, notice period, what authorisation you hold and when it needs action, days on site, willingness to carry a pager
- Whether you give a compensation range with something behind it, such as current total compensation or a competing timeline, rather than leaving the band untested
- Whether your stated timeline is real, since a competing deadline raised now is something scheduling can sometimes work around and the same deadline raised at offer stage usually is not
How to prepare
- Write each constraint down in one line before the call and state them as facts rather than negotiating them live under a question you were not expecting
- Set your range from two or three current data points for that level and location, and name the structure you are quoting in, so the number is comparable to the one they are holding
- If another process is running, say where it stands and by when, and ask directly whether this loop can be scheduled inside that window
Technical Screening
reportedWhat this round decides is narrow: whether you can produce code that runs and is correct on inputs nobody showed you. An elegant solution that does not compile scores below a plain one that does, so write a correct brute force first, say out loud that you know its cost, and improve it with the working version still on screen. What separates strong answers is who finds the broken case. Trace your own code against an empty input, a single element, and duplicate keys before you say you are finished, because being told is far more expensive than noticing.
What to demonstrate
- Whether degenerate inputs get checked without being asked for: an empty collection, one element, every element equal, and the extreme value the input type allows
- Whether the complexity you state matches the code you actually wrote, including a sort or a copy sitting inside a loop
- Whether the finished answer is verified against the worked examples before you call it done, rather than assumed correct because the code reads correctly
How to prepare
- Take five problems you have already solved and, without running anything, write down what each returns for empty input, a single element, and all-duplicates. Then run them and count how many you predicted wrong.
- Drill the brute force as its own skill: on ten problems, write only the obviously-correct slow version and time how long it takes to get it passing. If that is more than a few minutes, that is what to practise, not the optimal version.
- Add a fixed last step before you submit anything, reading only the loop bounds and the initial value of each accumulator, which is where most off-by-one errors live
Virtual Onsite Loop
reportedWhere the day includes a partner from product, design or data, that conversation is weighted like the technical ones and prepared for least. They are deciding one thing: whether having you in the room makes their decisions cheaper. That means options with costs attached, not implementation detail and not "it depends". An estimate someone can plan against — a range, the assumption that would push it to the high end, and what you would drop to hit the low one — is worth more than a confident single number, which everyone present already knows is wrong.
What to demonstrate
- Whether an estimate comes as a range with the assumption most likely to break it, and states what a specific scope cut would actually buy
- Whether a technical constraint is handed over as a choice with consequences on their side, rather than as a verdict they have no standing to argue with
- Whether you establish what decision is on the table before proposing anything
- Whether risk is raised while it can still change the plan, with the trigger that would confirm it, instead of reported afterwards as a slip
How to prepare
- Take a project that shipped late and write the two-sentence warning you could have given three weeks earlier, naming what you would have needed decided at that point
- Rehearse one estimate out loud until it arrives in three parts: the range, the single assumption that would blow it, and the smallest thing you would cut to protect the date
- Rewrite an objection you have actually made — the "we can't do that" version — as two options with their costs, so the choice ends up with the person who owns it
8 candidate reports. Individual accounts describe a particular role and hiring cycle.
Spotify Software Engineer interview: DSA, system design, and behavioral loop
My process started with an HR screening, followed by a technical and behavioral screen with two engineers. That round used a HackerRank-style setup and included what felt like standard DSA questions. I then went through a full loop with several interview slots: two more DSA coding rounds, one system design round, and one behavioral interview. The format was methodical. Each interviewer seemed to…
Read full experienceSpotify Full Stack Engineer interview: Backend Tech Screen changed to Web Tech Screen
I had a phone screen followed by a technical interview with two senior software engineers. The interview invite initially said "Backend Tech Screen." The night before, the recruiter sent an updated invite that kept the same time. I noticed the change while checking the details, then apologized for sending too many messages. On the day of the interview, I got front-end questions instead. Only afte…
Read full experienceSpotify Software Engineer interview with a difficult technical panel
My first contact was a recruiter call covering the usual basics: my background and resume, why I was interested in Spotify, why the role suited me, current compensation and expectations, work authorization and visa status, and my availability. The later interviews were noticeably harder than the early stages. I reached a technical round with a hiring panel led by a senior-level engineer, and the…
Read full experienceSpotify Software Engineer interview with a 75-minute side-project and coding session
After speaking with a recruiter by phone, I moved into one focused interview with a senior software engineer. It lasted about 75 minutes. The format was structured: I introduced a side project, then answered technical questions based on what I'd shared, and finally did live coding in the style of classic LeetCode problems. The pacing felt fairly straightforward, but the interview was still techni…
Read full experienceSpotify Data Scientist interview: five-week wait for feedback
My process started with a recruiter screen, followed by a technical round with two MLEs. That part was focused and direct. The final step was a behavioral interview with the hiring manager. It took about five weeks to hear back. They still came back with feedback after making a decision, which made the wait feel less abrupt than I expected. Location: United States. Overall feedback: Positive. Off…
Read full experiencePracHub editorial advice for the preparation topics above.
Treating the manifest as the authorisation boundary.
Checking entitlement when the manifest is requested feels natural, because that is where the session visibly begins. But the manifest is a cacheable document and the segment URLs inside it carry no identity, so anyone who obtains it plays until the delivery token expires, and a subscriber who cancels mid-title finishes the title. Authorisation has to sit at the licence request, the only thing on the path that is per-user and uncacheable by construction.
Assuming one encrypted copy of the ladder serves every DRM system.
Common encryption (ISO/IEC 23001-7) defines several protection schemes, and two matter in practice: AES-CTR full-sample ('cenc') and AES-CBC pattern encryption ('cbcs'). One of the three widely deployed DRM systems accepts only cbcs, and older client versions of the other two accept only cenc, so a single-copy strategy is correct only if every device class you still support has reached cbcs. Discovering late that one legacy class has not means a second packaging run, a second set of objects in every cache, and roughly double the storage for the affected catalogue.
Issuing one query per row of a result set
Fetch related rows in a single batched query keyed by the ids you already hold, or join them into the original query. A per-row round trip multiplies network latency by the row count, and it looks perfectly fine against the ten rows in your development database.
Not asking what the system looks like if it dies halfway through
For any multi-step write, say what state remains if the process stops between step two and step three, and what brings it back: a single transaction, a saga with compensating actions, an outbox, or a reconciliation job. Partial failure is routine at any real call volume, so 'that shouldn't happen' is an answer with nothing behind it.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
Write a function to remove duplicate characters from a string, allowin…
Write a function to remove duplicate characters from a string, allowing up to a specified maximum number of duplicates to remain.
Approach
- Choose the data structure from the access pattern, not from familiarity.
- Restate the input: its shape, its size, and what is guaranteed about it.
- Name the brute-force solution and its complexity before improving on it.
Follow-up
- How does this change if the input no longer fits in memory?
- What is the worst case, and how likely is it on real data?
Solve a sliding window problem to find the longest substring or subseg…
Solve a sliding window problem to find the longest substring or subsegment matching a specific set of criteria.
Approach
- Name the brute-force solution and its complexity before improving on it.
- Restate the input: its shape, its size, and what is guaranteed about it.
- Walk one small example through your approach before writing the whole thing.
Follow-up
- How does this change if the input no longer fits in memory?
- Which test case would catch an off-by-one here?
Implement a data structure class (such as a queue or a custom cache) w…
Implement a data structure class (such as a queue or a custom cache) with specific time-complexity constraints for its core operations.
Approach
- Name the brute-force solution and its complexity before improving on it.
- State the target complexity and say which constraint rules the naive version out.
- Restate the input: its shape, its size, and what is guaranteed about it.
Follow-up
- How does this change if the input no longer fits in memory?
- What is the worst case, and how likely is it on real data?
Parse a live media playlist for seek offsets and deltas
You are given two successive snapshots of an HLS media playlist as text: #EXT-X-TARGETDURATION, #EXT-X-MEDIA-SEQUENCE, then repeated #EXTINF:, lines each followed by a segment URI, with #EXT-X-DISCONTINUITY markers possible between entries. A four-hour DVR window at two-second segments is 7,200 entries. Build a structure that answers which segment contains playback position P in better than linear time, and compute which segments were added and removed between the two snapshots. State the complexity of both the build and each query.
Approach
- Single-pass parse into an array of (media_sequence_number, duration_ms, uri, discontinuity_flag), converting each #EXTINF decimal-seconds value into integer milliseconds at parse time. Accumulating 7,200 floating-point seconds drifts, and every comparison downstream wants exact integers.
- Build prefix sums of duration_ms alongside the array, O(n) time and O(n) space, and answer a position query by binary search over the prefix array: O(log n), about 13 probes at 7,200 entries.
- Reset the accumulator at each #EXT-X-DISCONTINUITY and keep a per-discontinuity base. A discontinuity is a timeline break, so a prefix sum that runs straight through one maps every position after it to the wrong segment. The query then returns (discontinuity index, offset within it) rather than a single scalar.
- Diff by media sequence number, not by URI or by position. The first entry's number is #EXT-X-MEDIA-SEQUENCE and each subsequent entry is one greater, so added = numbers in B above A's maximum and removed = numbers in A below B's first. That is O(added + removed) and survives URI reuse across a discontinuity.
- Tie it back to delivery: every viewer refetches this document about once per target duration, so the delta is the thing you would actually transmit, and it is the reason a 7,200-entry playlist is a bandwidth problem rather than a parsing problem.
Worked solution 25 min
- Write the parser loop and state where the decimal-seconds to integer-milliseconds conversion happens and why not later.
- Build the prefix array and write the binary search, including the tie rule at an exact boundary.
- Insert a discontinuity into a small example and show the query result before and after resetting the accumulator.
- Compute added and removed from media sequence numbers on two snapshots that differ by three segments.
- State the playlist size in bytes for the stated DVR window and note what that implies for refetch traffic.
Follow-up
- Add #EXT-X-BYTERANGE so several segments share one resource. What changes in the structure, and what does a seek now have to return?
- The playlist is served as a delta against a client-held version. What must the server retain, for how long, and what happens to a client that has fallen further behind than that?
Put segment duration where alignment cannot drift
content_version(id, title_id, duration_ms, encoder_build_id, packaging_status) and rendition(id, content_version_id, stream_kind ENUM('video','audio','subtitle'), width, height, frame_rate_milli_fps, segment_duration_ms, gop_size_frames, status). Every video rendition of a content version must have identical segment boundaries, and each segment must begin at an IDR frame, so gop_size_frames has to divide the segment length in frames. Both facts currently live per rendition and nothing stops a re-encoded rung from disagreeing. Restructure so the database rejects drift, and say what breaks at a frame rate of 24000/1001 with a 2,000 ms segment.
Approach
- Identify the functional dependency. For video rows,
segment_duration_msis determined bycontent_version_id, not byrendition_id. A dependency stored at the wrong grain is exactly what lets two rows disagree, and the invariant is presently a sentence in a comment rather than anything the database can refuse. - Move the authoritative value to the parent as
content_version.segment_frames. Ask whether the child copy earns its keep: the manifest build already loads the content_version row to resolve the URL, so the join costs nothing and the denormalised copy buys no read. If it is kept anyway for write-path convenience, guard it with a composite foreign key —UNIQUE (id, segment_duration_ms)on the parent andFOREIGN KEY (content_version_id, segment_duration_ms) REFERENCES content_version(id, segment_duration_ms)on the child. Under the default MATCH SIMPLE semantics a NULL in either child column satisfies the constraint, which is the escape hatch that lets audio and subtitle rows carry no video segment length. - Fix the representation before writing any CHECK. 24000/1001 is 23.976023976… and is not representable in integer thousandths, so
frame_rate_milli_fps = 23976is already a different number from the real rate and every derived value drifts from it. Store the exact rational asframe_rate_num INTEGER, frame_rate_den INTEGER— 24000 and 1001 — and store the segment length in frames rather than milliseconds. - With frames and an exact rational, both invariants become integer CHECKs that cannot round:
CHECK (segment_frames % gop_size_frames = 0)for IDR alignment, and the millisecond duration becomes derived —segment_frames * 1000 * den / num— rather than a second stored copy to keep in sync. - Work the stated case: at 24000/1001, 2,000 ms is 48000/1001 frames, about 47.952, so no segment of that target length contains a whole number of frames and no integer GOP size divides it. 96 frames is exactly 4.004 s and admits any GOP size dividing 96. The encoder has to round a millisecond target to whole frames, and that rounding is where two rungs encoded at different times end up on different boundaries.
- Accept what the schema still cannot catch. Rate control can relocate an IDR without changing any stored column, so add a post-packaging QC step that compares segment counts and first-sample timestamps across the video renditions of a version and fails the publish on a mismatch, and pin
encoder_build_idper rendition so a rung re-encoded on a newer build is a visible difference rather than an inference.
Worked solution 30 min
- Work the frame arithmetic for 25, 30000/1001 and 24000/1001 at 2,000 ms and at 4,004 ms, marking which combinations yield a whole number of frames.
- Draft both schemas — milliseconds on the child with a composite FK, frames on the parent — and write the CHECK constraints each one admits.
- Insert two video renditions of one content version with different segment lengths and confirm the second is rejected.
- Insert an audio rendition with a null video segment length and confirm it is accepted.
- Write the post-packaging QC query that compares segment counts across the video renditions of one version and returns the offending rendition ids.
Follow-up
- One rung fails QC and is re-encoded alone. What do you verify before republishing the manifest, and what is the rollback if boundaries moved?
- Audio renditions legitimately use a different segment length from video. Which of your constraints still applies and which has to be weakened?
- How do you migrate the existing millisecond column to frames without a window in which neither constraint holds?
Serve a rights window where a null end means perpetual
availability_window(id, content_version_id, territory_code CHAR(2), licence_type ENUM('subscription','ad_supported','rental','purchase'), starts_at, ends_at NULL, max_resolution_cap NULL, status ENUM('draft','active','withdrawn')) carries INDEX (content_version_id, territory_code, starts_at, ends_at). A null ends_at means perpetual. Playback start asks: may this content version be served in this territory, on this licence type, at this instant, and under what resolution cap? Write that query. Then say which index columns actually narrow the scan and which are only filters, and what should happen when one row's ends_at equals another row's starts_at.
Approach
- Name the failure in the naive predicate first.
:now BETWEEN starts_at AND ends_atexpands to:now >= starts_at AND :now <= ends_at, and on a perpetual row the second comparison is:now <= NULL, which is unknown rather than true. Three-valued logic drops the row with no error, so every perpetual grant becomes unplayable and nothing in the system complains. - Write it as a half-open interval with an explicit null branch:
status = 'active' AND content_version_id = :cv AND territory_code = :terr AND licence_type = :lt AND starts_at <= :now AND (ends_at IS NULL OR ends_at > :now). Half-open means a renewal row starting exactly when the previous row ends matches exactly one row at the boundary instant rather than two or zero. - Read the index honestly.
content_version_idandterritory_codeare equalities and set the scan range;starts_at <= :nowis the first inequality and closes it.ends_atsits after a range column, so in a b-tree it cannot narrow the scanned range at all — it is evaluated against each index tuple in the range.licence_typeis not in the index, so it is a filter after the fetch; putting it beforestarts_atwould make it a third equality. - Put the index in proportion: a single (content version, territory) pair has a handful of window rows, so the range is tiny whatever the column order. The index exists so the lookup does not touch other titles, not to make the range tight — say that rather than over-tuning a predicate whose selectivity is already a few rows.
- Decide the multi-match case deliberately. Subscription and rental windows for the same content version can both be active, which is legitimate; two active rows for the same triple are the overlap data bug. The serve path should take the most restrictive
max_resolution_capand raise, because picking arbitrarily means two viewers in one territory get different caps from the same data.
Follow-up
- What constraint would stop two overlapping active rows for the same (content_version_id, territory_code, licence_type) from being written at all?
- The contract says the window closes at midnight local to the territory, but
starts_atandends_atare instants. Where does the local-time-to-instant conversion live, and what happens in a territory that shifts its clocks? - How stale is a cached answer to this query allowed to be, given that the cache TTL is the exposure window for a rights change?
Design a real-time music recommendation system that scales to handle m…
Design a real-time music recommendation system that scales to handle millions of active users.
Approach
- State the consistency you need, and where you are willing to be stale.
- Name the failure you are designing for, then the recovery path.
- Fix the scope first: who calls this, how often, and what they do when it fails.
Follow-up
- How does this behave when that dependency is down for an hour?
- What breaks first when traffic grows ten times?
How would you design the backend architecture to support a live audio-…
How would you design the backend architecture to support a live audio-streaming feature with minimal latency?
Approach
- Name the failure you are designing for, then the recovery path.
- State the consistency you need, and where you are willing to be stale.
- Fix the scope first: who calls this, how often, and what they do when it fails.
Follow-up
- What breaks first when traffic grows ten times?
- What would you drop to keep the system up under load?
For frontend-focused roles, design a modular web application dashboard…
For frontend-focused roles, design a modular web application dashboard that efficiently handles real-time state updates and heavy data visualization.
Approach
- Fix the scope first: who calls this, how often, and what they do when it fails.
- State the consistency you need, and where you are willing to be stale.
- Name the read and write paths separately; they rarely have the same bottleneck.
Follow-up
- What breaks first when traffic grows ten times?
- What would you drop to keep the system up under load?
Predict the exact output and order of execution for a complex JavaScri…
Predict the exact output and order of execution for a complex JavaScript snippet involving nested timeouts, promises, and synchronous code.
Approach
- Clarify what is being asked and what a complete answer contains.
- Say what you would check first and why it is the highest-information step.
- State your assumptions explicitly before working the problem.
Follow-up
- What assumption would you test first?
- How would you know your answer was wrong?
Hold the concurrent-stream limit with one atomic lease operation
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
- 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.
- 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.
- 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.
- 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.
- 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
- Write the acquire script line by line and mark the point where a non-atomic version would interleave.
- Write the renewal script and state what it returns in the expired case and what the client does with that answer.
- Fix a heartbeat interval, derive the TTL from it, and state the false-eviction condition and the stranded-slot ceiling as numbers.
- Compute fleet-wide renewal operations per second and the per-key rate, then choose the shard key.
- Add the clean-release path and the key-level expiry, and say what each one prevents.
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?
How would you debug a memory leak occurring in a production environmen…
How would you debug a memory leak occurring in a production environment that only manifests under high concurrent user load?
Approach
- Establish what changed and when, before forming any theory.
- Separate the trigger from the cause; the deploy is rarely the bug.
- Say what evidence would prove you wrong, then go and look for it.
Follow-up
- What would you look at first, and what would it rule out?
- How would you tell a cause from a coincidence here?
You are notified of a sudden spike in 5xx errors on a core streaming A…
You are notified of a sudden spike in 5xx errors on a core streaming API. How do you triage, isolate, and mitigate the issue under pressure?
Approach
- Check the instrumentation before believing the symptom.
- Separate the trigger from the cause; the deploy is rarely the bug.
- Establish what changed and when, before forming any theory.
Follow-up
- What would you add now so this is faster to diagnose next time?
- How would you tell a cause from a coincidence here?
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.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Rebuild the primitives by implementing them
- Implement a dynamic array with doubling growth and an operation counter, then change the growth rule to add a fixed sixteen slots instead, and time both for n of ten thousand, a hundred thousand and a million. The fixed-increment version resizes n/16 times at O(n) each, so its total work is quadratic; doubling is what makes append amortised constant.
- Implement a hash map with separate chaining and a load-factor resize, then insert ten thousand keys engineered to land in one bucket and record what happens to lookup time, so that average-case O(1) becomes a claim with a stated precondition rather than a reflex.
- For dynamic-array append and hash-map insert, write down which cost is amortised rather than worst-case, which single operation pays the whole bill, and what a system with a hard per-operation deadline would have to do instead.
Deliverable: Two working implementations plus a timing table showing the input at which each structure's advertised complexity stops holding.
Practice prompt ↗Practice prompt ↗Practice prompt ↗Worked solution ↗02Arrays under an invariant: two pointers, sliding window, binary search
- Solve longest-subarray-with-sum-at-most-K using a sliding window, then run it on an input containing negative numbers and watch it return the wrong answer: extending the window only moves the sum monotonically when every element is non-negative, and that precondition is the whole reason the technique works.
- Write the binary search that finds the first index satisfying a predicate rather than an exact value, put the loop invariant above the loop in a comment, and verify termination on the two inputs that break careless versions: the empty range, and a range where every element satisfies the predicate.
- Compute the midpoint as lo + (hi - lo) / 2 and write one line on why the obvious (lo + hi) / 2 is a genuine defect in a fixed-width integer type and a non-issue in a language with arbitrary-precision integers.
Deliverable: Three solved problems, each with its invariant written above the loop, plus one recorded input on which the sliding window is provably wrong.
Practice prompt ↗Practice prompt ↗Practice prompt ↗03Sorting, heaps, and the greedy argument that has to be proved
- Solve one top-k problem three ways, by full sort, by a size-k heap, and by quickselect, then write the values of n and k at which each becomes the right choice, along with quickselect's quadratic worst case and why a randomised pivot makes that unlikely rather than impossible.
- Implement bottom-up heapify and count sift-down steps to confirm it does linear work rather than n log n, because most nodes sit near the bottom of the tree and therefore move only a short distance.
- Take interval scheduling by earliest finishing time and write the exchange argument out in full: given any optimal schedule, swapping in the earliest-finishing interval keeps it feasible and no smaller. Then construct the weighted variant where that same greedy fails and name what has to replace it.
Deliverable: A three-way top-k comparison with measured crossover points, one written exchange argument, and one counterexample to a greedy rule that looks almost identical.
Practice prompt ↗Practice prompt ↗04Recursion, memoisation, and the step to a table
- Take one problem with overlapping subproblems, such as edit distance or coin change, instrument the plain recursion with a call counter to show the blow-up, then add memoisation and re-count.
- Convert the memoised version to a bottom-up table and state the two properties you relied on: each subproblem's result depends only on its arguments, and the dependencies form a DAG you can enumerate in order.
- Rewrite one deep recursion with an explicit stack, then find the input length at which the original hits the interpreter's frame limit, which defaults to about a thousand frames in CPython, so you know when the rewrite is required rather than decorative.
Deliverable: One problem in three forms, naive, memoised and tabulated, with call counts for each and the input length at which recursion depth becomes the binding constraint.
Practice prompt ↗Practice prompt ↗Worked solution ↗05Graphs, where most of the work is choosing the traversal
- Implement BFS and DFS over one adjacency list, then answer for each which finds a shortest path in an unweighted graph and which you would use to detect a cycle in a directed graph, including why the in-progress versus finished distinction matters for the second.
- Implement topological sort by in-degree, feed it a graph containing a cycle, and confirm the failure signature is that fewer than V nodes come out rather than an exception, then note that the order it produces is one of several valid ones.
- Run a shortest-path search on a graph with a single negative edge weight and show the wrong answer, then write the precondition Dijkstra actually needs, non-negative weights, because it finalises a node's distance the first time that node is popped, and name the algorithm you would switch to and its own limit.
Deliverable: A small graph library with BFS, DFS and topological sort, plus two inputs that produce documented wrong answers under the wrong algorithm choice.
Practice prompt ↗Practice prompt ↗06One day for everything that is not an algorithm
- Sketch one system only to the depth a coding-heavy loop tends to reach: the endpoints, what the service stores, and the single query pattern that decides the schema. Stop at twenty-five minutes.
- Prepare the project answer for an interviewer who codes, which means rehearsing the two levels they push to: the specific thing you built, and why you chose that approach over the alternative they will name. Open with a number and be ready to say what it excludes.
- Prepare the answer to what you would do differently, choosing a real technical mistake with a specific fix rather than a complaint about process or staffing.
Deliverable: One design sketch at endpoint-and-schema depth, plus a project answer rehearsed to two levels of follow-up.
Practice prompt ↗Practice prompt ↗07Solve out loud, under time
- Do three timed problems at twenty-five minutes each in a plain editor with no autocomplete and no execution until the end, then tally separately the failures that were syntax and the ones that were approach, because those two numbers call for different fixes.
- Narrate one solution from the first sentence, stating the approach and its complexity before writing any code, and rehearse the sentence you will use when you realise mid-solution that the approach is wrong.
- Re-solve from blank the two problems you were slowest on this week and compare the times against the day they first appeared.
Deliverable: A recording of one fully narrated solution and a tally that separates syntax failures from approach failures.
Practice prompt ↗Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
For anything that touched live traffic, be ready to say how you would have undone it: a flag, a staged rollout, dual writes with the old path still authoritative. Once the old column is dropped or the source rows are overwritten there is no reverse, so name what you kept a copy of and for how long.
Describe a time when you had a major technical disagreement with a tea…
Describe a time when you had a major technical disagreement with a teammate. How did you resolve it and maintain a positive working relationship?
Approach
- Name the disagreement and how you resolved it with evidence.
- Give the blast radius: what could have broken, and what you measured.
- State the situation in two sentences and spend the rest on the reasoning.
Follow-up
- What would you do differently if you ran that again?
- How did you know your change caused the improvement?
Estimate a catalogue re-packaging pass you have never run
A device class you still support accepts only one of the two common-encryption schemes, so part of the catalogue needs a second encrypted copy: roughly 200,000 content versions, each with 6-12 video renditions plus audio and subtitles. Nobody here has run a pass at this scale. You are asked for a date and a cost. Describe estimating work you had never done: how you decomposed it, what you measured before committing, the number you gave and the uncertainty attached to it, and how far off you turned out to be.
Approach
- The probe is whether you find the dominant unknown before producing a number. Here one fork is worth more than every other term: if the encoded elementary streams or the unencrypted packaged output were retained, this is a re-packaging pass - I/O-bound and cheap. If only encrypted output survived, it is a re-encode - CPU-bound at hours of machine time per hour of source. Settle it by querying what storage actually holds, not by asking what the pipeline is meant to retain.
- Decompose into units you can measure on a sample instead of terms you can argue about: assets in scope, renditions per asset, bytes per asset, machine-seconds per asset, publish and verification time per asset, and the steady-state storage and cache delta afterwards.
- Buy the information cheaply before committing. Run 50 assets end to end sampled across the size distribution rather than 50 short ones, recording wall clock, cost and failure rate. One day of calibration converts three of six terms from guesses into measurements, and that conversion is the whole technique.
- Give a range with its dominant term named and say what collapses it: six to ten weeks if this is a re-encode, about two if the elementary streams are retained, and you will know which within two days. A single date with no uncertainty is a worse answer than a range you can defend.
- Include the terms people forget because they land after the run: roughly double the storage for the affected catalogue, a second set of objects competing for the same cache footprint in every POP, and the manifest and key-mapping change that points each client at the right copy. The run finishing is not the project finishing.
- Say how far off you were, in which direction, and which term caused it. An estimate that missed on a term you never decomposed teaches more in the retelling than one that happened to land.
Follow-up
- Two days in, throughput is half your sample rate. What do you tell the person holding the date?
- Which titles go first, and on what basis?
- What happens to edge hit ratio for the affected catalogue once two encrypted copies are live?
Own the outage where a token entered the cache key
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
- 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.
- 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.
- 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.
- 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.
- 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.
- 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?
- 01
Describe a time when you had a major technical disagreement with a teammate. How did you resolve it and maintain a positive working relationship?
- 02
A device class you still support accepts only one of the two common-encryption schemes, so part of the catalogue needs a second encrypted copy: roughly 200,000 content versions, each with 6-12 video renditions plus audio and subtitles. Nobody here has run a pass at this scale. You are asked for a date and a cost. Describe estimating work you had never done: how you decomposed it, what you measured before committing, the number you gave and the uncertainty attached to it, and how far off you turned out to be.
- 03
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.
Is this an official Spotify interview guide?
No. It is PracHub's own research and practice material for the Software Engineer role at Spotify. Rounds and questions reflect what candidates have reported, not a process Spotify has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗How difficult is the Spotify Software Engineer interview?
The interview is generally considered to be of medium to high difficulty. While the coding questions are usually practical (LeetCode easy-to-medium) rather than highly theoretical, the system design and case study rounds are highly rigorous and require deep architectural and troubleshooting experience.
PracHub interview research ↗What is the "Case Study" round and how should I prepare?
This round simulates a live production outage. You should prepare by reviewing common distributed system failure modes, understanding how to read system metrics (CPU, memory, latency, error rates), and practicing structured troubleshooting methodologies. Focus on explaining your diagnostic steps out loud.
PracHub interview research ↗How heavily does Spotify weigh cultural fit?
Exceptionally heavily. Spotify will routinely reject highly skilled technical candidates who do not demonstrate alignment with their collaborative, feedback-driven, and empathetic culture. Treat the values interview with the same preparation and respect as the technical rounds.
PracHub interview research ↗What is the typical timeline from application to offer?
The process is thorough and can take anywhere from 4 weeks to 3 months. Recruiter response times can vary, so do not hesitate to politely follow up if you have not heard back within a week of a completed round.
PracHub interview research ↗Sources & methodology 3 sources ↗
Official role evidence, timestamped platform data and clearly labeled preparation advice.
- 01PracHub interview research ↗
PracHub editorial research into this company and role, maintained with this guide. Candidate-reported, not an employer publication.
platform · Accessed 2026-09-24 - 02PracHub Software Engineer practice ↗
Cross-company practice questions for this role.
platform · Accessed 2026-09-24 - 03PracHub interview preparation framework ↗
The framework the preparation plan follows.
platform · Accessed 2026-09-24