12 system design interview questions on ticketing, payments and ride sharing, each with the correct answer and a full explanation.
Ticketing: a user selects a seat and has 10 minutes to pay. If they abandon checkout the seat must become available again. How do you model it?
Show answer
Answer
A hold with an expires_at, treated as free once expired, confirmed on payment
A hold row with an expiry (or a Redis key with TTL) frees the seat automatically and the confirm step checks the hold is still valid. Users who close the tab never click cancel. A daily job leaves seats locked for hours. App memory is lost on restart and invisible to other servers.
A concert on sale draws 1 million users for 50,000 seats in the first minute. What keeps the booking system up and fair?
Show answer
Answer
A virtual waiting room that admits users at a controlled rate
A waiting room turns a spike into a steady flow the booking path can handle and gives a fair order. Database autoscaling is slow and contention on the same rows does not scale out. Caching the seat map helps browsing but not 1 million concurrent bookings. Racing on row locks melts the database and is random, not fair.
Design Ticketmaster. Requirements, core entities, API and the deep dives.
Show answer
Answer
Requirements: browse and search events, view seat map, book without double booking, handle on sale spikes. Consistency for booking, availability for browsing.
Entities: event, venue, seat, ticket, booking.
API: GET /events/:id, POST /bookings/hold, POST /bookings/:id/confirm.
Deep dive 1, holds: conditional update or Redis lock with TTL, confirm on payment, expire otherwise.
Deep dive 2, spikes: virtual waiting room, cached seat map with live updates via SSE.
A wallet system keeps a double entry ledger. Which rule must always hold?
Show answer
Answer
Every transaction's entries sum to zero and entries are never edited, only reversed by new entries
Double entry means each movement debits one account and credits another for the same amount, and the log is append only so history is auditable; corrections are new entries. Updating a balance in place without entries loses the audit trail. Deleting entries destroys history. Recording one side only is single entry bookkeeping.
Your call to the payment provider timed out, so your database marked the charge failed. The provider actually succeeded. How do you catch and fix this?
Show answer
Answer
Reconcile with the provider by idempotency key or settlement report and correct your state
A timeout is unknown, not failed. Query the provider (same idempotency key) and run periodic reconciliation against settlement files. A new key creates a second real charge. Providers do not refund on your behalf. External providers do not join your 2PC.
How should a payments service store the amount 19.99 USD?
Show answer
Answer
As the integer 1999 in minor units, with the currency code
Integers in the smallest currency unit add exactly, which is how Stripe's API takes amounts; note some currencies have zero or three decimals. Binary floats cannot represent 0.1 or 19.99 exactly, so sums drift, and rounding each write does not fix accumulation. Strings with symbols need parsing and lose the currency for symbols like $ shared by many currencies.
Checkout calls payment, then inventory, in separate services with their own databases. Payment succeeds, inventory is out of stock. What happens in a saga?
Show answer
Answer
A compensating action refunds the payment
Sagas replace a distributed transaction with local steps and explicit compensations. 2PC across microservices is avoided because it blocks and couples availability. Ignoring it takes money for nothing. Retrying forever leaves the customer charged with no order.
Drivers update location every 4 seconds; riders query drivers within 2 km. Which index fits?
Show answer
Answer
Geohash or H3 cells in an in memory store, querying the rider's cell and its neighbours
Cell IDs turn a 2D search into a few key lookups and updates are cheap key moves, which suits high churn. A B tree on lat and long can only range on one dimension efficiently. Rebuilding a quadtree in a database per update is expensive. Full scans do not scale.
A geohash search queries only the rider's own cell and misses a driver 50 meters away. Why, and what fixes it?
Show answer
Answer
The driver is in an adjacent cell; also query the 8 neighbouring cells
Points near a boundary can sit in different cells, sometimes with very different prefixes, so you search neighbours too. A shorter geohash alone still has edges. Geohashes are not random. Staleness is a different issue from a 50 meter boundary miss.
1 million active drivers each send a location every 4 seconds. What write rate must the location service handle?
Show answer
Answer
250,000 per second
1,000,000 / 4 = 250,000 updates per second, which is why locations go to an in memory store rather than a durable row per update. 4,000,000 multiplies by 4. 25,000 drops a zero. 1,000,000 assumes one update per second.
Design Uber's ride matching. Requirements, location ingestion, matching and the deep dives.
Show answer
Answer
Requirements: riders request, nearby drivers found in seconds, each driver gets one ride at a time, ETA.
Ingestion: drivers push location every few seconds to an in memory geo index (H3 or geohash cells), sharded by region.
Matching: query nearby cells, rank by ETA, offer to one driver at a time with a timeout; claim the driver atomically (conditional update or lock with TTL) so no double dispatch.
Deep dives: hot cells downtown, surge, driver offline detection, consistency of the trip state machine, payments via the payment system.