Design an e-commerce site
Catalogue, cart, checkout and orders, with inventory that is never oversold.
- Sharding and partitioning — Splitting data by key so each machine holds a slice it can handle.
- Distributed transactions (2PC and sagas) — All-or-nothing across services, or a sequence of steps with compensations.
- Caching strategies — Read-through, write-through, write-back, TTLs and invalidation.
- Message queues and pub/sub — Decoupling producers from consumers with a durable log in between.
- Inverted indexes — From every word to the documents that contain it: how search answers in milliseconds.
01The design, in the six-step framework
1. Requirements
Functional
- Browse and search a catalogue; see price and availability.
- Cart that survives devices and sessions; checkout with address and payment.
- Orders: placed, paid, shipped, delivered, with history.
- Inventory is never oversold; a flash sale of a scarce item must behave.
- Out of scope: recommendations, seller onboarding, returns logistics.
Non-functional
- 100 million products; 50 million daily users; 5 million orders a day, 50,000 orders a minute at a flash-sale peak.
- Product page in under 300 ms; search in under 500 ms; checkout confirmation in under 2 s.
- Strong consistency for inventory and orders; eventual for catalogue, reviews and search.
- Availability: browsing 99.99 percent, checkout 99.95 percent; the two must fail independently.
- Audit trail for every order state change.
Reads dominate: 50 M users × 20 page views = 1 billion product page views a day, about 12,000 a second average, 100,000 at peak; nearly all cacheable.
Orders: 5 × 106 ÷ 86,400 ≈ 60 per second average; a flash sale is 50,000 a minute ≈ 830 per second, still small for a transactional database partitioned by region.
Catalogue: 100 M products × 5 KB (text, attributes, a few image references) = 500 GB; images are tens of petabytes in object storage behind a CDN.
Search index: 100 M documents × 2 KB of postings = 200 GB, sharded across a few dozen nodes with replicas.
2. Core entities
- Product: product_id, title, attributes, images, category, price (current), version.
- Inventory: sku_id, warehouse_id, on_hand, reserved, version.
- Cart: user_id, items (sku, quantity, price seen), updated_at.
- Order: order_id, user_id, items with prices at purchase, address, state, payment reference, created_at.
- Order event: order_id, from_state, to_state, actor, timestamp.
3. API
GET /products/{id}: page data from the cache; availability from the inventory service with a short TTL.GET /search?q=…&filters=…&cursor=…: ranked results with facets.PUT /carts/{user}/items: add or change quantity; carts are server side.POST /orderswith cart version, address, payment token, idempotency key; returns order_id and state.GET /orders/{id}andGET /users/{id}/orders: history.
4. High-level design
From product page to order:
- The product page is assembled by the catalogue service from a cached product record (invalidated on edit) plus a short-TTL availability flag from inventory; images come from the CDN.
- Search goes to the inverted index, sharded by product id with replicas; results carry price and availability snapshots that may be seconds old.
- Adding to cart writes the cart record in a key-value store keyed by user; the cart stores the price the user saw.
- Checkout calls the order service with the cart version and an idempotency key; the service runs one transaction on the orders database: for each item, decrement reserved-available inventory where available ≥ quantity, create the order in state placed, write the order event.
- Payment is authorised with the provider; on success the order moves to paid (another transaction and event); on failure the reservation is released.
- Order events on the queue drive fulfilment, email, and the search index's availability refresh, all asynchronously.
5. Deep dives
How is inventory never oversold during a flash sale of 1,000 units wanted by 500,000 people?
| Option | Behaviour | Trade-off |
|---|---|---|
| Read stock, then write order | Thousands of concurrent readers all see 1,000 and all order | Massive oversell |
| Conditional decrement in the order transaction | Update inventory set available = available − q where available ≥ q; zero rows means sold out | Correct; one hot row takes all the contention, roughly thousands of updates a second at most |
| Pre-sharded stock counters | Split 1,000 units into 10 counters of 100 in a cache, decrement one at random, fall back to others | Scales the hot row; needs reconciliation with the database and a final guard |
The conditional decrement inside the order transaction is the correctness guard and suffices for most products; for the few flash-sale items, a pre-sharded counter in front absorbs the burst and only successful decrements proceed to the transaction, so the database sees at most 1,000 attempts plus a few. Losers are told sold out in milliseconds.
Why are browsing and checkout independent, and how does that show in the design?
A catalogue database incident must not stop orders, and an order database incident must not stop browsing. The services have separate stores, separate deployment units and separate capacity. The product page needs one piece of order-side data, availability, and gets it through a cached flag with a short TTL that falls back to showing the item as available when the inventory service is down, since the checkout transaction is the real guard. Checkout, in turn, needs prices, which it takes from the cart snapshot rather than calling the catalogue, so the two paths share nothing on the hot path.
How do prices stay correct from page to payment?
Prices change; a user who added an item at 100 should not be silently charged 120 at checkout, nor should the merchant honour a price from last month. The cart stores the price seen and when; checkout compares it with the current price, accepts it if within a validity window (say 30 minutes) or re-prices with an explicit message otherwise. The order stores its own copy of every price, so history never changes when the catalogue does.
How does search stay fresh when a product sells out?
Re-indexing 100 M documents on every stock change is impossible; stock is not part of the ranked index at all. The index holds searchable text and attributes, updated from catalogue change events within minutes; availability is joined at query time from a small in-memory map of sku to in-stock flag that the order events update within seconds. Sold-out items are demoted, not removed, so a page refresh does not reshuffle results.
What makes checkout idempotent and auditable?
A client that times out and retries must not create two orders: the idempotency key (generated when the checkout page opens) is stored with the order, and a repeat returns the existing order. Every state change writes an order event row in the same transaction as the change, which is the audit trail and the source for the event queue, published after commit by an outbox reader so an event is never emitted for a transaction that rolled back.
6. What a strong answer sounds like
- Separate the read path (catalogue, search, caches, CDN) from the write path (cart, orders, inventory) and give each its own consistency and availability targets.
- Quote 100,000 page views a second versus 830 orders a second at peak and say what that asymmetry buys.
- Explain the conditional decrement and when to add sharded counters in front of it.
- Expect follow-ups on the outbox pattern, on multi-warehouse inventory, and on how microservices share nothing on the hot path.
- A common mistake is to put inventory in the same cache as the product page and decrement it there; the cache is not transactional with the order.
02Where this question was reported
- interviewkickstart.com · 'online bookstore'; e-commerce; e-commerce using microservices with transactions
- interviewkickstart.com · Listed; 'your version of Amazon'
- interviewkickstart.com · 'microservices for a large-scale e-commerce platform'
- DesignGurus · 'Online Store (like Amazon)'
- PracHub · SWE, Oct 2026
- Taro · Design a recommendation engine for a social media platform; loop also had a separate ML system design round
- Blind · Recommendation system for e-commerce; interviewer stayed on inputs/outputs and product value, never reached architecture (felt like a PM question)
- 1point3acres · ML design: video recommendation system and an Explore-style recommender (search snippet, Chinese)
- PracHub · In-person onsite Oct 2026, 'solutions design' round: review ingestion platform with LLM summaries; focus on trust and safety, prompt injection, sanitization, PI