Design an Auction System (eBay)
Table of Contents + −
On eBay, people put items up for sale, and others bid on them. The highest bid when time runs out wins. It sounds easy, but think about a popular item in its final seconds: hundreds of people bidding at the exact same moment, all wanting to be the highest. Getting that right, safely and fairly, is a great system design challenge. Let’s build it.
🎯 What the System Does
An auction system needs to:
- Let a seller list an item with a starting price and an end time.
- Let buyers place bids on it.
- Track the highest bid, and reject bids that are too low.
- End the auction at the set time, and the highest bidder wins.
- Notify users when they’re outbid or when they win.
The tricky part is the bidding, especially when many bids land at once. Let’s keep that front of mind.
📋 Requirements
Functional (what it must do):
- List items, place bids, track the highest bid.
- End auctions on time and pick the winner.
- Notify users about outbids and wins.
Non-functional (how well it must do it):
- Correctness: the right person must win. No lost or double-counted bids. This is critical.
- Speed: bids must register instantly, especially in the final seconds.
- Scale: handle popular items with many bidders at once.
Here, correctness beats everything
Money and fairness are on the line. If two bids clash and one is lost, someone wins unfairly. So the design must handle many bids at once without errors. That’s the heart of this problem.
🏗️ High-Level Design
Here’s the shape of the system.
Reading the parts:
- The bid service takes each bid, checks if it’s high enough, and records it.
- The database stores items, bids, and the current highest bid for each item.
- The auction closer watches the clock and ends auctions when their time is up.
- The notification service tells users when they’re outbid or they’ve won.
🔒 The Core Problem: Many Bids at Once
This is the part that makes or breaks the design. Picture two people bidding on the same item at the same instant:
- Both read the current highest bid: 100.
- Both place a bid of 110.
- If we’re not careful, both get accepted, or one quietly overwrites the other.
This is a race condition, the same problem two threads have with shared data. We must make sure bids on one item are handled one at a time, in order.
The fix is to process each item’s bids in a single, ordered line:
- All bids for one item go to the same place and are handled one after another.
- For each bid, we check “is this higher than the current highest?” If yes, accept and update. If no, reject.
- Because they’re handled one at a time, there’s no clash. The second bidder sees the first bid already counted.
This is the same idea as keeping messages in order per item: order matters per item (per auction), not across the whole system. Different items can be handled in parallel.
Never let two bids update the highest at once
If two bids update the highest bid at the same time, one can be lost and the wrong person wins. Handling each item’s bids in a single ordered line is what keeps the auction fair.
⏰ Ending the Auction on Time
Each auction has an end time. When it hits, the highest bid wins. How do we trigger that exactly on time?
- The auction closer keeps track of which auctions end when.
- When an auction’s time is up, it locks the item (no more bids), reads the highest bid, and marks that bidder the winner.
- Then it tells the notification service to alert the winner and the seller.
A subtle real-world touch: some auctions extend by a few seconds if a bid lands at the very last moment, so people can’t win by sneaking in a bid at 0 seconds. That’s a fairness rule worth mentioning in an interview.
📣 Notifications and Reads
Two more pieces round it out:
- Notifications: when someone is outbid, we send them an alert so they can bid again. This is a great use of a message queue: the bid service drops a “notify” task, and a separate worker sends it, so bidding stays fast.
- Reading the current price: lots of people watch an item without bidding. Those views can read a cached highest bid, so the database isn’t hammered by everyone refreshing.
📈 Scaling
To handle popular items and many users:
- Split bids by item, so each item’s ordered line runs independently. Popular items get their own handling, and unrelated items don’t slow each other down.
- Use read caches for the many people just watching prices.
- Use the queue for notifications so the bidding path stays quick.
🧰 Tech Choices
Part of system design is not just naming pieces, it’s saying why you picked each one. Here are the main technology decisions for this system and the reason behind each.
| Decision | Choice | Why |
|---|---|---|
| Handle bids on one item | Single ordered line per item | Prevents race conditions so the right, highest bidder wins. |
| Send outbid / won alerts | Message queue | Notifications happen off the bid path, so bidding stays fast. |
| Show live price to watchers | Cache + WebSockets | Cheap reads, and the new price is pushed instantly to many watchers. |
| Permanent record | Relational database | Durable, consistent store of items, bids, and winners. |
⚠️ Common Mistakes and Misconceptions
A few things to keep straight:
- “Just accept bids as they come.” Without ordering bids per item, two bids can clash and one is lost, making the auction unfair. Bids on one item must be handled one at a time.
- “Send notifications inside the bid request.” That slows bidding down. Drop a task on a queue and let a separate worker send alerts.
- “Check the clock on every bid to end the auction.” Better to have a dedicated closer handle endings, so the bidding path stays simple and fast.
🧩 What You’ve Learned
Nice work. Here’s the recap:
- ✅ An auction system lists items, takes bids, tracks the highest bid, ends on time, and notifies users.
- ✅ The hardest part is many bids at once: a race condition that can lose bids or pick the wrong winner.
- ✅ The fix is to handle each item’s bids in a single ordered line, so they never clash.
- ✅ A dedicated auction closer ends auctions on time and picks the winner.
- ✅ Notifications go through a queue, and price views read from a cache, so bidding stays fast and scales.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What is the core danger when many bids arrive at once?
Why: Two bids updating the highest bid at the same time can clash, losing a bid and making the auction unfair. That's a race condition.
- 2
How do we handle bids on one item safely?
Why: Handling one item's bids one after another means each bid sees the previous one counted, so there's no clash.
- 3
Why send outbid notifications through a queue?
Why: Dropping a notify task on a queue lets a separate worker send alerts, so placing a bid stays quick.
- 4
Does bid ordering need to be global across all auctions?
Why: You only need order within one auction. Different items run independently, which lets the system scale.
🛠️ Design Challenge
Try extending the auction yourself. Think each one through first, then open the answer to see a full breakdown.
Automatic (proxy) bidding. Let a user set a maximum they’re willing to pay, and the system bids on their behalf just enough to stay on top. How would you build that?
Last-second bidding (sniping). People wait until the final second to bid so others can’t respond. How do you keep it fair?
Showing the live price to many watchers. Thousands watch a hot item without bidding. How do you show them the current price without overloading the system?
🚀 What’s Next?
You’ve designed an auction. Let’s look at problems with the same “many people at once” challenge.
- Design a Coupon / Flash-Sale System handles a rush of people grabbing limited items.
- Locks & Synchronization is the core idea behind handling bids safely.
Get these down and you’ll be ready for any “lots of users competing at once” design.