Interview guides · Coupang
Coupang Software Engineer Interview Guide
Coupang Software Engineer interview prep: reported coding, system design, concurrency and behavioral questions, the six rounds, and a 7-day plan.
Interviewing at Coupang
- Practice bank: 10+ questions
- Rounds: 2
- Typical prep: 1–2 weeks
- Interview reports: 2
Interview rounds
| Round | Questions |
|---|---|
| Technical Screen | 2 |
| Onsite | 8 |
How hard is the Coupang Software Engineer interview?
| Difficulty | Share | Questions |
|---|---|---|
| Easy | 10% | 1 |
| Medium | 90% | 9 |
| Hard | 0% | 0 |
What Coupang actually tests for
| Topic | Share | Questions |
|---|---|---|
| Coding & Algorithms | 40% | 4 |
| System Design | 30% | 3 |
| Behavioral & Leadership | 20% | 2 |
| Software Engineering Fundamentals | 10% | 1 |
The 60-second brief
Coupang is an e-commerce company. The source notes for this role name Rocket Delivery, Coupang Eats and fintech services among its businesses, and list engineering hubs in Seoul, Seattle, Mountain View and Bangalore. Software engineers there are described as working on high-throughput traffic, logistics and supply-chain automation, inventory systems, gateway routing and recommendation systems, built as microservices in languages such as Java, C++, Go and Python.
Coupang candidates report 6 rounds · ≈ 4-6 weeks. The stages below are what candidates describe, not a published process.
What a Software Engineer does at Coupang
For interview preparation, that description points to three areas. Reported coding questions cover arrays and intervals, the K-th largest element, recursive and iterative tree traversals, graph traversal with dynamic programming, and string manipulation under constraints. Reported design questions include a distributed job scheduler for delayed tasks with retries, a search system for real-time inventory queries, a marketplace API and storage schema, a large-scale file processing and streaming service, and the trade-off between SQL and NoSQL storage for transaction-heavy fulfillment. A third group covers concurrency and fundamentals: multithreading primitives, a thread-safe in-memory data structure, object-oriented modeling of an order pipeline, memory management and garbage collection, and database indexing and isolation levels.
Behavioral questions in the source ask about driving consensus under ambiguity, trading shipping speed against technical debt, running a post-mortem after a production failure, and disagreeing with a senior lead's technical decision. The source also notes that candidates for senior or staff roles should expect more system design relative to coding. Treat the round list as what candidates report rather than a published process, and confirm the format for your team with your recruiter.
The Coupang interview process
Coupang candidates report 6 rounds · ≈ 4-6 weeks. The stages below are what candidates describe, not a published process.
HR Phone Screen
Half 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 it evaluates
- 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
Online Coding Assessment
Input bounds are the part of the prompt most often skimmed, and they usually contain the answer. They tell you which complexity class is admissible, which narrows the search before you have thought about the problem itself. As a rough planning figure, a compiled language does on the order of 10^8 simple operations per second and an interpreted one roughly an order of magnitude less. So n up to about twenty admits enumerating subsets, a few thousand admits a quadratic pass, and a million admits neither: you need near-linear, or linear with a log factor. If the bounds are missing, ask for them.
What it evaluates
- Whether the approach is justified by the stated input size rather than by whichever pattern you recognised first
- Whether you ask about the properties that change the algorithm: whether the input arrives sorted, whether duplicates occur, whether values are bounded integers, whether it all fits in memory
- Whether you can name the bottleneck in your own solution and what would remove it, even when you deliberately leave it in place
- Whether a claimed speedup is real, since memoising a recursion only helps when subproblems genuinely overlap and the state can be keyed cheaply
How to prepare
- For each algorithm you rely on, write down the largest n it handles in roughly a second, then check two of those figures by timing them in the language you will actually type in
- For two weeks, write one line naming your target complexity and the bound that justifies it before you write any code, then compare that line with what you ended up submitting
- Practise the conversion backwards: given a required O(n log n), list the mechanisms that get you there (sorting, a heap, an ordered map, divide and conquer) and choose by what the problem needs to query, not by what you used last
Onsite Interview Loop
Candidates describe the onsite (or virtual onsite) as four to five sessions that mix live coding, system design covering both low-level and high-level design, and behavioral conversations with hiring managers and cross-functional leads. The coding topics reported for this role cover arrays and intervals, trees and graphs, dynamic programming and strings, with less common structures such as tries, segment trees and custom priority queues. Each session calls for a different approach. In coding, finish a correct solution and test it. In design, agree on requirements before drawing anything. In behavioral, give specific decisions and your own part in them. Before each new session, remind yourself which of these it is.
What it evaluates
- Whether coding answers reach working code with a stated time and space complexity and are checked against empty, single-element and boundary inputs, the edge-case coverage the source emphasises
- Whether you state your approach and its cost before typing, and adjust it when a follow-up changes the constraints
- Whether you explain memoization in a dynamic programming answer by naming the state, the recurrence and the table size rather than only adding a cache
- Whether your habits of restating the problem, asking for constraints and stating the plan before typing still show up in the later sessions
How to prepare
- Practise inorder, preorder and postorder traversal both recursively and with an explicit stack from an empty file, since a reported coding question asks for both versions without library helpers, and say which one is safer when the tree is deep and unbalanced
- Work the reported interval coding question: sort by start, merge overlaps in one pass, then report the gaps between consecutive merged intervals, and state that sorting makes it O(n log n)
- For every dynamic programming problem you practise, write the state definition and recurrence as a comment before writing code
- Run two or three mock sessions of different types in one sitting so that switching between them becomes routine
System Design Evaluation
Candidates report that this stage covers both low-level design and high-level architecture. For the high-level side, the source's system design material lists load-balanced microservices, gateway routing, sharding, caching and message queues. Reported design prompts include a distributed job scheduler for delayed tasks with retries, a search system over real-time inventory, a marketplace API and data schema, a file processing and streaming service with bandwidth constraints, and SQL versus NoSQL for transaction-heavy fulfillment. Scenario examples add a global rate limiter, inventory that must not oversell during flash sales, and a push notification service. For the low-level side, prepare with a reported low-level design question: a memory cache module with configurable LRU or LFU eviction. In each case, fix requirements and scale first, then choose storage and consistency, and say where you accept staleness.
What it evaluates
- Whether you pin down read and write paths, expected load and what must never be wrong (for inventory, selling stock that does not exist) before choosing components
- Whether storage choices come with a stated trade-off, for example relational transactions for order and stock updates against a document or wide-column store for high-volume reads, rather than a preference
- Whether a low-level design has clear class responsibilities and interfaces, and whether shared state inside it is safe under concurrent access
- Whether you describe failure behavior: retries and idempotency for a job scheduler, back-pressure in a streaming service, degraded modes when a dependency is down
How to prepare
- Design the flash-sale inventory case end to end, and write the conditional decrement that prevents overselling (update stock set available = available - n where sku = ? and available >= n) along with where it has to run
- Sketch the delayed job scheduler: how tasks are stored by due time, how workers claim them without double execution, and how retries and dead tasks are handled
- Work the reported low-level design question by implementing an LRU cache with a hash map and a doubly linked list for O(1) get and put, then describe what would change for LFU
- Write one paragraph each on sharding key choice, cache invalidation and queue delivery semantics (at-least-once versus at-most-once) so you can explain them without notes
Behavioral Discussions
The source describes behavioral discussions with hiring managers and cross-functional leads covering your past experience, decision-making, communication and handling of critical production issues, framed around leadership principles. The reported prompts ask about ambiguity and building consensus across teams, shipping speed against technical debt, a production failure and its post-mortem, and disagreeing with a senior lead. The source's own FAQ recommends the STAR structure with quantified results and clear personal technical ownership. The weak answers here are usually vague, not wrong: team-level narration, no numbers, and no statement of what you personally decided.
What it evaluates
- Whether each story makes clear what you personally did, decided or changed, separate from what the team did
- Whether results are quantified (latency, error rate, incidents, customer impact) and you can say where each number came from
- Whether a production-failure story covers detection, mitigation, root cause and the follow-up change that stopped it recurring
- Whether a disagreement story ends in a resolution based on evidence, such as a benchmark, a prototype or a written comparison, rather than on who was more senior
How to prepare
- Prepare one story for each reported prompt: ambiguity and consensus, speed versus technical debt, a production failure with a post-mortem, and a disagreement with a senior lead
- For each story, write the situation in two sentences, then list your actions in the first person and one measurable result
- Rehearse the follow-ups that usually come next: what you would do differently, what you chose not to do, and how you knew the fix worked
- Prepare a proudest-project walk-through that can go several levels deep technically, since the practice bank lists that prompt for this role
Final Round/Bar Raiser
Candidates report that the final stage in many regions includes a Bar Raiser round run by an independent senior leader from outside the direct team. The source advises reviewing leadership principles before the managerial and Bar Raiser rounds. Since the interviewer is not on the hiring team, prepare stories that make sense without team context, and practise keeping them specific when someone keeps asking why.
What it evaluates
- Whether the largest work you claim ran end to end: the decision, the rollout, monitoring, and what you did when something broke
- Whether you can explain a technical decision clearly to a senior interviewer from outside the hiring team, without team-specific jargon
- Whether your stories stay consistent and specific under repeated follow-up questions
- Whether you can name a decision that went wrong and what you changed afterwards
How to prepare
- Choose your two strongest ownership stories and practise them with a partner who asks why at least three times per answer
- Write down one decision that failed, what you knew when you made it, and the concrete change you made afterwards
- Map each of your stories to the themes the source lists for behavioral rounds: customer impact, ownership and bias for action, deep technical debugging, and cross-team dependencies
- Remove internal acronyms and team jargon from every story so an outside interviewer can follow it
Questions Coupang candidates report
Data structures and algorithms
State the bound, then read your own code against it, because the cost you forgot is usually inside a call you did not write. A membership test against a list is linear while the same test against a set is expected constant, so one of those inside a loop turns a linear scan quadratic. Taking a slice copies the elements wherever slices are values rather than views. Repeated string concatenation is quadratic wherever strings are immutable, unless you accumulate into a builder or a list and join once. None of that is visible in the shape of the algorithm, and all of it is visible in the runtime.
- Perform binary tree traversals using both recursive and iterative implementations without relying on built-in library helpers.
- Find the $K$-th largest element in an unsorted array using the most efficient time complexity approach.
- Solve graph traversal and dynamic programming problems, ensuring clear explanation of memoization strategies.
- Process string manipulation challenges under strict constraints, verifying edge cases and memory footprints.
System design and architecture
A design is a set of promises about what happens when something is slow, down, or delivered twice, and that is the part usually left unsaid. For each dependency, say what the system does when it is unavailable: serve stale data, reject the write, or accept it and settle later. For each relaxation of consistency, describe the anomaly a user would actually notice, in the product's own words, rather than naming a consistency model and moving on. The separator is volunteering the failure behaviour as part of the design instead of producing it only when pushed.
- Architectural design of a high-throughput search system capable of processing real-time inventory queries.
- Outline the architecture for a large-scale file processing and streaming service with strict bandwidth constraints.
Languages, concurrency and fundamentals
Most of these questions have a definition-shaped answer and a mechanism-shaped one, and only the second scores. Take them down to what exists in memory: which objects are allocated, which names hold a reference to them, and what changes when a call returns. That reframing answers most of the family, because copying, mutation, lifetime and equality are questions about the object graph. What separates a strong answer is refusing to stop at the keyword. Naming Java pass-by-value is a fact; the answer is saying that the value copied is the reference, so reassigning a parameter is invisible to the caller while mutating the object it points at is not.
- Discuss memory management, garbage collection overhead, and CPU cache performance in high-scale systems.
- Demonstrate object-oriented modeling and design patterns for a real-time order processing pipeline.
Behavioural and engineering judgement
A reversal is the easiest of these to tell badly, because the honest version sounds like a mistake and the flattering version sounds like nothing happened. Give the trigger: the specific observation that made continuing worse than stopping, dated to when it arrived rather than when it became undeniable. Then give what stopping cost, whether that was work discarded, a migration left half-finished, or a team told the dependency they had built against was moving. The answer that stands out names a stopping condition set before it was needed, or says plainly that none had been set.
- How do you handle trade-offs between shipping code quickly and maintaining long-term technical debt?
Practice exercises with worked solutions
The prompts above tell you what gets asked.
Find the join that inflates every invoice total
invoice_line_item holds line_id, invoice_id, tenant_id, sku, rate_tier, quantity, unit_price_micros, amount_minor (bigint), currency, kind, voided_at. invoice_payment_attempt holds attempt_id, invoice_id, tenant_id, amount_minor, status (succeeded, failed, pending), created_at, and an invoice has many attempts. A finance report runs select i.invoice_id, sum(l.amount_minor), count(p.attempt_id) from invoice i join invoice_line_item l using (invoice_id) join invoice_payment_attempt p using (invoice_id) group by 1 and the totals are wrong. Say precisely what the sum now equals, and write a version that is also correct for invoices with zero attempts.
How a strong candidate works it
- Compute what the query actually returns before fixing it. The two joins form a Cartesian product per invoice, so each line row repeats once per attempt row:
sum(l.amount_minor)is the true total multiplied by the attempt count, andcount(p.attempt_id)is attempts times lines. Three lines and two attempts report double the money and six attempts. - Reject the reflex repair.
count(distinct p.attempt_id)does fix the count, because attempt_id is unique.sum(distinct l.amount_minor)does not fix the sum, because two legitimate lines with equal amounts collapse into one. DISTINCT inside an aggregate deduplicates values, not rows, and the difference stays invisible until two lines happen to match. - Aggregate each branch to invoice grain before joining: one CTE summing lines by invoice_id, one counting attempts by invoice_id, then join the two results. A LATERAL subquery per invoice is equivalent and sometimes plans better when the outer set is small. Either way every aggregate stays at the grain it was defined at.
- Keep invoices with no attempts by making the attempt branch a LEFT JOIN with
coalesce(attempt_count, 0). An inner join here silently drops every unpaid invoice, which is usually the exact population finance is asking about. - Push each filter to its own grain:
where l.voided_at is nullbelongs inside the line CTE, not the outer query, or it would also filter the attempt branch through the join. Put the tenant predicate on both branches, since the denormalised tenant_id is what stops a wrong join crossing tenants. - Leave yourself a standing check: an invoice total is a function of its non-voided lines and of nothing about payments, so if changing the payment filter moves the money figure, the fan-out is back.
Expected result (25 min): The original sum equals the true line total multiplied by the number of payment attempts and the count equals attempts times lines; the corrected query pre-aggregates each branch to invoice grain in separate CTEs and LEFT JOINs them with coalesce, with voided lines excluded inside the line CTE and the tenant predicate applied to both branches.
Check yourself
- The money total is unchanged by adding or removing payment attempts.
- An invoice with zero attempts appears with its total and a zero count.
- Two lines with identical amounts both contribute to the total.
- Voiding a line changes the money figure and leaves the attempt count alone.
Follow-ups
- Add a third branch for credit notes applied to the invoice. Does the CTE shape still hold, and when would a single pass with
filter (where ...)be better? - Over 500k invoices this report takes minutes. Which grain would you materialise, and how do you keep it correct when a line is voided?
- The same report is needed per tenant per month. What index makes the line CTE cheap?
Decide which facts an invoice line copies instead of joining
invoice_line_item already denormalises tenant_id, which is reachable through invoice_id, and stores amount_minor even though quantity times unit_price_micros would recompute it. A reviewer asks you to normalise both away, and separately asks whether the tenant's legal name and billing address should be copied onto the invoice header. Decide each case. For every field you keep denormalised, name the read pattern or the invariant that justifies it, the anomaly the copy can develop, and the mechanism that prevents that anomaly here.
How a strong candidate works it
- Split the question into two kinds of copy, because they fail differently. A copy of a currently mutable fact is a cache: it drifts and needs invalidation. A copy of a fact frozen at write time is not a cache at all, it is the record of what happened, and normalising it away destroys information the source no longer holds.
- Keep tenant_id on the line. It costs 8 bytes, it leads every index on the table so no read is ever accidentally cross-tenant, and it turns a wrong join into an empty result rather than another tenant's money. Prevent the drift structurally: a unique constraint on invoice (invoice_id, tenant_id) plus a composite foreign key from the line on (invoice_id, tenant_id) makes a mismatched pair impossible, so the database enforces agreement instead of a code review.
- Keep amount_minor. Rounding must happen exactly once, at a named site, with a stated mode (half-even here). If readers recompute from quantity and unit_price_micros, every reader owns a rounding decision, and half-up and half-even diverge systematically across thousands of lines rather than cancelling out. A check constraint can bound the stored value but deliberately cannot re-derive it.
- Copy the legal name and billing address onto the invoice header, written once and never updated. The statement must show what was true when it was sealed, and the tenant record will change afterwards. This is a snapshot for the same reason
source_rollup_watermarkis stored per line: without it, nobody can reconstruct what the customer was told. - Name the read pattern that pays for all of it. Rendering, dispute response and export are per-tenant, per-period reads over thousands of lines that would otherwise join back to slowly changing dimensions that no longer hold the historical value. The write side is a once-per-period batch, so the extra columns cost nothing that matters.
- Concede the case where the reviewer is right: a mutable operational attribute such as the tenant's current plan name has no business on a line. If a report wants it, join. If a statement needs the plan as of the period, that is another snapshot and it belongs on the header with the rest.
Follow-ups
- Write the composite foreign key and the unique constraint it requires on the parent. What does it cost on every line insert, and what does it do to a bulk load?
- A tenant is renamed after being invoiced. Which rows change, and what does the customer see on last quarter's PDF?
- Where does currency live, and what breaks if a tenant's billing currency changes between two periods?
Parse and verify a timestamped multi-signature webhook header
An inbound webhook carries a signature header of at most 1 KiB shaped t=<unix seconds>,v1=<64 hex chars>, with up to five v1 values during secret rotation and possibly unknown scheme keys. You hold the raw request body bytes and the currently active signing secrets. Write the parser and the verifier: accept when any active secret reproduces a signature and the timestamp is within a five-minute tolerance in either direction, reject otherwise. Single left-to-right pass over the header, no regular expression. State what is inside the MAC and why.
How a strong candidate works it
- Parse in one scan: split on
,, then on the first=only, since a value may itself contain=under a future scheme. Accepttexactly once and treat a secondtas a reject rather than last-wins. Push everyv1onto a short list and ignore any other key, so av2can be introduced later without breaking this verifier. - Say what is signed: HMAC-SHA256 over the exact byte string
<t>.<raw body bytes>, yielding 32 bytes or 64 hex characters. The timestamp sits inside the MAC because otherwise an attacker replays yesterday's body with its still-valid signature and only has to edit the header timestamp. - Hash the bytes as received. Verifying against a re-serialised JSON body is the usual defect: key order, whitespace and number formatting all change the bytes while the parsed objects compare equal, so signatures fail for honest senders and the popular 'fix' is to stop checking.
- Compare in constant time over fixed-length digests. Decode the hex to 32 bytes, accumulate
acc |= a[i] ^ b[i]across the whole length, and testacc == 0at the end. Evaluate every candidate without an early exit; at five candidates that is five HMACs over the body, linear in body size and negligible beside the network. - Apply the tolerance as a two-sided bound, rejecting when
|now - t| > 300seconds. A sender whose clock runs ahead of yours is an ordinary case, and an unbounded future timestamp is a free replay window. - Complexity: O(L) over the header producing k candidates, plus k HMACs at O(|body|) each. Space is O(k) beyond the body itself. Do the cheap rejections, including the tolerance check, before any cryptography runs.
Expected result (15 min): The valid case accepts. Both out-of-tolerance cases reject, including the future one. The unknown v2 field is ignored and the v1 still verifies. The re-serialised body fails, which is correct and is exactly why the raw bytes must be retained.
Check yourself
- Timing the comparison over 10,000 mismatching signatures that differ at the first character versus the last shows no separation you can measure.
- Removing
tfrom the MAC input makes an old captured body with its original signature pass. Write that test, watch it pass, then puttback. - Every rejection except the signature mismatch itself returns before an HMAC is computed.
Follow-ups
- The body is 40 MB. What changes about where you verify, and what can you do before the whole body has arrived?
- A customer reports that signatures fail for exactly the requests whose body contains a non-ASCII character. What is your first hypothesis?
- How do you rotate the signing secret with no failed deliveries, and how long do both secrets stay live?
Sealing a billing period against late-arriving usage
billing seals a tenant's period once the metering watermark passes the period end, prices the sealed rollups against the plan (included allowance, tier boundaries, negotiated discount) and writes invoice_line_item rows: quantity numeric(28,6), unit_price_micros bigint, amount_minor bigint, currency char(3), source_rollup_watermark timestamptz. The job is re-run after failures and two workers may attempt the same tenant. Events legitimately arrive with occurred_at inside the period and ingested_at after it. Specify the seal transition, the idempotency key for every write including the payment-processor call, the rounding position and mode, and the fate of a post-seal event.
How a strong candidate works it
- Make sealing a conditional write rather than a read followed by a write: UPDATE usage_rollup_hourly SET status = 'sealed', sealed_at = now() WHERE tenant_id = $1 AND hour_start >= $2 AND hour_start < $3 AND status = 'open' RETURNING rollup_id. Two concurrent sealers cannot both win because the loser's predicate no longer matches, and the loser learns it lost from an empty result rather than from a lock timeout.
- Gate the seal on the watermark, not on the clock - the period closes when the fold is trustworthy past period_end - but cap the wait, because an unsealed period blocks the whole billing run. Seal anyway after a stated maximum (six hours past period end is a reasonable default) and let whatever arrives afterwards become an adjustment. The trade-off is named in one line: adjustments are cheap, a missed billing cycle is not.
- Make every write converge under re-run. Line items are idempotent on (invoice_id, sku, rate_tier) with ON CONFLICT DO UPDATE permitted only while the invoice is draft; the payment-processor call carries (tenant_id, billing_period_start) as its idempotency key, because a timeout there is an unknown outcome, not a failed one, and a plain retry is how a customer gets charged twice.
- Keep the arithmetic exact and round exactly once. quantity stays numeric, the rate is an integer unit_price_micros in millionths of a minor unit because per-request prices are genuinely below a cent, and amount_minor = round_half_even(quantity x unit_price_micros / 1,000,000) is computed once per line and stored. Tiering walks the boundaries in order emitting one line per (sku, rate_tier) with tier 0 as the included allowance at price zero; the invoice total is the sum of stored amount_minor values and is never recomputed downstream from quantity and rate.
- Handle the post-seal event as a forward-only correction. The sealed rollup is frozen, so the difference between the sealed value and the restated value becomes a new invoice_line_item with kind = 'adjustment' and voided_by_line_id pointing at the line it reverses, carrying its own source_rollup_watermark. Nothing is edited in place, because the original line is the only evidence of what the customer was actually charged and is precisely what a dispute, a refund or an audit asks to see.
Expected result (35 min): Seal as a conditional UPDATE from status = 'open' gated on the metering watermark with a bounded maximum wait; line writes idempotent on (invoice_id, sku, rate_tier) and the processor call idempotent on (tenant_id, billing_period_start); amount_minor rounded once per line with half-even; late data becoming an adjustment line that points at the line it reverses and records its own watermark, with no in-place edit anywhere.
Check yourself
- Re-running the entire billing job on an already-invoiced period must produce identical line rows and must not call the payment processor a second time. If it charges again, the idempotency key was on the retry rather than on the business fact.
- Over four hundred lines with uniformly distributed fractional parts the two totals should differ by a handful of minor units - standard deviation around sqrt(400/12), about 5.8, with a worst case of 200. If your two totals match exactly, the test data has no fractional parts and has not exercised the trap.
- Every line carries a source_rollup_watermark, and two lines for the same SKU with different watermarks should explain their difference without opening any other table.
Follow-ups
- Two workers start the same tenant's seal a millisecond apart and the winner crashes after sealing but before writing any line. What does the second worker observe, and is the resulting invoice correct?
- Show the divergence between rounding each line and rounding the total once over four hundred lines, and say which direction it goes.
- A customer disputes a charge from two quarters ago. Which rows answer it, and which single design decision made that answer possible?
Metering partition crash-loops and the sealing watermark freezes
One metering-ingest partition has stopped advancing. Lag grows linearly, the consumer restarts about every 40 seconds, and the same offset appears in every startup log while other partitions stay healthy. Events are committed in batches of a few thousand and the acknowledgement follows the commit. Sealing is six hours away and source_max_ingested_at for that partition's tenants is frozen. Give an ordered checklist, a containment action available within minutes, and the durable fix, saying what each does to exactly-once accounting.
How a strong candidate works it
- Distinguish a poison record from a capacity problem in one measurement: compare the offset and the exception across restarts. An identical pair every time is deterministic failure on one record, whereas a throughput problem still advances the offset between crashes.
- Read the record from a separate consumer group so the bytes can be inspected without perturbing the stuck consumer, then classify the defect: schema violation, a quantity failing the non-negative check, a null workspace, an unmappable SKU enum, or a payload past a size limit. That classification decides whether this is a producer bug or a missing consumer guard.
- Account for batch granularity before acting. With commits of a few thousand, one bad record fails thousands of good ones, so the blast radius is the batch. Halve the batch around the offset to isolate the record, or move to per-record error isolation so the radius becomes the record.
- Contain by diverting that record to a dead-letter store with its raw bytes and offset, then resume. This is safe here precisely because the acknowledgement follows the commit: the good records from the failed batch are re-consumed and absorbed by the uniqueness check on (tenant_id, idempotency_key) rather than counted twice.
- Make the fix durable with per-record error isolation, a bounded poison counter, and an alert on dead-letter rate rather than on lag alone, since lag only reveals this after the sealing margin has already been eaten.
- Check the horizon before replaying anything. The unique index lives on a daily-partitioned table and therefore includes the partition key, so it deduplicates within a day only; a replay landing on a later ingest day needs the separate dedup store or it double-counts into a tenant's bill.
Follow-ups
- Move the acknowledgement before the commit and describe exactly what is lost and what is duplicated in each of the two crash windows.
- Sealing is in six hours and the partition will not drain in time. What do you seal on, and what does the invoice have to record so the difference is explainable later?
- A producer replays two weeks of events next month. Which part of your fix stops holding, and what is the dedup horizon you would actually configure?
Resolve a review disagreement over a quota check
A colleague's pull request enforces a per-tenant quota by selecting the current count and then inserting when it is under the limit. You flag it as a race. They reply that the transaction already runs at repeatable read, so the snapshot makes it safe, and the tests pass. Walk through taking that disagreement to a resolution: what you write in the review, what you demonstrate rather than assert, which fix you propose and why, and what you do if they still disagree after all of it.
How a strong candidate works it
- Answer the claim precisely instead of restating your objection, because they have made a specific technical argument. In PostgreSQL, repeatable read is snapshot isolation; this is write skew, which snapshot isolation permits by design. Both transactions read a count that is stable within their own snapshot, insert disjoint rows that the other cannot see, and both commit, so the limit is exceeded by exactly the concurrency.
- Demonstrate rather than cite. Two psql sessions, both BEGIN ISOLATION LEVEL REPEATABLE READ, both select the count, both insert, both commit: it succeeds. Repeat at SERIALIZABLE and the second commit fails with serialization_failure, SQLSTATE 40001. That takes two minutes, ends the argument without anyone conceding a position, and leaves an artefact for the next reviewer.
- Offer the options with their costs rather than a verdict. Serialisable plus a retry loop on 40001 is correct but obliges every caller to retry and degrades under contention. An increment-and-compare on a counter row — update tenant_quota set used = used + 1 where tenant_id = $1 and used < limit returning used — is safe even at read committed, because a blocked updater re-evaluates the WHERE clause against the row version it finally locks, and zero rows returned means full. A unique or exclusion constraint that makes the surplus write fail is the third.
- Name the plausible non-fix explicitly, since it is what usually gets merged instead: folding the count into the insert as insert ... select ... where (select count(*) ...) < limit is still racy under read committed, because the subquery cannot see the other transaction's uncommitted rows. It looks atomic and is not.
- Say what you do if they still disagree: escalate the decision rather than the disagreement. Attach the reproduction, hand it to the service owner or a third reviewer, and state that you will not block the merge if the owner accepts the risk knowingly — and that you want that acceptance written down.
- Close with the general lesson worth leaving in the review thread: a passing suite is weak evidence for a concurrency claim because it runs one request at a time. Ask for a test that runs two.
Follow-ups
- Write the counter-row version. Does your answer change if the quota counts child rows rather than a column?
- Under serialisable, who performs the retry, and what does the API client see if the retry also fails?
- This is the third disagreement with the same reviewer this month. What changes in how you review?
Ship metered billing with a named deduplication horizon
Metered billing must be on in three weeks. usage_event is partitioned daily, so its unique index must include the partition key and deduplicates only within a day: a producer retry that crosses midnight, or a replay run a week later, gets through. A cross-partition dedup store is two weeks you do not have. Describe shipping with debt you named in advance: what you shipped, what you wrote down, the detector you added, the trigger and date for paying it off, and what you would have refused to ship under the same pressure.
How a strong candidate works it
- Show you can separate the two kinds of debt, because that distinction is what the question actually probes. Debt that costs engineering time later is shippable on a deadline. Debt that silently corrupts a number a customer gets charged for is not shippable unless the corruption is detectable, and detectability is the whole negotiation.
- Make the exposure narrow and measured rather than gestural. The hole is duplicates whose occurrences straddle a UTC day boundary, plus any replay older than partition retention. Measure it before arguing about it: how often an idempotency_key recurs at all, and the distribution of the gap between first and last occurrence. If the ninety-ninth percentile of that gap is four minutes, the residual risk is a small band around midnight and you can say so numerically.
- Add the detector before the feature, not after. A nightly job counting keys that appear in more than one partition is one grouped scan over recent partitions, and it converts a silent overcount into a page. State what it costs to run and what it fires on.
- Buy the cheap half of the real fix immediately: extend partition retention so the dedup horizon exceeds the producer's maximum retry window plus the longest replay you intend to support. That reframes retention as a correctness parameter rather than a storage cost, which is the sentence you need on record before someone optimises the bill.
- Make repayment mechanical instead of aspirational: a dated entry with a named owner, plus a threshold that pulls the date forward — first detector hit above N events, or first customer dispute. Debt with a trigger gets paid; debt with only a date does not.
- Answer the second half honestly by naming what you would refuse under identical pressure: the sealing path, because a sealed row is frozen and a wrong number there stops being a bug and becomes an adjustment line, a dispute and an audit question.
Follow-ups
- The detector fires on forty duplicate events for one tenant, and two of their invoices have already sealed. What happens next?
- Whom did you tell that the billing numbers had a known hole, and in what words?
- Finance asks you to cut storage by shortening partition retention. What do you say, and to whom?
Where candidates lose points
Answering the reported K-th largest question by sorting the whole array when the prompt asks for the most efficient approach
Sorting is O(n log n) and ignores k. Give the alternatives with their costs: a min-heap of size k is O(n log k) time and O(k) space and works on a stream. Quickselect with a random pivot runs in expected O(n) time, but its worst case is still O(n^2); only median-of-medians pivot selection guarantees O(n) in the worst case. Name which one you would use and why before writing code, then test k = 1, k = n and arrays with duplicates.
Only being able to write tree traversals recursively when the reported question asks for iterative versions without library helpers
Practise all three orders with an explicit stack. For inorder, push left children until you reach null, pop and visit, then move to the right child. For postorder, use either two stacks or a last-visited pointer. Explain that recursion depth equals tree height, so a degenerate tree of n nodes can exceed a runtime's recursion limit, while the iterative version uses heap memory instead of call-stack frames.
Writing a thread-safe bounded queue that checks its condition with if instead of while, or uses one lock with no condition variables
The source lists a thread-safe bounded blocking queue as an example concurrency question. Guard the buffer with one mutex, use separate not-full and not-empty conditions, and re-check the predicate in a while loop after every wake-up, because wake-ups can be spurious and another consumer may have taken the item first. Signal not-empty after a put and not-full after a take. If you use a single condition variable, signal all waiters, not one. Then explain what could deadlock if a caller held another lock while blocked in put.
Designing the reported real-time inventory search or a flash-sale inventory system without saying where consistency is required and where staleness is acceptable
Separate the paths. Search results and stock badges can be served from an index or cache that is seconds behind. The reservation or checkout write cannot be, so make it a conditional decrement or a transactional reservation against the system of record, and make retries idempotent. State this split early, together with the SQL versus NoSQL trade-off for that write, because the design questions reported for this role ask for that reasoning directly.
Telling the reported post-mortem or disagreement stories at team level, with no numbers and no clear personal decision
The source's guidance for behavioral rounds is STAR with quantified results and direct technical ownership. For each story, name the decision you made, the evidence you used, one measured result, and the change you made afterwards. In a post-mortem story, cover detection, mitigation, root cause and prevention in that order. In a disagreement with a senior lead, show how you settled it with a benchmark or prototype rather than by giving in or escalating by default.
Evenings only, compressed around a full-time job
Roughly ninety minutes on weeknights with one longer weekend block. The plan cuts scope rather than compressing everything, on the assumption that one thing finished per night beats four half-started.
Day 1 — Screen logistics and a cold coding baseline
- Before the HR phone screen, write one line each for your earliest start date, notice period, location and work authorisation, and a compensation range based on current data for your level and location.
- Solve the reported K-th largest element question cold, then solve it again three ways: sort, a size-k min-heap, and quickselect, with the time and space cost of each written down.
- Solve Merge Two Sorted Arrays and Detect Cycle in Linked List from the practice bank, and before running each one, test it against empty, single-element and duplicate inputs.
- Write one sentence on what slowed you down most today; it decides where days 2 to 4 go.
Deliverable: A constraints sheet for the screen and three K-th largest solutions with their complexities stated.
Day 2 — Online assessment topics: arrays, intervals and strings
- Solve the reported interval question: sort by start, merge overlaps, then return the gaps between consecutive merged intervals, and handle touching intervals explicitly.
- Work two string problems under stated constraints, including the Longest Palindrome question from the practice bank, and note the memory each solution uses.
- For each problem, write the target complexity and the input bound that justifies it before writing any code.
- Work through the SQL worked exercise on join fan-out to practise tracing a wrong result back to its cause on a small input.
Deliverable: Three solved problems, each annotated with its complexity, the input bound behind it, and the edge cases tested.
Day 3 — Trees, graphs and dynamic programming
- Write inorder, preorder and postorder traversal both recursively and iteratively from an empty file with no references open.
- Solve Detect Cycles in Graphs for both directed (three-colour DFS) and undirected (parent tracking) graphs, then use it for a build-order problem with topological sort.
- Solve Find Shortest Distance Between Two Islands from the practice bank with DFS to mark one island and multi-source BFS to reach the other.
- For one dynamic programming problem, write the state, recurrence and table size before coding, then reduce the space usage if you can.
Deliverable: Six traversal implementations, a working topological sort with cycle detection, and one dynamic programming solution with its state written out.
Day 4 — Concurrency and low-level design
- Write a short answer to Thread vs Process covering memory sharing, isolation, context-switch cost and failure blast radius.
- Implement a bounded blocking queue with one mutex and two condition variables, checking each condition in a while loop, and explain which waiters each operation signals.
- Implement an LRU cache with O(1) get and put, then describe the changes needed for LFU and how you would make it safe for multiple threads.
- Model the reported real-time order processing pipeline as classes with clear responsibilities, and mark which state is shared between threads.
Deliverable: A working blocking queue, an LRU cache, and a one-page class model of the order pipeline with shared state marked.
Day 5 — System design from the reported prompts
- Design search over real-time inventory: separate the indexed read path from the stock write path and state how far behind the index is allowed to lag.
- Design the distributed delayed job scheduler, covering storage by due time, claiming tasks without running them twice, retries and dead tasks.
- Write a comparison of SQL and NoSQL for a transaction-heavy fulfillment write path and a high-volume catalog read path.
- Work through the billing-period sealing design exercise to practise idempotency keys and conditional writes.
Deliverable: Two design sketches with requirements, data model, consistency choices and failure handling, plus a written SQL versus NoSQL comparison.
Day 6 — Behavioral and Bar Raiser stories
- Write one STAR story for each reported prompt: ambiguity and consensus, speed versus technical debt, a production failure with its post-mortem, and a disagreement with a senior lead.
- Put your own decisions in the first person and one measured result in each story, and note where every number came from.
- Prepare a proudest-project walk-through that goes three levels deep technically, including what you would change now.
- Have a partner ask why three times on each story, and rewrite any part that falls apart.
Deliverable: Five stories, each with a clear personal decision, a quantified result, and answers to the likely follow-ups.
Day 7 — Full mock and taper
- Run one coding problem, one design prompt and one behavioral story back to back without a break, then write down where you lost the thread before hearing feedback.
- Do the webhook-parsing coding exercise as a timed warm-up to practise checking edge cases before declaring a solution finished.
- Review only your notes from this week, and open no new material.
- Confirm the logistics with your recruiter: the format of each round, the coding environment, and whether your language choice is open.
Deliverable: Mock notes naming the moments you lost the thread, plus a one-page card of your stories, design checklist and logistics.