DEV Community

Cover image for Design an online auction system: the system design interview walkthrough
Gowtham Potureddi
Gowtham Potureddi

Posted on

Design an online auction system: the system design interview walkthrough

Design an online auction system: the system design interview walkthrough

The request to design an online auction system sounds like one line — list an item, people bid, the highest wins — and that one line hides the whole interview. Picture it for a village hall and a gavel settles it; picture it for eBay, where thousands watch one item, a hundred people fire a bid in the same second, the price has to climb live on every screen, and at a hard deadline exactly one winner must be named and charged, and it becomes a problem where the same item pulls two opposite ways at once. This is one of the most revealing auction system design prompts — the eBay-style question — because the two things it asks for fight each other: every bid must be serialized against the current high so none is lost, while every accepted bid must broadcast to a crowd, and then the whole thing must close cleanly on a timer.

The round turns on one structural insight. "Highest bid wins" hides a system with opposite needs on the same item. On the write side, a bid is only valid if it beats the current high, so bids on one item must line up and be checked one at a time — a strongly consistent, serialized write, with no lost updates when a hundred people bid at once. On the read side, the moment a bid is accepted the new high and the "you were outbid" notice must reach thousands of watchers fast — a massive fan-out that can tolerate being a moment stale. And sitting over both is the close: at a hard deadline the system must pick exactly one winner from the record, do it safely even if the closing logic runs twice, and stop a last-second snipe from stealing a settled auction. Get that reframe — serialize the bid, broadcast the price, schedule the close — and the rest of the system design interview is deciding how each of those three is made correct. This walkthrough follows the path a strong candidate draws on the whiteboard — requirements, estimate, API and data model, high-level design, then the deep dives — naming the follow-up the interviewer uses at each step to tell a memorised answer from real understanding.

Title card for a system design interview walkthrough on designing an online auction system like eBay, showing the same item pulling two opposite ways — bids serializing one-at-a-time against a rising current high on one side, and that new high broadcasting out to a crowd of watcher screens on the other — with a countdown clock marking the hard close, on a dark citrus-and-lavender background.

How the online auction round is actually run

An online auction round runs about 45 minutes on a shared canvas — you draw the boxes and arrows while you talk, and the interviewer interrupts. It is a favourite because the one-line problem opens onto deep water fast: the moment you draw "place a bid, show the price," the question becomes how do you accept a hundred simultaneous bids without losing one and what happens to a bid that lands in the final second, and every answer after that compounds from those two. The clock is a touch tighter than the hour-long rounds, so you have to spend your minutes on the bid write, the broadcast and the close, not on the item catalogue.

The interviewer rarely lets "the highest bid wins" stand. "A thousand people are watching one item and a hundred fire a bid in the final second — who wins, how do you make sure exactly one person does, and what stops a bid at the very last millisecond from stealing it?" That cluster — serialized bids, a clean winner, and sniping — is what a copied answer can never rehearse, and what this round is really testing.

The rounds here are modelled on how the loop runs, not scraped from any candidate's recall. What matters for prep is the shape: a blank canvas, a tighter clock, and a follow-up after every decision you make.

The five-step framework for any HLD round

Every high-level design answer fits the same spine. Say it out loud at the start so the interviewer knows you have a plan, then walk it:

  1. Clarify requirements — functional and non-functional, and what you are explicitly not building.
  2. Estimate — watchers per hot item and bid rate per item versus total auctions, to justify every later choice.
  3. High-level design — the boxes and arrows: the clients, the bid path, the broadcast path, and the scheduled close.
  4. Deep dive — the two or three decisions that actually decide this system.
  5. Follow-ups — bottlenecks, failure modes, and what you would change at ten times the scale.

What the debrief measures

The score is not "right or wrong design." A good HLD debrief reads on four dimensions, and every move below maps to one: problem solving (did you reach the serialize-vs-broadcast split and treat the close as the correctness minefield), communication (did you say why bids must serialize but the price can broadcast lossily, in a single sentence), technical execution (an atomic compare-and-set on the current high over an append-only bid log, pub-sub plus websocket fan-out for the price, and a scheduled, idempotent, anti-sniping close), and time management (did you spend your 45 minutes on the bid, the broadcast and the close, not on the listing UI). Reading a walkthrough teaches the design; it does not teach you to deliver it on the clock while someone pushes back.

Step 1 — Requirements: what an online auction platform must do

Spend the first few minutes here. Jumping to boxes before you have scoped the problem is the fastest way to design the wrong system.

Type Requirement
Functional A seller lists an item with a start and a hard end time; buyers place bids that must beat the current high; everyone watching sees the live price climb; the previous leader gets an outbid notification; at the deadline the auction closes with exactly one winner, who pays.
Functional (optional) Proxy / automatic bidding up to a max, a hidden reserve price, sealed-bid or second-price formats, buy-it-now — call these out, then scope most of them for later.
Non-functional Bids and the final winner are strongly consistent — no lost bids, exactly one winner; the live price is real-time to thousands of watchers and can tolerate being a moment stale; high availability; scale to many concurrent auctions, some of them hot.

The sentence that earns points: "The same item wants two opposite things — bids must serialize against the current high so none is lost, while the price must broadcast to thousands — and the close has to name exactly one winner even if it runs twice." Say that and you have framed the entire design before drawing a box.

Step 2 — Capacity estimation (back of the envelope)

Estimation here is about proving one thing: the write path and the read path have opposite shapes, and the close is a third, time-driven concern.

Quantity Assumption Result
Watchers per hot item thousands to tens of thousands on a popular auction a large websocket fan-out per item — the read-side scaling problem
Bids per item bursts of many bids per second in the final seconds writes that must serialize on one item's current high — the consistency problem
Total auctions millions of independent auctions running at once shard by auction id; different items never contend, so the system scales horizontally
Close events one scheduled close per auction at its deadline time-triggered work — a scheduler firing per-auction, spiking as popular auctions end

Two conclusions fall out, and you should say both out loud. First, bids are the serialized, high-stakes axis — the write rate on any one item is modest even in a burst, so it can afford a strongly consistent, one-at-a-time check against the current high, which it needs so no bid is lost and no two "winners" are accepted. Second, the price feed is the fan-out, low-stakes axis — it must reach thousands of screens fast and a momentarily stale price is harmless, so it is a broadcast that must never be forced through the serialized write. You have now pointed the interview at the real deep dives — the bid write, the broadcast, and the close — before anyone asked.

Step 3 — API and data model

The API is deliberately small: a seller creates an auction, buyers bid and watch, and the close runs on a schedule.

Operation Shape
create_auction(item, start_time, end_time, reserve) lists an item with a hard end time and an optional reserve.
place_bid(auction_id, amount, idempotency_key) attempts a bid; accepted only if it beats the current high; the idempotency_key makes a retry safe.
get_auction(auction_id) returns the item, current high price, and time remaining.
subscribe(auction_id) opens a websocket to the live price / outbid feed for that auction.
(close) not a public call — a scheduled job fires at end_time.

The data model is thin. The current high and the append-only bid log are the source of truth; the live feed is derived:

Entity Notes
auction auction_id, item, seller_id, start_time, end_time, reserve, current_high, current_leader, state (open / closing / closed).
bid bid_id, auction_id, bidder_id, amount, ts — an append-only log; the authoritative record of every accepted bid.
subscription (auction_id, connection) — who is watching, for the broadcast fan-out; ephemeral.

Step 4 — High-level design: the bid path, the broadcast path and the close

Now the boxes. The clients talk through an API gateway to three separated concerns — a serialized write, a fan-out read, and a scheduled close:

  • Bid service: owns each auction's current high and the append-only bid log; it runs the atomic check-and-accept so only a strictly-higher bid wins, and it is the source of truth for who is leading.
  • Pub-sub + broadcast service: every accepted bid is published to a per-auction topic; a broadcast layer fans the new high out to every websocket-connected watcher of that auction.
  • Notification service: turns an accepted bid into an outbid notification to the previous leader.
  • Scheduler / delay-queue: holds a timer per auction and fires the close at the hard end_time — time-triggered, not user-triggered.
  • Payment service: settles the winner once the auction closes.
  • Sharding: auctions are independent, so everything shards by auction id — a hot item is one shard's problem, not the whole system's.

Say explicitly that this is a serialized write path, a fan-out read path, and a scheduled close kept as separate concerns. Naming that split unprompted — bids serialize on the bid service, the price broadcasts over pub-sub, the close fires from a scheduler — is a signal the interviewer is listening for.

An online auction architecture diagram showing client apps talking through an API gateway to a bid service that holds the strongly consistent current high and an append-only bid log, publishing accepted bids to a pub-sub layer that a broadcast and websocket service fans out to watchers while a notification service sends outbid alerts, plus a scheduler and delay-queue service that closes each auction at its deadline and a payment service that settles the winner.

Deep dive: placing a bid — serialize against the current high

This is the first decision that actually decides the round. The follow-up is always the burst: a hundred bids hit the same item in the same second — how do you not lose an update or accept two "winning" bids?

A bid is only valid if it beats the current high, which means two bids that each read the same old high and then both write will produce a lost update — one bidder's money, another bidder's item. The mistake is to read the high, compare in the application, and write as a separate step. The fix is to make check-and-accept a single atomic operation on the auction's current high, so bids on that item serialize and exactly one can win each round:

Step What it guarantees
Atomic compare-and-set accept the bid only if amount > current_high, as one conditional update (optimistic concurrency) — the database itself rejects a bid that no longer beats the high.
Append to the bid log on accept, append the bid to the append-only log and advance current_high / current_leader in the same transaction — the log is the authoritative history.
Reject and re-bid a bid that lost the race (someone beat it by a hair) comes back rejected: too low, and the bidder sees the new high and bids again.
Serialize per item, shard across items all bids on one auction serialize on its current high; different auctions are independent, so the system shards by auction id and a hot item never blocks the rest.

The reason this is safe under a burst is that the atomic compare-and-set makes the "is this still the highest?" question and the write a single indivisible step — a hundred simultaneous bids are ordered by the store, each re-evaluated against the latest high, and only strictly-higher ones are accepted. No lost updates, never two winners for one round. Saying that the bid must be a conditional update and not a read-then-write is the sentence that separates a real answer from a hand-wave.

"A hundred bids hit the same item in the same second — how do you not lose an update?" I never read the high and write the bid as two steps. Accepting a bid is one atomic compare-and-set — take it only if it still beats the current high — and the accepted bid is appended to the authoritative bid log as the high advances in the same transaction. Bids that lost the race come back rejected as too low and re-bid. All bids on one item serialize; different items shard independently, so a hot auction is one shard's load, not the whole system's.

System Design
HLD · Payments & marketplaces · 45 min
Design an online auction system out loud, with follow-ups

Practise on Zynter →

Deep dive: showing the price live — broadcast and outbid notifications

Serializing the bid decides who is leading; this decides how ten thousand people see the price jump the instant it changes — and it is the opposite problem to the one you just solved. The follow-up is about reach: ten thousand people watch one item — how does the new high reach all their screens at once?

The moment a bid is accepted, the new high has to appear on every watcher's screen, and the previous leader has to learn they were outbid. You do not serve that by having ten thousand clients poll the bid service — that would crush the very store that must stay consistent. Instead it is a broadcast fan-out, deliberately kept off the write path:

  • Publish once: the bid service, having accepted a bid, publishes the new high to a pub-sub topic for that auction — one message per accepted bid.
  • Fan out over websockets: every watcher holds a websocket subscribed to that auction; the broadcast layer pushes the new high to all of them, scaling by adding connection servers, not by touching the database.
  • Outbid notification: the previous leader gets a targeted outbid push (or notification) so they can decide to bid again — often exactly what drives the final-seconds burst.
  • Tolerate a little staleness: the broadcast price is for display; the authoritative high lives in the bid service and is re-checked on every place_bid, so a watcher whose screen is a half-second behind simply has their next bid rejected as too low and sees the corrected price. Display can lag; the bid cannot.

The point to state plainly: the price feed is a lossy-tolerant fan-out that must never share the serialized write path. The accepted bid is the single source of truth and the one event; broadcasting it to thousands is a read-side concern that scales horizontally and is allowed to be a moment behind, because the bid itself is always re-validated against the authoritative high.

"Ten thousand people watch one item — how does the new high reach all their screens the instant it changes?" Not by polling — the bid service publishes each accepted bid to a pub-sub topic for that auction, and every watcher holds a websocket that the broadcast layer pushes the new high to, so one accepted bid fans out to all of them, with a targeted outbid notice to the prior leader. The feed can lag a touch because the authoritative high is re-checked on the next bid, so display staleness never turns into a wrong winner.

A diagram contrasting the two opposite needs of an auction item: on the left many bidders funnel single-file into one atomic compare-and-set against the current high, where a strictly-higher bid is accepted and appended to the bid log while a lower bid is rejected as too low; on the right the accepted new high fans out over a pub-sub topic and websockets to thousands of watcher screens with an outbid notification to the previous leader.

System Design
HLD · Payments & marketplaces · 45 min
Defend the serialize-vs-broadcast split out loud

Practise on Zynter →

Deep dive: closing the auction — deadline, winner and sniping

Serializing bids and broadcasting the price run while the auction is open; this decides the single most error-prone moment — the instant it ends. The follow-up is the snipe: a bid lands in the last 200 milliseconds — does it count, and what stops it stealing a settled auction?

The close is time-triggered, not user-triggered, and it must produce exactly one winner even under failures. Three things make it correct:

  • Schedule the close: a scheduler / delay-queue holds a timer per auction and fires at the hard end_time, moving the auction to closing. You do not poll millions of auctions for "is it over yet?" — each has its own scheduled wake-up.
  • Winner from the settled log, idempotently: determining the winner is authoritative — read the append-only bid log and take the highest accepted bid — and idempotent, safe to run twice. If the close job runs again after a crash or a duplicate trigger, it reads the same settled log and names the same single winner; it never produces two. A closed auction is terminal.
  • Anti-sniping extension: a bid landing in the final seconds would otherwise win uncontested as the clock runs out. Handle it head-on: a bid inside the closing window extends the deadline by a small amount, giving others a chance to respond. The deadline is hard, but it is pushed out by late action rather than letting the last millisecond steal a settled auction. Only when a quiet window passes with no new bid does the close finalize.

The point to state plainly: the close reads from the settled record, not from whatever the broadcast last showed, and it is idempotent so a retry can never crown a second winner. Then the winner is handed to the payment service to settle. It is worth naming the sealed-bid / second-price variant too: when bids are hidden until the deadline, the live fan-out and the sniping problem disappear entirely, and the whole challenge becomes recording every bid and computing the winner (and, for second-price, the second-highest) at close time.

"A bid lands in the last 200 milliseconds — does it count, and what stops it stealing a settled auction?" It counts, and it extends the deadline. A scheduler fires the close at the hard end time, but a bid inside the final window pushes the deadline out by a small amount so others can respond — anti-sniping. The winner is read authoritatively from the append-only bid log as the highest accepted bid, and the close is idempotent, so even if it runs twice it names exactly one winner before handing them to payment.

An auction-close diagram showing a scheduler fire at the hard deadline, an idempotent close that reads the settled append-only bid log to determine exactly one winner (the highest accepted bid) and is safe to run twice, and an anti-sniping branch where a bid landing in the final seconds pushes the deadline out by a small window so a last-second bid cannot steal an otherwise-settled auction, followed by settlement of payment for the winner.

The follow-ups that decide the online auction round

Scan the probes from each deep dive and they rhyme. Master these five and you are ready for a version of the question that is not drawn exactly like this one.

  • "How do you accept a hundred simultaneous bids without losing one?" → Accepting a bid is a single atomic compare-and-set — take it only if it still beats the current high — appended to the authoritative bid log as the high advances; bids on one item serialize, and losers are rejected as too low. (technical execution)
  • "How does the new high reach ten thousand watchers at once?" → The bid service publishes each accepted bid to a per-auction pub-sub topic, fanned out over websockets to every watcher, with an outbid notice to the prior leader — a lossy-tolerant broadcast kept off the serialized write path. (problem solving)
  • "A bid lands in the final millisecond — what happens?" → It extends the deadline. A scheduler fires the close at the hard end time, but a bid in the closing window pushes the deadline out (anti-sniping), so a last-second bid can't steal a settled auction. (problem solving)
  • "How do you guarantee exactly one winner, even if the close runs twice?" → The close reads the winner authoritatively from the append-only bid log and is idempotent — a re-run reads the same settled log and names the same single winner; a closed auction is terminal. (technical execution)
  • "Why not serve the live price straight from the bid database?" → Because the write path must stay serialized and consistent; ten thousand pollers would crush it, so the price is a separate broadcast fan-out that can be a moment stale, re-validated on the next bid. (communication)

Each maps to a debrief dimension. Reading this list teaches you the answers; it does not teach you to deliver them while someone interrupts — that only comes from saying them out loud.

How to prepare: a one-week plan

You do not need fifty systems. You need this one cold and the framework automatic, so the next "design X" is just new nouns in the same five steps.

  • Days 1–2: Learn the five-step framework until it is reflex. Do the online auction system end to end on paper — requirements, estimate, API, data model, and the bid-path/broadcast-path/close boxes.
  • Days 3–4: Drill the bid write: the atomic compare-and-set against the current high, the append-only bid log, rejecting a stale bid, and sharding by auction id. Say each tradeoff in one sentence.
  • Days 5–6: Own the broadcast and the close — pub-sub plus websocket fan-out and outbid notices, and the scheduled, idempotent, anti-sniping close that names exactly one winner. Answer each of the five follow-ups out loud.
  • Day 7: A full timed 45-minute round, narrating the whole way, with someone (or something) interrupting. This is the rep that counts.

This post is the deep walkthrough behind one row of the broader Top 25 system design interview questions — once the online auction system is automatic, the same spine carries the rest of the list.

Frequently asked questions

How do I design an online auction system step by step?

Follow the five-step HLD framework. Clarify requirements (seller lists an item with a hard end time; buyers place bids that must beat the current high; everyone watches the live price; the prior leader is notified when outbid; at the deadline the auction closes with exactly one winner, who pays); estimate to show the split — a websocket fan-out of thousands of watchers per hot item against serialized bid writes on one current high, plus a scheduled close per auction; define the small API (create_auction, place_bid with an idempotency key, get_auction, subscribe) with a thin auction/bid-log/subscription model; draw the design (clients through a gateway to a bid service owning the current high and append-only bid log, a pub-sub + websocket broadcast path, a notification service, a scheduler that closes auctions, and a payment service); then deep-dive the bid write, the broadcast, and the close. Finish on the follow-ups about simultaneous bids and sniping.

How does an auction accept bids without lost updates?

By never reading the current high and writing the bid as two separate steps. Accepting a bid is a single atomic compare-and-set (a conditional update under optimistic concurrency): take the bid only if its amount still strictly beats the current high, and in the same transaction append it to the append-only bid log and advance the current high and leader. Two bids that each read the same old high can no longer both win — the store serializes them, re-evaluates the second against the new high, and rejects it as too low so the bidder can re-bid. All bids on one item serialize on that item's current high, while different auctions are independent, so the system shards by auction id and a hot item never blocks the rest.

How does the live auction price reach thousands of watchers?

Through a broadcast fan-out, not by polling the bid database. When a bid is accepted, the bid service publishes the new high to a pub-sub topic for that auction; every watcher holds a websocket subscribed to that auction, and the broadcast layer pushes the new high to all of them, scaling by adding connection servers rather than touching the consistent store. The previous leader gets a targeted outbid notification. The broadcast price is for display and may lag by a fraction of a second, which is harmless because the authoritative high lives in the bid service and is re-checked on every new bid — so a stale screen just means the next bid is rejected and the price corrects, never a wrong winner.

How does an auction close with exactly one winner?

The close is scheduled, authoritative, and idempotent. A scheduler or delay-queue holds a timer per auction and fires at the hard end time rather than the system polling millions of auctions. At close, the winner is read authoritatively from the append-only bid log as the highest accepted bid, and the close is idempotent — safe to run twice — so if it re-runs after a crash or a duplicate trigger it reads the same settled log and names the same single winner, never two; a closed auction is terminal. The winner is then handed to the payment service to settle. Reading from the settled record rather than the last broadcast value is what keeps the result unambiguous.

What is anti-sniping and how do you handle last-second bids?

Sniping is bidding in the final moment so no one has time to respond, letting a late bid win an otherwise-settled auction uncontested. The common fix is an anti-sniping extension: a bid that lands inside a short closing window pushes the deadline out by a small amount, giving other bidders a chance to react. The deadline stays hard, but late action extends it rather than ending the auction on the last millisecond, and the auction only finalizes when a quiet window passes with no new bid. This keeps the close fair without ever accepting a bid after the auction has truly ended. Sealed-bid and second-price formats avoid the problem entirely, since no one sees a live price to snipe against.

How do I practise a system design interview out loud?

Reading a walkthrough is not the same as defending a design while someone interrupts. On Zynter, the HLD track runs live voice rounds modelled on how real system design loops run — you talk through a shared design on the canvas, and a voice interviewer pushes with the follow-ups above one at a time, then gives you a debrief on problem solving, communication, technical execution, and time management. The score is a practice signal, not a hiring decision.

Practise it on Zynter

You can read every bid rule. The round is won on serializing the write and closing with one winner.

Pick the Online Auction round, draw the bid-path, broadcast-path and close on the canvas, and defend it out loud to a voice interviewer that keeps asking "how do you not lose a bid in a burst?" and "what stops a last-second snipe from stealing it?" — then read the debrief. Zynter.ai is on-demand mock interviews modelled on real company loops, with 150+ rounds across 20 companies including the HLD system design track (Sep 2026).

Practise the Online Auction round →
Browse all interview tracks

Top comments (0)