Articles
Coupon System Design Interview: Redemption Races, Budgets, and Abuse Controls
Prepare for a coupon system design interview with atomic redemption, budget ledgers, refund rules, abuse controls, and race-condition tests.
Coupon system design interviews look simple until two checkouts claim the final redemption, an order is canceled after the budget closes, or one person creates twenty accounts. The answer should begin with the money invariant, not a cache diagram. Define whether a campaign limits successful redemptions, reserved discounts, total currency value, or several of those at once.
Official evidence provides useful primitives. Stripe documents coupons and promotion codes with redemption limits, expiration, and customer-facing restrictions. PostgreSQL documents that SELECT ... FOR UPDATE locks selected rows against conflicting updates. The architecture below combines such primitives into an interview design; its transaction boundaries, data model, and abuse thresholds are editorial inference for the stated requirements.
For a closely related drill, use Design Inventory Availability and Order Placement.
Coupon System Interview: Quick Answer
Store an immutable campaign version, a redemption ledger, and a budget row. At checkout, create a reservation identified by checkout_id; in one transaction verify time, audience, per-account use, remaining count, and remaining monetary budget, then decrement available capacity. Finalization converts the reservation to redeemed. Expiry releases it according to a documented policy. A repeated request with the same idempotency key returns the first result.
Do not make Redis the financial source of truth. It may reject obvious losers quickly, but the durable ledger decides whether value was granted. Separate abuse risk from correctness: rate limits, device signals, and review queues can block or hold suspicious attempts, while atomic accounting prevents even honest concurrent requests from overspending.
The Core Contract
A coupon is a conditional financial authorization. The design is credible only when eligibility, a finite campaign budget, one-time redemption, cancellation, and investigation evidence share an explicit transaction boundary.
| State | Budget effect | Allowed next states | Evidence retained |
|---|---|---|---|
reserved | Hold count and maximum discount | redeemed, expired, canceled | checkout, rule version, amount cap |
redeemed | Consume actual discount | reversed, partially reversed | order and payment references |
expired | Release the hold | none | expiry job and original deadline |
reversed | Policy decides whether value returns | none | refund reason and approver |
The useful invariant is available + active_holds + consumed = campaign_limit for count budgets, with a parallel currency ledger for variable discounts. Never infer a refund by deleting a redemption row; append a compensating entry so reconciliation can reproduce history.
Model Eligibility and Money as Separate Decisions
Eligibility answers whether this principal may try: campaign window, market, SKU, minimum basket, customer segment, and per-account history. Pricing answers how much can be granted after stacking and rounding. Keep both under a numbered rule version. If a merchant edits the minimum basket while carts are open, the reservation records the version it evaluated so support can explain the result.
Compute the final discount server-side from authoritative cart lines. Never trust a client-supplied discount amount. State a rounding rule and cap percentage discounts. If stacking is allowed, define order: item discounts, order coupon, credits, tax, and shipping can produce different totals. An interview answer becomes stronger when it gives one numeric example and names the rule rather than saying “apply promotions.”
Close the Last-Redemption Race
Suppose a campaign has one redemption and $20 remaining. Requests A and B both read “one left.” A read-then-write flow may approve both. The linearization point must combine the predicate and mutation: a conditional update, serializable transaction, or locked budget row. Insert the unique (campaign_id, principal_id, checkout_id) ledger entry inside the same boundary.
Reservations need deadlines because checkout and payment are separate. A sweeper may release expired holds, but finalization must compare the reservation token and version so an old payment callback cannot revive released capacity. For popular campaigns, partition budgets into signed regional allocations and rebalance them; acknowledge that unused allocation lowers utilization until reconciliation. That trade-off is preferable to claiming global coordination is free.
Design Cancellation and Reconciliation Explicitly
Cancellation is a product rule, not the inverse of redemption. A canceled unpaid checkout can usually release its hold. A refunded completed order may return customer eligibility, campaign count, monetary budget, neither, or only a prorated amount. Record the decision as an append-only reversal with the original redemption ID.
Run reconciliation between order discounts, payment/refund records, and the coupon ledger through a shared watermark. Report missing ledger entries, amount differences, expired holds, and reversals without a source transaction. Correct with compensating entries rather than history edits. This gives finance an auditable answer and lets engineers replay the state after a consumer failure.
Layer Abuse Controls Without Hiding the Accounting
Threats include code enumeration, credential stuffing, account farming, self-referral, scripted high-rate holds, and insider configuration changes. Use high-entropy codes, uniform error messages, rate limits by account and network, verified-account requirements, device or payment-risk signals, and manual review for large value. Limit who may create or expand a campaign and audit every rule change.
Risk controls create false positives, so define hold, deny, and challenge outcomes. A review hold should reserve budget only for a bounded time. Track attempts per successful redemption, hold-expiry rate, unique principals per device or payment instrument, review reversal rate, and discount-to-order-value distribution. These are design metrics, not claimed universal thresholds.
Work Through a Failure Trace
At 12:00:00, a campaign has two redemptions and $30 remaining. A requests $20, B requests $15, and A retries after a timeout. A wins and records checkout-A; its retry returns the same reservation. B fails the currency predicate even though one count remains. Canceling A appends a $20 release only if the campaign policy permits it. The fixture must produce the same ledger after any delivery order.
Coupon System check: What must be checked atomically when two checkouts redeem the last coupon? Walk the trace once in event-time order, once with retries and reordering, and once after rebuilding projections from durable records. State the expected invariant after every step. If the outcome depends on message arrival order when the contract says it should not, the design is missing an identity, version, or transaction boundary. If the design intentionally permits a temporary difference, give it a watermark, maximum age, and repair owner.
Coupon System check: How do per-customer limits interact with a campaign-wide budget? This trace is an interview model, not a measured production benchmark. Add a skew test concentrated on one tenant or key, a two-times-peak capacity test, and a fault injection at the acknowledgment boundary. Preserve input versions and random seeds so a failed run can be reduced and replayed.
Turn the Requirements into Verification
Write these five questions beside the diagram: What must be checked atomically when two checkouts redeem the last coupon? How do per-customer limits interact with a campaign-wide budget? Does cancellation return capacity, money, both, or neither? Which controls stop guessing, account farming, and merchant misuse? How can finance reproduce the discount total after retries and refunds? Each question must point to a record, transition, test, and observable signal. If one can be answered only with 'the service handles it,' the contract is still incomplete.
Coupon System check: Does cancellation return capacity, money, both, or neither? Build a deterministic fixture with five to ten timestamped inputs. Include an identical retry, a conflicting reuse of an ID, a late or stale input, a worker failure after durable work but before acknowledgment, and a rebuild of the serving projection. Expected results must be written independently of the implementation. Verify the authoritative state, the serving state through a named watermark, and every emitted correction or rejection reason.
Coupon System check: Which controls stop guessing, account farming, and merchant misuse? Then add property checks. Applied logical IDs remain unique; versions never move backward; reserved or consumed value obeys the topic's invariant; a rebuild through the same input boundary produces the same digest; and an unauthorized reader cannot cross its scope. Generate reordered deliveries around the boundary instead of testing only the happy sequence. A passing fixture proves these declared cases, not production throughput or every network failure.
Coupon System check: How can finance reproduce the discount total after retries and refunds? For fault testing, stop a worker immediately before and after its commit, duplicate queue delivery, delay one partition, and make the read projection unavailable. The runbook should say whether intake pauses, work accumulates durably, readers serve a last-known-good generation, or the request fails closed. Record the recovery point and compare the rebuilt digest before switching traffic. This is concrete evidence that the recovery story matches the write contract.
Capacity, Operations, and Recovery
Coupon System check: What must be checked atomically when two checkouts redeem the last coupon? Estimate the durable write rate, average and peak payload, partition skew, retention volume, read or query shape, and acceptable lag. Convert those assumptions into partitions, worker concurrency, storage tiers, and backpressure. Numbers are sizing inputs, not universal facts. Say which limit fails first and what the user sees when it does.
Coupon System check: How do per-customer limits interact with a campaign-wide budget? Operate from contract metrics: accepted-versus-applied counts, duplicate/conflict rate, oldest pending work, authoritative-versus-serving divergence, retry exhaustion, and p95/p99 latency. Alert on sustained error-budget consumption and correctness gaps rather than raw traffic. Keep a runbook that can pause intake or rollout, drain work, rebuild a named projection through watermark W, verify a checksum, and switch readers to the new generation.
Coupon System check: Does cancellation return capacity, money, both, or neither? Use least privilege between producers, workers, control plane, data plane, and investigators. Encrypt transit and storage, rotate credentials, audit high-risk actions, and avoid placing secrets or unnecessary personal data in operational logs. Security controls support the data contract; they do not replace idempotency or verification.
Misleading Answers and Better Follow-Ups
Coupon System check: Which controls stop guessing, account farming, and merchant misuse? A weak answer names Kafka, Redis, and a database before defining truth. Another says “exactly once” without identifying the idempotency key, persistence boundary, or replay behavior. A third adds a cache but cannot explain invalidation or rebuild. Replace each slogan with one invariant, one linearization point, and one counterexample.
Coupon System check: How can finance reproduce the discount total after retries and refunds? Ask the interviewer to change a requirement: permit temporary staleness, add multiple regions, require a correction after finalization, or remove a dependency. Explain which state transition, key, and monitor changes. That is more useful than redrawing the same boxes at a larger scale.
Coupon System check: What must be checked atomically when two checkouts redeem the last coupon? Separate three evidence classes in the answer. Official documentation supports the cited product or protocol behavior. The proposed architecture, thresholds, and sequencing are inference from the stated requirements. This prevents a sensible design choice from being presented as an external fact.
A 45-Minute Interview Plan
Spend the first five minutes clarifying actors, scale, correctness, retention, and failure expectations. Use the next eight to define the authoritative records, IDs, versions, and invariants. Draw the write path and its acknowledgment boundary for ten minutes, then the read or serving path for five.
Use ten minutes for the title-specific failure trace, including one retry, one stale or late event, and one recovery. Reserve the final seven minutes for sizing, skew, access control, monitors, and trade-offs. Keep a short list of assumptions visible. When the interviewer changes one, update the affected contract before changing components.
End with the source of truth, the permitted temporary inconsistency, and the repair procedure. A compact design that can be replayed and verified is stronger than a large architecture with undefined states.
Final Takeaway
For a coupon system design interview, lead with this thesis: A coupon is a conditional financial authorization. The design is credible only when eligibility, a finite campaign budget, one-time redemption, cancellation, and investigation evidence share an explicit transaction boundary. Define the evidence boundary, run a concrete race or failure trace, and show how the system detects and repairs divergence.
Sources and Further Reading
- Stripe coupon and promotion code documentation
- PostgreSQL explicit locking documentation
- OWASP Automated Threats to Web Applications
Official sources support only the cited mechanism. Architecture recommendations, capacity figures, and interview sequencing are editorial inference.