System design sample solution

System design: design an online checkout

A step-by-step system design answer you could actually give in 60 minutes.

System design interview · Sample 60-minute answer · Not Amazon's actual design · Independent, not affiliated with Amazon

The system design question

Design the checkout for an online store like Amazon. A customer reviews their cart, pays, and gets an order confirmation. We must never charge anyone twice, and we can't sell more of an item than we have.

Agree on the scope (5 minutes)

Say up front what you're covering.

We'll designWe'll skip
Turning a cart into an orderSearch and the catalog
Holding stock while someone paysShipping and delivery
Taking the paymentReturns and refunds
Confirming the orderRecommendations

Then agree on what "good" means:

  • Correct: nobody gets charged twice, and we never sell stock we don't have.
  • Fast: placing an order should take a couple of seconds at most.
  • Keeps running: a big sale shouldn't take checkout down.
  • Clear: if something goes wrong, the customer should know whether they've been charged.

Rough numbers (5 minutes)

These are made up, but reasonable. Say 10 million orders a day, about 3 items each, and sale days that bring 10 times the normal traffic.

~120
orders per second, on average
~1,200
orders per second on a big sale day
~3,600
stock updates per second at that peak
~50 GB
of new order data a day
QuestionRough mathWhat it means
How many payment calls?One per orderAbout 1,200 a second at a peak, to an outside provider
How much order data?10 million orders × about 5 KBAbout 50 GB a day, fine for a relational database
How bad is one hot item?A deal everyone wantsThousands of updates a second to one row

The overall design (10 minutes)

Customer appplaces the orderCheckout serviceruns each stepInventoryholds the stockPayment providercharges the cardOrder queuetells everyone else
Checkout holds the stock, takes the payment, then tells the rest of the company about the order.
  1. When the checkout page opens, the app makes up a unique order ID. Every retry of 'Place order' sends the same ID.
  2. The checkout service saves the order as pending.
  3. It holds the stock for each item, with a time limit.
  4. It charges the customer through the payment provider, passing along the same order ID.
  5. It marks the order as placed and puts an 'order placed' message on a queue for the warehouse, emails and so on.

What the apps need to do

ActionWho does itWhat's sent
Place an orderCustomerCart items, address, payment method and the order ID
Check an orderCustomerOrder ID, and we return its status
Payment resultPayment providerA webhook saying whether a charge went through

What we store and where

DataWhere it goesWhy
OrdersRelational databaseIt's the permanent record, and it has to be correct
Stock countsRelational databaseUpdates must never let the count go below zero
Stock holdsRelational databaseEach one expires if the customer doesn't finish
Payment attemptsRelational databaseSo we always know what we asked the provider to do
Order placed messagesQueueOther teams react without slowing checkout down

Step 1: Never charge twice (10 minutes)

Customers double-click, phones lose signal, and requests get retried. That's why the order ID is made up front: if the same order ID comes in twice, we return the first result and don't start again. That's idempotency.

Before calling the payment provider, save a 'payment attempt' with the order ID. Most providers accept an idempotency key, so pass the order ID along. If we retry, they won't charge the card again.

The tricky case is a timeout, where we don't know if the charge went through. Don't just try again blind. Ask the provider what happened to that key, or wait for their webhook to tell us.

Step 2: Never sell what you don't have (10 minutes)

When an order starts, take the item out of stock with one update that only works if there's at least one left. If it fails, the item's sold out and we tell the customer right away. That's the consistency we need here.

Each hold expires after about 10 minutes. If the payment fails or the customer walks away, the stock goes back on its own.

What about a lightning deal?

If everyone wants the same item, thousands of updates hit one row and wait in line. One fix is to split that item's stock into several smaller counts and have each order take from one at random. That spreads the load, a lot like sharding the item.

Step 3: After the order's placed (10 minutes)

The warehouse, confirmation emails and analytics all care about new orders, but none of them should slow checkout down. Checkout puts an 'order placed' message on a message queue, and each of them reads it in its own time.

Save the message in the same database update as the order, and have a small job send it to the queue. That way an order can never exist without its message, even if the queue's down for a minute.

Choices you should be able to defend

We choseInstead ofBecauseWe'd reconsider if
Holding stock at checkoutHolding it when items go in the cartCarts sit around for days and would lock up stockItems sell out while people are still paying
Charging during checkoutCharging later, after the order's confirmedThe customer gets a clear answer straight awayThe payment provider got slow enough to hurt checkout times
A relational databaseA NoSQL storeWe need updates that can't go wrongOrder volume outgrew what we can split up
Splitting hot stock countsOne count per itemOne deal can't pile up on one rowMost items never get busy enough to need it

What could go wrong (5 minutes)

What happensWhat we do about it
The payment provider times outCheck what happened using the order ID before doing anything else
The payment provider is downKeep the order pending, hold the stock, and tell the customer we'll retry or ask them to try again
Checkout crashes after chargingA cleanup job finds charged orders that never got confirmed, and the provider's webhook fills in the gap
A sale overloads the stock databaseSplit hot items' counts, and let customers wait in a short virtual line
The queue goes downOrders still go through. Their saved messages get sent once it's back

Follow-up questions (5 minutes)

What about gift cards and split payments?

Treat each payment as its own attempt with its own key, and only confirm the order once they've all succeeded. If one fails, undo the others.

How would you handle cancellations?

Add a cancelled state, release any stock, and refund through the provider with a new idempotency key.

What if we sold in many countries?

Run checkout in several regions, but keep each item's stock in one place so two regions can't both sell the last one.

How the 60 minutes add up

MinutesWhat you're doing
0–5Agree on what you're building
5–10Rough numbers
10–20Draw the overall design
20–30Never charge twice
30–40Never sell what you don't have
40–50After the order's placed
50–55What could go wrong
55–60Follow-up questions

Key concepts used here

Further reading

We wrote this sample answer ourselves.

Practice Loop is independent and isn't affiliated with or endorsed by Amazon.