Movie Ticket Booking (BookMyShow) — Machine Coding Round
Machine coding round — design a BookMyShow-style movie ticket system: city-to-show browsing, timed seat holds, UPI/card/wallet payments and notifications.

Design a movie ticket booking system that lets a user browse from city down to a specific show, pick seats visually, hold them briefly while paying, and book — all while staying correct when many users compete for the same seats at once.
This one tests whether you can model the browsing hierarchy (city → movie → cinema → show → seats) cleanly, while getting the trickier real-world behavior right: temporary seat holds that expire, and fair, race-free seat allocation when multiple users try to book the same seat simultaneously.
Objective
The primary objective is to design and implement a movie ticket booking system that supports browsing from city down to a show, visual seat selection with timed holds, and fair, race-free booking when many users compete for the same seats.
Data Model
The browsing flow is a hierarchy that narrows down to a single show's seat map.
Requirements
The source splits requirements into functional (what the system does) and non-functional (how well it must do it) rather than basic-vs-bonus — kept as Part 1 and Part 2 here for consistency with the rest of this series.
Part 1 — Functional Requirements
Core functionality you must build and get working first.
The system must serve booking requests for contested seats in First In, First Out order — if two users race for the same seat, whoever's request was received first should win the hold, not whoever's request happens to be processed first by chance.
Part 2 — Non-Functional Requirements
These describe how the system must behave under load and failure, not new user-facing operations.
Example Usage: Full Walkthrough
The source doesn't specify an exact command syntax, so here's one reasonable walkthrough exercising the browsing flow, seat holding, and concurrent contention.
1. Browse from city down to a show
> listCities()
["Bengaluru", "Mumbai", "Delhi"]
> listMovies(city: "Bengaluru")
["Interstellar Returns", "The Last Byte"]
> listCinemasAndShows(movie: "Interstellar Returns", city: "Bengaluru")
[
{ cinema: "PVR Forum", shows: ["18:00", "21:30"] },
{ cinema: "INOX Mantri", shows: ["19:15"] },
]
2. View the seating layout for a chosen show
> getSeatLayout(showId: "pvr-1800")
Row A: [A1:available, A2:available, A3:booked, A4:available]
Row B: [B1:available, B2:booked, B3:available, B4:available]
3. Select seats — placed on hold
> holdSeats(showId: "pvr-1800", user: "alice", seats: ["A1", "A2"])
✅ Seats A1, A2 held for alice. Hold expires in 10 minutes.
4. A concurrent request for an overlapping seat is queued fairly
> holdSeats(showId: "pvr-1800", user: "bob", seats: ["A2", "A4"]) // arrives moments after alice's request
❌ Seat A2 is currently held by another user. Only A4 held for bob.
✅ Seat A4 held for bob. Hold expires in 10 minutes.
5. Alice pays via UPI and gets notified
> confirmBooking(showId: "pvr-1800", user: "alice", seats: ["A1", "A2"], payment: "UPI")
✅ Booking confirmed for alice: seats A1, A2 (paid via UPI).
📧 Email + WhatsApp sent to alice: booking confirmed for Interstellar Returns, PVR Forum, 18:00.
6. An expired hold releases its seats automatically
... 10 minutes pass with no confirmBooking call from bob for seat A4 ...
⏱ Hold on seat A4 for bob expired. Seat A4 is available again.
Alice's hold on A1/A2 blocks Bob from holding A2 concurrently — his request is resolved fairly rather than racing silently — and once Alice pays, those seats are permanently booked. Bob's own hold on A4, left unconfirmed, expires cleanly and returns that seat to the pool, demonstrating both the concurrency-fairness and timed-hold requirements together.