CASE STUDY

E-commerce Shopping Website (and Fixing Cart Latency)

4 min read·766 words·Intermediate

How to use this case study

SDE-2 / Mid

Explain the main services (catalog, search, cart, checkout, orders, payments, inventory) and the checkout flow.

SDE-3 / Senior

Go deeper on caching the cart and product data, reserving inventory at checkout, and "notify me when back in stock".

Staff / Principal

Discuss profiling an existing slow system before changing it, peak events (sales), and consistency between inventory, orders and payments.


0) Problem Restatement

JPMorgan asked two versions:

  1. Design a scalable e-commerce website: browse products, search, cart, checkout, orders and payment.
  2. Improve an existing system: the shopping cart (web and mobile) already works, but it's slow, and product wants "notify me when this item is back in stock". You should improve the current design, not rewrite it.

Asked at: JPMorgan — 2 candidate reports between Feb 2026 and Jun 2026.

1) Requirements

  • Browse and search products, product pages.
  • Add to cart, view the cart, update quantities.
  • Checkout: address, payment, order creation, inventory update.
  • Order history and status.
  • Back-in-stock alerts.
  • Handle big sale days (5–10x traffic).


2) Architecture (greenfield)

Architecture Diagram

flowchart LR
    U["Web / Mobile"] --> CDN["CDN - static, product pages"]
    U --> GW["API Gateway"]
    GW --> CAT["Catalog"]
    GW --> SRCH["Search"]
    GW --> CART["Cart Service"]
    GW --> CHK["Checkout / Orders"]
    CART --> RC[("Redis - carts")]
    CART --> CDB[("Cart DB - durable")]
    CHK --> INV[("Inventory")]
    CHK --> PAY["Payments"]
    CHK --> ODB[("Orders DB")]
    CHK --> K[("Events")]
    K --> NOTIF["Notifications"]
    INV --> K
  • Catalog + Search: read-heavy. Product pages are cached at the CDN, and search uses a search index.
  • Cart: fast reads and writes in Redis, persisted to a durable DB (so carts survive restarts and work across devices).
  • Checkout/Orders: validates price and stock, reserves inventory, takes payment, creates the order (idempotently).
  • Events: order placed, stock changed, feeding notifications and analytics.

2.1 Checkout in short

  1. Re-validate the cart (prices, availability).
  2. Reserve inventory atomically (available >= qty → decrement and hold for 10 minutes).
  3. Authorize payment → create the order → confirm the reservation. If payment fails, release the hold.
  4. An idempotency key on "Place order" prevents double orders.


3) Version 2: Improving an Existing Slow Cart

Step 1: Measure first. Don't guess. Add tracing to the cart endpoints and find where time goes. Typical findings:
  • The cart page calls many services one by one (product details, price, stock, promotions) for each item → N sequential calls. Fix: batch calls (getProducts(ids)), make them in parallel, and cache product data.
  • Every read hits the DB with joins. Fix: keep the cart as one document in Redis (write-through to the DB), and invalidate or update it on each change.
  • Price and stock computed live on every view. Fix: cache prices briefly, and show stock as "In stock / Few left" from a cached value, with exact checks only at checkout.
  • Chatty mobile clients re-fetching the whole cart after every tap. Fix: return the updated cart in the mutation response, use ETags, and avoid extra round trips.
  • Large payloads: send only what the cart UI needs.

Step 2: Roll out safely: behind a feature flag, compare latency (p50/p99) before and after, and keep the old path for rollback.

4) "Notify Me When Back in Stock"

Architecture Diagram

flowchart LR
    U["User taps Notify me"] --> SUB["Subscription API"]
    SUB --> SDB[("Subscriptions - by product_id")]
    INV["Inventory updates"] -->|"stock 0 to positive"| K[("Stock events")]
    K --> W["Back-in-stock worker"]
    W --> SDB
    W -->|"batched, rate limited"| N["Push / Email"]
  1. Store subscriptions (product_id, variant_id, user_id, created_at, notified=false), indexed by product.
  2. The inventory service publishes an event when stock goes from 0 to above 0 (only on that transition, not on every change).
  3. A worker loads the subscribers for that product and sends notifications in batches. If stock is small (e.g., 20 units but 50,000 subscribers), notify in waves or first-come order, and mark notified=true so nobody is spammed.
  4. Include a deep link to the product. Stock may run out again, which is fine: it's a notification, not a reservation.


5) Handling Sale Days

  • Pre-scale stateless services, warm the caches, and serve static and product pages from the CDN.
  • Put a queue or waiting room in front of checkout for extreme spikes.
  • Protect inventory with atomic decrements, and use a separate hot-item stock counter (e.g., Redis) for flash-sale items, reconciled with the DB.


6) Trade-offs & Alternatives

DecisionChoiceWhyAlternative
Cart storageRedis + durable DBFast and safeDB only: slow; cache only: carts lost
Stock on cart pageCached approximate statusFastExact live stock: slow, not needed until checkout
CheckoutReserve → pay → confirm, idempotentNo oversell, no double ordersDecrement after payment: oversell risk
Back in stockEvent on 0→positive, batched sendsEfficient, no spamPoll stock for every subscriber: wasteful

7) Wrap-Up

Split the site into catalog and search (CDN and cache heavy), cart (Redis + durable DB), and checkout (re-validate, reserve stock atomically, pay, create the order idempotently), connected by events. To fix a slow existing cart, measure with tracing, then batch and parallelize service calls, cache the cart and product data, and trim payloads, rolling out behind a flag. Implement back-in-stock alerts as subscriptions triggered by a stock 0→positive event, sent in batches.

More Case Studies

Practice with a Mock Interview

Apply what you learned in a live system design mock interview with our AI interviewer.

Start System Design Interview →