Parking Lot System: A Complete Low-Level Design Walkthrough for Machine Coding Interviews

This article is a focused revision guide. I’ll walk through the key design decisions, the class structure, the patterns used, and the edge cases that separate an average attempt from a great one.

The Problem at a Glance

Design a Parking Lot system that:

  • Has multiple floors, each with multiple spots
  • Supports different vehicle types : Motorcycle, Car, Truck
  • Has different spot sizes : Small, Medium, Large
  • Issues a ticket on entry and calculates a fee on exit
  • Tracks real-time availability of spots

Sounds manageable, right? The real challenge is doing it in a way that is clean, extensible, and doesn’t collapse into a single God class.

Step 1 : Identify the Entities

Before writing any code, pause and list the nouns in the problem. These become your classes.

  • Vehicle (Motorcycle, Car, Truck)
  • ParkingSpot (has a size, a floor, an availability status)
  • ParkingFloor (a collection of spots)
  • ParkingLot (the whole system of multiple floors, gates)
  • ParkingTicket (issued at entry, presented at exit)
  • EntryGate and ExitGate
  • FeeStrategy (how you charge)
  • SpotAllocationStrategy (how you assign spots)

Getting this list right in the first few minutes of an interview immediately signals structured thinking to the interviewer.

Step 2 : Model the Relationships

ParkingLot
├── has many ParkingFloors
│ └── each has many ParkingSpots
├── has EntryGates
└── has ExitGates
ParkingTicket
├── belongs to a Vehicle
└── points to a ParkingSpot

A ParkingTicket is the bridge between entry and exit. At entry you issue it; at exit you look it up, calculate the duration, charge the fee, and free the spot.

Step 3 : The Core Classes

Vehicle Hierarchy

public abstract class Vehicle {
private String licensePlate;
private VehicleType type;
public Vehicle(String licensePlate, VehicleType type) {
this.licensePlate = licensePlate;
this.type = type;
}
public VehicleType getType() { return type; }
public String getLicensePlate() { return licensePlate; }
}
public class Car extends Vehicle {
public Car(String licensePlate) {
super(licensePlate, VehicleType.CAR);
}
}

Keep it simple. The vehicle hierarchy exists primarily so vehicle type can be used to determine spot size. Don’t over-engineer it.

ParkingSpot

public class ParkingSpot {
private final String spotId;
private final SpotSize size;
private final int floorNumber;
private boolean isAvailable;
private Vehicle parkedVehicle;
public synchronized boolean assignVehicle(Vehicle vehicle) {
if (!isAvailable) return false;
this.parkedVehicle = vehicle;
this.isAvailable = false;
return true;
}
public synchronized void freeSpot() {
this.parkedVehicle = null;
this.isAvailable = true;
}
}

Notice the synchronized on mutation methods, spots are a shared resource and concurrent access is a real concern.

ParkingTicket

public class ParkingTicket {
private final String ticketId;
private final Vehicle vehicle;
private final ParkingSpot spot;
private final LocalDateTime entryTime;
private LocalDateTime exitTime;
private double fee;
// constructor, getters...
}

Tickets are immutable at creation (except for exit time and fee, populated at checkout). Never store just the vehicle plate. Store the entire ParkingSpot reference so the exit gate can free it directly.

ParkingLot : Singleton

public class ParkingLot {
private static ParkingLot instance;
private final List<ParkingFloor> floors;
private final Map<String, ParkingTicket> activeTickets;
private ParkingLot() {
floors = new ArrayList<>();
activeTickets = new ConcurrentHashMap<>();
}
public static synchronized ParkingLot getInstance() {
if (instance == null) instance = new ParkingLot();
return instance;
}
}

The Parking Lot is a natural Singleton, there’s only one lot. Use ConcurrentHashMap for the active tickets map if you’re supporting concurrent access.

Step 4 : Strategy Pattern for Allocation and Fee

This is where many candidates stumble. The temptation is to hardcode spot assignment logic inside EntryGate and fee logic inside ExitGate. Don’t.

Spot Allocation Strategy

public interface SpotAllocationStrategy {
Optional<ParkingSpot> allocate(List<ParkingFloor> floors, VehicleType type);
}
public class NearestFirstAllocationStrategy implements SpotAllocationStrategy {
@Override
public Optional<ParkingSpot> allocate(List<ParkingFloor> floors, VehicleType type) {
SpotSize required = getRequiredSize(type);
return floors.stream()
.flatMap(floor -> floor.getSpots().stream())
.filter(spot -> spot.isAvailable() && spot.getSize() == required)
.findFirst();
}
private SpotSize getRequiredSize(VehicleType type) {
return switch (type) {
case MOTORCYCLE -> SpotSize.SMALL;
case CAR -> SpotSize.MEDIUM;
case TRUCK -> SpotSize.LARGE;
};
}
}

Now if tomorrow you need a “Handicap Nearest” strategy or a “Load Balanced” strategy, you implement a new class. You don’t touch existing code. That’s the Open/Closed Principle in action.

Fee Strategy

public interface FeeStrategy {
double calculate(ParkingTicket ticket);
}
public class HourlyFeeStrategy implements FeeStrategy {
private final double ratePerHour;
public HourlyFeeStrategy(double ratePerHour) {
this.ratePerHour = ratePerHour;
}
@Override
public double calculate(ParkingTicket ticket) {
long minutes = Duration.between(ticket.getEntryTime(), LocalDateTime.now()).toMinutes();
long hours = (minutes / 60) + 1; // round up to nearest hour
return hours * ratePerHour;
}
}

You can easily swap in a DayRateFeeStrategy or a WeekendSurgeFeeStrategy without touching the exit gate at all.

Step 5 — Entry and Exit Gates

public class EntryGate {
private final SpotAllocationStrategy allocationStrategy;
public EntryGate(SpotAllocationStrategy allocationStrategy) {
this.allocationStrategy = allocationStrategy;
}
public ParkingTicket parkVehicle(Vehicle vehicle, List<ParkingFloor> floors) {
Optional<ParkingSpot> spot = allocationStrategy.allocate(floors, vehicle.getType());
if (spot.isEmpty()) throw new ParkingLotFullException("No spot available for " + vehicle.getType());
spot.get().assignVehicle(vehicle);
return new ParkingTicket(vehicle, spot.get());
}
}
public class ExitGate {
private final FeeStrategy feeStrategy;
public ExitGate(FeeStrategy feeStrategy) {
this.feeStrategy = feeStrategy;
}
public double processExit(ParkingTicket ticket) {
double fee = feeStrategy.calculate(ticket);
ticket.setFee(fee);
ticket.setExitTime(LocalDateTime.now());
ticket.getSpot().freeSpot();
return fee;
}
}

Each gate has a single responsibility. They depend on interfaces, not concrete implementations. This is dependency inversion at its cleanest.

Step 6 — Enums Over Magic Strings

Always model fixed sets of values as enums.

public enum VehicleType {
MOTORCYCLE, CAR, TRUCK
}
public enum SpotSize {
SMALL, MEDIUM, LARGE
}

Avoid strings like “car” or “medium” scattered across the codebase. Enums give you compile-time safety and enable switch expressions cleanly.

The Key Design Decisions — Quick Reference

Decision Choice Why ParkingLot lifecycle Singleton Only one lot exists Spot assignment Strategy interface Swap algorithms without changing gates Fee calculation Strategy interface New pricing models without touching exit logic Vehicle types Inheritance Common base, type-specific behaviour Thread safety synchronized + ConcurrentHashMap Spots are shared resources Ticket storage Map in ParkingLot O(1) lookup at exit

Edge Cases You Must Handle

1. Lot is full Throw a meaningful ParkingLotFullException rather than returning null. Null-returns are silent failures — exceptions are explicit.

2. Invalid ticket at exit Validate the ticket ID exists in the active tickets map before proceeding. Throw InvalidTicketException with the ticket ID in the message.

3. Vehicle already parked Track active license plates and reject a vehicle that’s already inside.

4. Zero-duration exit The fee calculation should handle 0 minutes gracefully round up to 1 hour minimum is a reasonable real-world rule.

5. Concurrent entry Two cars hitting Entry Gate 1 and Entry Gate 2 simultaneously could get assigned the same spot if assignVehicle isn’t synchronized.

What Interviewers Look For

Modelling clarity — Can you identify entities without being prompted? Can you articulate why a ParkingTicket exists as a separate class?

Separation of concerns — Does each class do one thing? Is the ParkingLot class clean, or is it a dumping ground for all logic?

Extensibility — If I ask you to add a new vehicle type mid-interview, can you do it without rewriting existing logic?

Real-world thinking — Do you bring up thread safety without being asked? Do you think about overflow scenarios?

Code quality — Are your names meaningful? Is there dead code? Are exceptions informative?

What to Do in the First 10 Minutes of the Interview

  1. Clarify scope — How many floors? Is there a display board? Payment modes? (Understand what’s in/out)
  2. List entities — Say them out loud: Vehicle, Spot, Floor, Ticket, Gate
  3. Sketch the class diagram — Even rough boxes and arrows show structured thinking
  4. Start with models, then services, then strategies — work bottom-up
  5. Mention thread safety — even if you don’t implement it, naming the problem earns points

Summary

The Parking Lot problem isn’t about memorising a template — it’s about demonstrating that you can take a vague real-world system and decompose it into clean, maintainable, extensible code under pressure. The Strategy pattern for fee and allocation, Singleton for the lot, proper ticket lifecycle management, and handling edge cases gracefully are the four things that elevate a solution from “decent” to “strong hire.”

Build it, extend it, break it with edge cases, then fix it. That’s the practice loop that actually prepares you.

Find the full source code on GitHub: Machine_Coding_Parking_Lot

Grab these resources: 🛒 Full Editions (use code FRIENDS20 for 20% off):

Grokking the Java Interview: link
Grokking the Spring Boot Interview: link
250+ Spring Professional Certification Practice Questions: link

🆓 Try before you buy — Free Sample Copies:

Grokking the Java Interview [Free Sample Copy]
Grokking the Spring Boot Interview [Free Sample Copy]
Spring Boot Certification Practice Questions [Free Sample Copy]


Parking Lot System: A Complete Low-Level Design Walkthrough for Machine Coding Interviews was originally published in Javarevisited on Medium, where people are continuing the conversation by highlighting and responding to this story.

This post first appeared on Read More