Define the APIs expected from the system. This is your chance to analyze and define the read and write paths so that you can come up with the high-level design...
Describe the overall system architecture. Identify the main components needed to solve the problem end-to-end. Use the diagramming tool to create a block diagram.
We have the Request Path which is the Client, then the request gets fed to the API Gateway which handles Rate Limiting and Load Balancing. After that it gets sent to the services: Transaction, Reservation and Gate. On the Data Layer we have Transaction Service and Reservation Service connecting to both a Redis Cache and PostgreSQL DB. On the External and Ops layer, we have the Transaction Service connecting to the Payment Provider. The Gate Service connecting to the Lot Gate Devices. Reservation and Gate Services are connected to Background Jobs. All Three services are connected to a monitoring system like Datadog.
Deep dive into 2-3 key components. Explain how they work, how they scale, discuss tradeoffs, capacity, and any relevant algorithms or data structures.
S — State the problem
"I want to deep dive on the Reservation Service because preventing double bookings is the most critical problem in this system. If two people book the same spot simultaneously the business fails at its core function."
W — Why it's hard
"Under normal traffic this is straightforward. But during a high demand event like a concert or sports game, thousands of requests arrive simultaneously. A naive implementation reads a spot as available, then another request reads the same spot as available before either one commits — both proceed and we have a double booking."
I — Implement your solution
"I'd solve this with database transactions and pessimistic locking. When a reservation request comes in I open a transaction, lock that specific spot row, check availability, create the reservation, then commit atomically. If two requests arrive simultaneously one gets the lock and the other waits. When the first commits the second checks availability, finds the spot taken, and returns an error to the user.
An alternative is optimistic locking — add a version number to each spot row. Both requests proceed without locking but when committing check if the version changed since you read it. If yes someone else got there first — retry. This is faster under normal load but adds retry complexity."
F — Failure handling
"Two main failure scenarios. First — what if the service crashes after locking the spot but before confirming the reservation? I'd use a saga pattern — lock spot and create reservation are two separate steps each with compensating actions. If confirmation fails the saga releases the lock and marks the spot available again automatically.
Second — what if we get a flood of requests for the last available spot? I'd add a queue in front of the reservation service during peak load so requests are processed one at a time rather than hitting the database simultaneously. Dead letter queue handles any requests that fail after multiple retries."
For stale cache — I'd combine a short TTL of 30-60 seconds with Kafka driven cache invalidation on every booking. This minimizes the window where Redis returns outdated availability.
For hot lot surges I'd use a Redis sorted set queue to serialize requests
T — Tradeoffs
"The main tradeoff is pessimistic vs optimistic locking. Pessimistic locking guarantees no double bookings but creates contention under high load — thousands of requests queuing for the same lock degrades performance significantly. Optimistic locking is faster under normal conditions but requires retry logic and can cause poor user experience when many people are competing for the same spot simultaneously.
S — State the problem
"I want to deep dive on the Transaction Service because payment reliability is the most critical and complex part of this system. If payments fail silently or customers get double charged the business loses trust immediately."
W — Why it's hard
"The challenge is that payments involve three separate systems — our Transaction Service, the payment provider like Stripe, and our database. Any one of them can fail at any point in the flow. The hardest scenario is when the payment succeeds at Stripe but our service crashes before we record it — now the customer was charged but has no reservation."
I — Implement your solution
"I'd handle this in two ways. First, idempotency keys — every payment request includes a unique client generated ID. If the same request is retried after a timeout, Stripe recognizes the key and returns the original result without charging again. This prevents double charging entirely.
Second, I'd use the saga pattern for the full reservation flow. Each step — lock spot, charge payment, confirm reservation — has a compensating action. If confirmation fails after payment succeeds, the saga automatically triggers a refund and releases the spot. No manual intervention needed."
F — Failure handling
"For the payment provider going down entirely I'd add a circuit breaker — after five consecutive failures we stop sending requests to Stripe and immediately return an error rather than making users wait for timeouts. I'd also publish a payment succeeded event to Kafka immediately after charging so even if our database write fails the event is persisted and a background consumer can retry the write."
T — Tradeoffs