TSTier SlateAll templates

Marketplaces & booking

Coupon codes - redeemable once, by anyone, at the same moment

Why a single-use code is a race between every checkout holding it, and how a conditional write settles it.

  • System design
  • starter
  • 8 step walkthrough
  • 4 tables

Share

  • Facebook
  • X

What this board gets wrong on purpose

The tension

The conditional write settles the race by serialising it: every checkout using a public 10,000-use code updates the SAME row, and at about 3 ms of row lock per transaction that row tops out near 330 redemptions a second while 450 a second are queueing for it in the first minute of a launch - so the correct design is also the slow one exactly when it matters. The fifteen minute hold makes it worse in a different way: abandoned carts keep their reservations, so a code can read "fully redeemed" while 2,000 of its uses are holds that will be released, and shoppers are told it is gone and then watch it come back. The sweeper that releases holds must check the payment first, or a capture that succeeded while its commit write failed is released and resold. And "once per customer" is a unique index on customer_id, which a guest checkout with a fresh email address walks straight past.

Practise this as an interview

Take Coupon codes - redeemable once, by anyone, at the same moment as a timed system design interview on a blank board - on your own, with the requirements to check yourself against, or with a candidate you invite.

Practise this as an interview

Start here

Why this design

Coupon codes are the friendliest version of a hard problem: a limited resource claimed concurrently by strangers. The same shape appears in ticket inventory, limited-edition drops and free trial slots. Interviewers like it because the bug in the obvious answer - read the count, check it, write count plus one - is easy to explain and easy to miss, and because the right answer (a conditional write) immediately raises the next two questions: what happens to the count while payment is pending, and what happens when forty thousand people want the same row at once.

What you will learn

  • Why "is this code valid" is a cacheable read and "is there one left" is a serialised write, and why they are separate boxes.
  • The conditional UPDATE that settles a redemption race in one statement, and why an application-level lock is worse.
  • Reserve, commit, release: holding a redemption across a payment, and the three ways that count drifts.
  • Why one public code is one hot row, and two ways out - striped counters and per-customer unique codes.
  • Code generation: alphabets without look-alike characters, and why codes must not be guessable from each other.

How to read this board

  1. Press Play and follow the walkthrough from the SHOPPER along the top.
  2. READ SIDE hangs down from the coupon service; THE RACE is on the right.
  3. PAYMENT sits under the race, because a redemption only becomes real after capture.
  4. ISSUANCE at the bottom left, with the cloud every code eventually leaks to.
  5. 1 · Requirements to 6 · Deployment in the written band, then the DATA MODEL.
  6. Then argue with the tension: the correct write is a hot row, and the fair hold is a count that lies.

1 · Requirements

Ask these before drawing anything

  • Is the code shared (SAVE20, used by anyone up to a cap) or unique (one code per recipient, used once)? These are different systems. A shared code is one hot counter. Unique codes are millions of rows with no contention at all.
  • Is the limit global, per customer, or both? "Once per customer" needs a notion of customer that guest checkout does not have.
  • Does an unpaid checkout use up a redemption? If yes, abandoned carts burn the campaign. If no, two shoppers can both be shown the discount and only one can pay with it.
  • Can codes stack? Stacking turns validation into an ordering problem (percent off before or after fixed off) and should be answered "no" unless somebody insists.
  • Is going over the cap a disaster or an accounting note? Ten extra uses of a 20 percent code is a rounding error. Ten extra uses of a free-iPhone code is a press story.

Functional

  • Apply a code to a cart: return the discount or a specific reason (expired, minimum spend not met, already used, none left).
  • Reserve one redemption when the shopper starts payment; commit it when payment is captured; release it if payment fails or the hold expires.
  • Enforce a global cap and an optional per-customer cap, exactly - never over.
  • Marketing can create a campaign with a shared code, or bulk-generate up to 10 million unique codes.
  • Disable a leaked code immediately without a deploy.

Non-functional

  • Apply (validation only) p99 under 30 ms; it runs on every cart change.
  • Reserve p99 under 100 ms in normal traffic. Under a hot-code launch, bounded queueing rather than timeouts.
  • Zero over-redemption. This is the one property that is not negotiable.
  • Holds released within one minute of expiry.
  • Unique codes not enumerable: knowing one code tells you nothing about another.

Out of scope

  • Pricing and tax. The coupon service returns a discount; the order service computes the total.
  • Gift cards and store credit. Those are money, with a ledger; coupons are a promise.

The rest of this board

Board preview

Available on Tier Slate

This page publishes the question. The answer — 5 more written pages, an 8-step narrated walkthrough and a 4-table schema — is the board itself, and it opens in Tierslate.

  • 5written pages
  • 8walkthrough steps
  • 4tables33 columns

5 pages behind this one

  • Capacity estimation
  • Storage estimation
  • Availability
  • How it works
  • Deployment plan
Open this board in TierslateBrowse every board

More boards

Two-sided systems where inventory must not be sold twice.

  • Checkout - holding stock you have not sold yet
  • Auction bidding - the clock is part of the system
  • Food delivery - three parties and a promise
  • Hotel booking - selling a range, on purpose, slightly twice
  • Seat sales - fifty thousand people, one hundred seats
  • Rental search - availability is a calendar, not a column
  • Job board - search, apply, and the recruiter's inbox
All 100 templatesOpen Tierslate
Tierslate

tierslate.com

HomeTemplatesPrivacyTerms