Design a Parking Lot
Learning Objectives#
- Model a real-world system whose rules live in the domain, not in a framework: spot fitting, ticketing, and fee calculation.
- See why composition and plain methods beat a tree of
Vehiclesubclasses for this problem. - Practice the seam placement that lets a follow-up question ("add hourly vs daily pricing") become a small, local change.
Introduction#
The parking lot is the opening act of LLD interviews for a reason. It has enough entities to feel like a system, enough rules to force real decisions, and no tricky concurrency to hide behind. Every software engineer who interviews for a backend role will get this question, or its sibling (the elevator, the vending machine), within their first few loops. Interviewers ask it because it separates people who draw nouns from people who trace verbs. A parking lot does not do anything until a car moves through it, and the whole design hangs off how you model that movement.
Requirements Gathering#
Functional requirements:
- The lot has multiple floors, and each floor has multiple spots of different sizes: compact, large, and accessible.
- A vehicle enters, is assigned a spot it fits in, and is issued a ticket with the entry time.
- A vehicle exits, the fee is computed from the time spent, payment is collected, and the spot is freed.
- Different vehicle types (car, truck) must be checked against spot size.
- The lot should track how many free spots are available per floor.
Non-functional requirements:
- Operations are fast enough that entry and exit gates do not form queues; a lookup and a few field updates per vehicle.
- The design should allow new vehicle types and new pricing schemes without rewriting the core flow.
Assumptions to state out loud: no reservation system, vehicles are not assigned to specific pre-booked spots; the lot is single-entry single-exit so we never handle a car that entered and never left; pricing is time-based only, no lost-ticket or monthly-pass scenarios. An interviewer expects you to cut these. If you try to design reservations, multiple exits, and monthly passes in 45 minutes, you will deliver nothing.
Identifying Core Entities#
The nouns that survive scrutiny are few.
| Entity | One-line responsibility |
|---|---|
ParkingLot | Owns floors, issues tickets at entry, settles them at exit. |
ParkingFloor | Owns a set of spots and answers "can you fit this vehicle?" |
ParkingSpot | A single slot with a size and an occupied flag. |
Vehicle | What enters; carries a type that determines which spot fits. |
Ticket | The contract between entry and exit; holds the spot and the entry time. |
Notice what is not on this list. There is no User, no Gate, no Payment. The gate is an implementation detail of how entry and exit happen, not a core entity; the fee logic can live on the ticket or in a small helper. Keep the noun list short and every noun load-bearing.
Class Design#
The design centers on one flow: enter and exit. Everything else hangs off it.
Vehicle is the entry point of the whole story, so it is the one place a small hierarchy earns its keep. But keep it to an enum that drives fitting logic, not a class hierarchy with ten subclasses. The interviewer's next question is always "how does a truck park?" and the cleanest answer is "the spot-finding logic checks size compatibility."
ParkingSpot is a dumb data holder with a single rule: it can take a vehicle if the vehicle's size fits and the spot is free.
ParkingFloor answers the allocation question. The naive implementation loops over spots, which is fine at this scale. If the interviewer pushes on scale, that is the moment to mention a TreeMap<Integer, List<ParkingSpot>> keyed by size so a truck only ever scans the spots that can hold it. Do not build that map preemptively; say the loop is fine and show you know where the optimization goes.
Ticket carries the entry time and the spot. The fee computation belongs here, as a method, because the ticket already has everything the fee needs: the entry time and, implicitly, the current time. A ParkingFeeService with a strategy interface is defensible but probably premature; the method version is honest.
ParkingLot is the facade every external actor talks to. Entry assigns the vehicle a spot, issues a ticket, and updates floor counts. Exit computes the fee, frees the spot, and settles the ticket.
Diagram: the two flows the whole design hangs off, plus the single fitting rule that decides whether a vehicle can use a spot.
This is a complete system in about 120 lines. That is the target. A parking lot design that needs 400 lines is a design that lost the plot.
Design Patterns Used#
The one pattern that genuinely fits is the Strategy pattern, placed at the pricing seam. The question to ask is not "which patterns can I name?" but "where will the interviewer push?" On a parking lot, the push is almost always pricing. If you have extracted an interface at that seam, the follow-up "add a weekend rate" is a new implementation and a wiring change. So the honest answer here is: one strategy seam at pricing, and no other patterns. No Observer, no Factory, no State machine. The State pattern for "occupied vs free" spots is overkill; a boolean plus two methods does the same job in a quarter of the code, and the interviewer will not be impressed by a pattern you cannot justify when they ask why it is better than the boolean.
Handling Edge Cases / Concurrency#
A basic parking lot has almost no interesting concurrency, and the honest answer is to say so. The genuinely sharp edge is the exit path: what if the same ticket is scanned twice, or a spot is freed twice? The paid flag and the activeTickets map guard against the double-settle. In a real deployment with multiple entry gates you would need locking on spot assignment so two cars are not handed the same spot, and that is the point where you would mention synchronized, an AtomicBoolean per spot, or a database row lock in a real system. In the interview, name the race ("two concurrent enter calls could pick the same free spot") and the fix ("synchronize the find-and-assign so the check and the update are atomic"), and you have shown more depth than most candidates ever reach on this problem.
Common Mistakes#
The classic failure is the Vehicle class hierarchy. Truck extends Vehicle, Bus extends Vehicle, ElectricVehicle extends Vehicle, and suddenly a parking lot has an inheritance tree that is doing no work. Size compatibility is a number; an enum carries it. Every subclass you add forces a decision somewhere, and none of those decisions exist in the requirements.
The second mistake is putting the fee logic in ParkingLot. When the interviewer asks "add a weekly flat rate," the candidate discovers the lot now has a pricing rule smeared across the class. The ticket's fee method, or a small strategy behind it, keeps the pricing rule local to the thing that knows the times.
The third mistake is ignoring the exit path. Candidates design a gorgeous entry flow and then a five-line exit. The exit is where the money is, literally. It computes the fee, it frees the spot, it settles the ticket, it handles the missing-ticket case. Shortchanging it reads as not having finished the job.
Interview Perspective#
A weak answer draws ParkingLot, then ParkingLotFloor, then ParkingLotFloorRow, then ParkingSpot with four subtypes, then a visitor for payments, and cannot park a car. The classes are fine nouns, but the verbs are missing and there is no walkthrough.
A strong answer says "here is how a car enters and here is how it leaves," and the classes visibly support both. When the interviewer says "what if two cars enter at the same time," the strong candidate points at the find-and-assign loop and names the race without being told. When the interviewer says "add weekend pricing," the strong candidate points at the one seam. Follow-up twists are standard: multi-level (already handled by floors), reserved spots (add a Reservable flag and check it in canFit), different rates per vehicle type (pass a per-type rate into the fee computation or map it in the pricing strategy). Each twist should land as a small, local change.
Knowledge Check#
- A truck enters a lot where every
LARGEspot is full but plenty ofCOMPACTspots are free. Trace exactly which methods run and what each returns. - Two cars approach two entry gates at the same moment and the only remaining large spot is the same one. Where is the race, and what is the minimal fix that keeps find-and-assign atomic?
- The business adds a rule: weekdays are hourly, weekends are a flat daily rate. Where does this rule live in the design given, and why is that location better than putting it in
ParkingLot.exit?
Key Takeaways#
- Keep the entity list short: lot, floor, spot, vehicle, ticket. Every noun must be load-bearing.
- Model vehicle size as an enum, not an inheritance tree.
- Give
Ticketthe fee method; the ticket is the only object that knows the entry time. - One strategy seam at pricing, nothing else. Most pattern-chasing on this problem is wasted motion.
- Walk both flows end to end. Entry is half the system; exit is the other half, and it is the half with the money.
What's Next#
The parking lot taught you the shape of a classic system: a small set of entities, one seam, two flows. The elevator throws away the "one seam" comfort and forces you to think about a controller that makes decisions, which changes everything about how you split responsibility.