Skip to main content

Documentation Index

Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt

Use this file to discover all available pages before exploring further.

Parking Lot System Design
Difficulty: 🟢 Beginner | Time: 45 minutes | Patterns: Singleton, Strategy, Factory

🎯 Problem Statement

Design a parking lot system that can:
  • Handle multiple floors and different vehicle types
  • Track available and occupied spots
  • Calculate parking fees based on duration
  • Support multiple entry/exit points
Why This Problem? Parking Lot is the #1 most asked LLD problem because it tests OOP basics, relationships, and simple design patterns without being overwhelming. It is the “Hello World” of LLD — deceptively simple at first glance, but rich enough to reveal how you think about class hierarchies (Vehicle types), resource allocation (spot assignment), and extensibility (adding EV charging or monthly passes). Companies like Amazon, Google, and Microsoft use this problem because it scales naturally from a 20-minute warmup to a deep 60-minute design discussion.

📋 Step 1: Clarify Requirements

Interview Tip: ALWAYS ask clarifying questions before designing. Jumping straight to code is a red flag!

Questions to Ask the Interviewer

CategoryQuestionPossible Answer
VehiclesWhat types of vehicles do we support?Cars, motorcycles, trucks
SpotsAre spot sizes different per vehicle type?Yes - compact, regular, large
FloorsHow many floors? Multiple entry/exit?3 floors, 2 entries, 2 exits
PricingFixed rate or hourly? Different per vehicle?Hourly, varies by vehicle type
PaymentWhat payment methods?Cash, card, mobile
FeaturesReservations? Electric charging?Out of scope for now

Functional Requirements

  • Park vehicles of different types (car, motorcycle, truck)
  • Assign the nearest available spot to a vehicle
  • Calculate fees based on vehicle type and duration
  • Support multiple payment methods
  • Display available spots per floor

Non-Functional Requirements

  • Handle concurrent entry/exit (thread safety)
  • Fast spot lookup - O(1) for availability check
  • Scalable to multiple parking lots

🧩 Step 2: Identify Core Objects

Technique: List all nouns from requirements → potential classes. List all verbs → potential methods.

Vehicles

Car, Motorcycle, Truck

Parking Spots

Compact, Regular, Large

Infrastructure

ParkingLot, Floor, Entry/Exit

Entity-Responsibility Mapping

EntityResponsibilities
VehicleStore type, license plate; check if fits in spot
ParkingSpotTrack availability; assign/remove vehicles
FloorManage spots on one floor; find available spot
ParkingLotCoordinate all floors; issue tickets
ParkingTicketTrack entry time, spot; calculate fee
PaymentMethodProcess payment (Strategy pattern)

📐 Step 3: Class Diagram

┌─────────────────────────────────────────────────────────────────┐
│                     ParkingLot (Singleton)                      │
├─────────────────────────────────────────────────────────────────┤
│ - id: UUID                                                      │
│ - name: String                                                  │
│ - floors: List<Floor>                                           │
│ - activeTickets: Dict<String, ParkingTicket>                   │
├─────────────────────────────────────────────────────────────────┤
│ + parkVehicle(vehicle): ParkingTicket                          │
│ + unparkVehicle(ticketId): float                               │
│ + getAvailability(): List<FloorInfo>                           │
└─────────────────────────────────────────────────────────────────┘

                              │ 1..*

┌─────────────────────────────────────────────────────────────────┐
│                          Floor                                   │
├─────────────────────────────────────────────────────────────────┤
│ - floorNumber: int                                              │
│ - spots: Dict[SpotType, List<ParkingSpot>]                     │
├─────────────────────────────────────────────────────────────────┤
│ + findAvailableSpot(vehicle): ParkingSpot                      │
│ + getAvailableCount(spotType): int                             │
└─────────────────────────────────────────────────────────────────┘

                              │ 1..*

┌─────────────────────────────────────────────────────────────────┐
│                       ParkingSpot                                │
├─────────────────────────────────────────────────────────────────┤
│ - id: String                                                    │
│ - spotType: SpotType (COMPACT/REGULAR/LARGE)                   │
│ - isAvailable: bool                                             │
│ - vehicle: Vehicle                                              │
├─────────────────────────────────────────────────────────────────┤
│ + canFit(vehicle): bool                                         │
│ + assignVehicle(vehicle): bool                                  │
│ + removeVehicle(): Vehicle                                      │
└─────────────────────────────────────────────────────────────────┘

┌───────────────────────┐       ┌───────────────────────────┐
│      <<abstract>>     │       │      ParkingTicket        │
│        Vehicle        │       ├───────────────────────────┤
├───────────────────────┤       │ - id: String              │
│ - licensePlate: str   │       │ - licensePlate: String    │
│ - vehicleType: enum   │       │ - spotId: String          │
├───────────────────────┤       │ - entryTime: DateTime     │
│ + canFitIn(spot): bool│       │ - exitTime: DateTime      │
└───────────────────────┘       ├───────────────────────────┤
         △                      │ + calculateFee(): float   │
    ┌────┼────┐                 └───────────────────────────┘
    │    │    │
┌───┴┐ ┌─┴─┐ ┌┴────┐
│Car │ │Moto│ │Truck│
└────┘ └───┘ └─────┘

💻 Step 4: Implementation

Enums and Constants

from enum import Enum
from datetime import datetime
from typing import Optional, Dict, List
from dataclasses import dataclass
import uuid

class VehicleType(Enum):
    MOTORCYCLE = 1
    CAR = 2
    TRUCK = 3

class SpotType(Enum):
    COMPACT = 1     # Fits motorcycle, car
    REGULAR = 2     # Fits motorcycle, car
    LARGE = 3       # Fits all vehicles

class TicketStatus(Enum):
    ACTIVE = 1
    PAID = 2
    LOST = 3

# Pricing per hour
HOURLY_RATES = {
    VehicleType.MOTORCYCLE: 1.0,
    VehicleType.CAR: 2.0,
    VehicleType.TRUCK: 4.0
}

Vehicle Classes

class Vehicle:
    def __init__(self, license_plate: str, vehicle_type: VehicleType):
        self.license_plate = license_plate
        self.vehicle_type = vehicle_type
    
    def can_fit_in(self, spot_type: SpotType) -> bool:
        """Check if vehicle fits in spot type -- Polymorphism: each subclass defines its own fitting logic"""
        raise NotImplementedError

class Motorcycle(Vehicle):
    def __init__(self, license_plate: str):
        super().__init__(license_plate, VehicleType.MOTORCYCLE)
    
    def can_fit_in(self, spot_type: SpotType) -> bool:
        return True  # Motorcycle fits anywhere

class Car(Vehicle):
    def __init__(self, license_plate: str):
        super().__init__(license_plate, VehicleType.CAR)
    
    def can_fit_in(self, spot_type: SpotType) -> bool:
        return spot_type in [SpotType.COMPACT, SpotType.REGULAR, SpotType.LARGE]

class Truck(Vehicle):
    def __init__(self, license_plate: str):
        super().__init__(license_plate, VehicleType.TRUCK)
    
    def can_fit_in(self, spot_type: SpotType) -> bool:
        return spot_type == SpotType.LARGE  # Only large spots

Parking Spot

class ParkingSpot:
    def __init__(self, spot_id: str, floor_number: int, spot_type: SpotType):
        self.id = spot_id
        self.floor_number = floor_number
        self.spot_type = spot_type
        self._vehicle: Optional[Vehicle] = None
        self._is_available = True
    
    @property
    def is_available(self) -> bool:
        return self._is_available
    
    def can_fit(self, vehicle: Vehicle) -> bool:
        """Check if vehicle can park here"""
        return self._is_available and vehicle.can_fit_in(self.spot_type)
    
    def assign_vehicle(self, vehicle: Vehicle) -> bool:
        """Park a vehicle in this spot"""
        if not self.can_fit(vehicle):
            return False
        
        self._vehicle = vehicle
        self._is_available = False
        return True
    
    def remove_vehicle(self) -> Optional[Vehicle]:
        """Remove vehicle from spot"""
        vehicle = self._vehicle
        self._vehicle = None
        self._is_available = True
        return vehicle

Parking Ticket

@dataclass
class ParkingTicket:
    id: str
    license_plate: str
    spot_id: str
    floor_number: int
    entry_time: datetime
    exit_time: Optional[datetime] = None
    status: TicketStatus = TicketStatus.ACTIVE
    
    @staticmethod
    def create(vehicle: Vehicle, spot: ParkingSpot) -> 'ParkingTicket':
        return ParkingTicket(
            id=str(uuid.uuid4()),
            license_plate=vehicle.license_plate,
            spot_id=spot.id,
            floor_number=spot.floor_number,
            entry_time=datetime.now()
        )
    
    def calculate_fee(self, vehicle_type: VehicleType) -> float:
        """Calculate parking fee based on duration"""
        if self.exit_time is None:
            self.exit_time = datetime.now()
        
        duration = self.exit_time - self.entry_time
        hours = max(1, int(duration.total_seconds() / 3600) + 1)  # Round up
        
        return hours * HOURLY_RATES[vehicle_type]

Floor

class Floor:
    def __init__(self, floor_number: int):
        self.floor_number = floor_number
        self.spots: Dict[SpotType, List[ParkingSpot]] = {
            SpotType.COMPACT: [],
            SpotType.REGULAR: [],
            SpotType.LARGE: []
        }
    
    def add_spot(self, spot: ParkingSpot):
        """Add a parking spot to this floor"""
        self.spots[spot.spot_type].append(spot)
    
    def get_available_count(self, spot_type: SpotType) -> int:
        """Get count of available spots of given type"""
        return sum(1 for spot in self.spots[spot_type] if spot.is_available)
    
    def find_available_spot(self, vehicle: Vehicle) -> Optional[ParkingSpot]:
        """Find first available spot that fits the vehicle"""
        # Try to find the smallest suitable spot
        for spot_type in SpotType:
            for spot in self.spots[spot_type]:
                if spot.can_fit(vehicle):
                    return spot
        return None
    
    def get_display_info(self) -> dict:
        """Get floor availability for display"""
        return {
            "floor": self.floor_number,
            "compact_available": self.get_available_count(SpotType.COMPACT),
            "regular_available": self.get_available_count(SpotType.REGULAR),
            "large_available": self.get_available_count(SpotType.LARGE)
        }

Parking Lot

import threading

class ParkingLot:
    _instance = None
    _lock = threading.Lock()
    
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self, name: str, num_floors: int):
        if hasattr(self, '_initialized'):
            return
        
        self.name = name
        self.floors: List[Floor] = []
        self.active_tickets: Dict[str, ParkingTicket] = {}  # ticket_id -> ticket
        self.vehicle_to_spot: Dict[str, ParkingSpot] = {}   # license -> spot
        self._lock = threading.Lock()
        self._initialized = True
        
        # Initialize floors
        for i in range(num_floors):
            self.floors.append(Floor(i + 1))
    
    def add_spots(self, floor_number: int, spot_type: SpotType, count: int):
        """Add parking spots to a floor"""
        floor = self.floors[floor_number - 1]
        for i in range(count):
            spot_id = f"F{floor_number}-{spot_type.name[0]}{i+1}"
            spot = ParkingSpot(spot_id, floor_number, spot_type)
            floor.add_spot(spot)
    
    def park_vehicle(self, vehicle: Vehicle) -> Optional[ParkingTicket]:
        """Park a vehicle and return ticket"""
        with self._lock:
            # Check if vehicle already parked
            if vehicle.license_plate in self.vehicle_to_spot:
                raise ValueError("Vehicle already parked")
            
            # Find available spot
            spot = self._find_available_spot(vehicle)
            if spot is None:
                return None  # No spot available
            
            # Assign vehicle to spot
            spot.assign_vehicle(vehicle)
            
            # Create ticket
            ticket = ParkingTicket.create(vehicle, spot)
            self.active_tickets[ticket.id] = ticket
            self.vehicle_to_spot[vehicle.license_plate] = spot
            
            return ticket
    
    def unpark_vehicle(self, ticket_id: str) -> Optional[float]:
        """Unpark vehicle and return fee"""
        with self._lock:
            ticket = self.active_tickets.get(ticket_id)
            if ticket is None or ticket.status != TicketStatus.ACTIVE:
                return None
            
            # Get spot and vehicle
            spot = self.vehicle_to_spot.get(ticket.license_plate)
            if spot is None:
                return None
            
            vehicle = spot.remove_vehicle()
            
            # Calculate fee
            fee = ticket.calculate_fee(vehicle.vehicle_type)
            ticket.status = TicketStatus.PAID
            
            # Cleanup
            del self.vehicle_to_spot[ticket.license_plate]
            
            return fee
    
    def _find_available_spot(self, vehicle: Vehicle) -> Optional[ParkingSpot]:
        """Find nearest available spot for vehicle"""
        for floor in self.floors:
            spot = floor.find_available_spot(vehicle)
            if spot:
                return spot
        return None
    
    def get_availability(self) -> List[dict]:
        """Get availability info for all floors"""
        return [floor.get_display_info() for floor in self.floors]

▶️ Step 5: Usage Example

# Create parking lot
parking_lot = ParkingLot("Downtown Parking", num_floors=3)

# Add spots to each floor
for floor in range(1, 4):
    parking_lot.add_spots(floor, SpotType.COMPACT, 10)
    parking_lot.add_spots(floor, SpotType.REGULAR, 20)
    parking_lot.add_spots(floor, SpotType.LARGE, 5)

# Park vehicles
car = Car("ABC-123")
motorcycle = Motorcycle("XYZ-789")
truck = Truck("TRK-001")

ticket1 = parking_lot.park_vehicle(car)
print(f"Car parked at: {ticket1.spot_id}")

ticket2 = parking_lot.park_vehicle(motorcycle)
print(f"Motorcycle parked at: {ticket2.spot_id}")

# Check availability
print(parking_lot.get_availability())

# Unpark and pay
import time
time.sleep(2)  # Simulate parking duration

processor = PaymentProcessor()
processor.process_payment(ticket1.id, parking_lot, CashPayment())

🎯 Key Design Decisions

In most real-world scenarios, there’s only one parking lot instance per location. Singleton ensures:
  • Consistent state across all entry/exit panels
  • No duplicate instances wasting memory
  • Single point of control for all operations
Trade-off: Makes unit testing harder. Consider dependency injection for testability.
Allows adding new payment methods (Apple Pay, UPI, Crypto) without modifying existing code:
  • Each payment method encapsulates its own logic
  • Easy to A/B test payment methods
  • Follows Open/Closed Principle
Alternative: Could use Factory pattern if payment logic is simpler.
Multiple entry/exit panels can operate simultaneously:
  • Prevents race conditions when assigning spots
  • Ensures ticket consistency
  • Handles concurrent unpark operations
Trade-off: Slight performance hit. Could use read-write locks for better read performance.
Using inheritance for vehicles provides:
  • Polymorphic behavior for canFitIn() method
  • Easy addition of new vehicle types
  • Type-safe spot assignment
Alternative: Could use composition with VehicleType enum for simpler cases.

🚀 Interview Extensions

Interviewer Favorite: “How would you extend this design to support…”
class ReservedSpot(ParkingSpot):
    def __init__(self, spot_id, floor_number, spot_type, reserved_for: str):
        super().__init__(spot_id, floor_number, spot_type)
        self.reserved_for = reserved_for  # License plate
    
    def can_fit(self, vehicle: Vehicle) -> bool:
        return (super().can_fit(vehicle) and 
                vehicle.license_plate == self.reserved_for)
class EVChargingSpot(ParkingSpot):
    def __init__(self, spot_id, floor_number, charger_type: str):
        super().__init__(spot_id, floor_number, SpotType.REGULAR)
        self.charger_type = charger_type  # "Level2", "DC Fast"
        self.charging_rate_per_kwh = 0.15
    
    def calculate_charging_fee(self, kwh_used: float) -> float:
        return kwh_used * self.charging_rate_per_kwh
class MonthlyPass:
    def __init__(self, holder_license: str, spot_type: SpotType):
        self.holder_license = holder_license
        self.spot_type = spot_type
        self.valid_until = datetime.now() + timedelta(days=30)
    
    def is_valid(self) -> bool:
        return datetime.now() < self.valid_until

# Modify fee calculation
def calculate_fee(self, vehicle_type: VehicleType, has_pass: bool) -> float:
    if has_pass:
        return 0.0
    # ... regular calculation
def handle_lost_ticket(self, license_plate: str) -> float:
    """Charge maximum daily rate for lost tickets"""
    spot = self.vehicle_to_spot.get(license_plate)
    if not spot:
        raise ValueError("Vehicle not found in parking lot")
    
    vehicle = spot._vehicle
    max_hours = 24
    return max_hours * HOURLY_RATES[vehicle.vehicle_type]
# Add validation layers
class ParkingValidator:
    def validate_entry(self, vehicle: Vehicle) -> bool:
        # Check if vehicle is banned
        # Check if license plate is valid
        # Check if lot has capacity
        pass
    
    def validate_exit(self, ticket: ParkingTicket) -> bool:
        # Verify ticket authenticity
        # Check for payment status
        pass

📊 Complexity Analysis

OperationTime ComplexitySpace Complexity
parkVehicle()O(F × S)O(1)
unparkVehicle()O(1)O(1)
getAvailability()O(F)O(F)
findAvailableSpot()O(F × S)O(1)
Where F = number of floors, S = spots per floor
Optimization: Use a heap/priority queue per floor for O(log S) spot finding!

✅ What Makes This Design Good?

PrincipleHow It’s Applied
Single ResponsibilityEach class has one job: Vehicle stores data, Spot manages allocation, Floor coordinates
Open/ClosedAdd new payment methods without changing existing code
Liskov SubstitutionCar, Motorcycle, Truck all substitute for Vehicle
Interface SegregationPaymentMethod is focused, not a fat interface
Dependency InversionParkingLot depends on abstract Vehicle, not concrete types

Interview Deep-Dive Questions

Strong answer:
  • The Singleton guarantees a single source of truth for lot state — every entry gate, exit gate, and display panel references the same instance, which prevents phantom availability where two gates think the same spot is free.
  • However, Singleton is one of the most over-applied patterns in LLD. The moment your company opens a second location, or you need to run integration tests in parallel, Singleton fights you. Each test shares mutable global state, so tests become order-dependent and flaky.
  • In production I would keep the single-instance behavior but achieve it through dependency injection and a composition root, not by baking it into __new__. Frameworks like Spring or Guice give you singleton scope without coupling your class to the pattern.
  • The real trade-off is testability vs. convenience. Singleton saves you from passing the instance around, but it hides a dependency, making the call graph harder to reason about and mock.
  • Example: Imagine running 200 unit tests that all hit ParkingLot._instance. One test parks a truck, the next test expects an empty lot — now you need manual teardown or a reset() method, which itself is a code smell.
Red flag answer: “Singleton is the standard pattern for this problem” without discussing any downsides, or saying “it prevents multiple objects” without explaining why multiple objects would be harmful.Follow-ups:
  1. If you replaced Singleton with dependency injection, walk me through how the entry gate, exit gate, and display panel would all receive the same ParkingLot instance.
  2. How would you redesign this if the system needed to manage a chain of 50 parking lots across a city, each with independent state but centralized reporting?
Strong answer:
  • A single coarse-grained lock means every park and unpark operation is serialized globally. If you have 4 entry gates and 4 exit gates, only one operation happens at a time — that is a throughput bottleneck when the morning rush hits and 200 cars arrive in 10 minutes.
  • The first optimization is floor-level locking: each Floor gets its own lock. Two cars parking on different floors never contend. This alone can give you roughly N-times throughput where N is the number of floors.
  • Going further, you could use spot-level compare-and-swap (CAS) with an atomic boolean for is_available. The spot assignment becomes lock-free for the common case: if spot.is_available.compare_and_set(True, False) succeeds atomically.
  • You still need a lock (or optimistic concurrency) for the vehicle_to_spot and active_tickets dictionaries, but those are short critical sections. A ConcurrentHashMap (Java) or threading.Lock scoped only to dict mutation keeps contention minimal.
  • The gotcha most people miss: the _find_available_spot scan itself is a read path. If you lock during the scan, you block everyone. If you don’t lock, you get a TOCTOU race — a spot looks free during scan, but by the time you assign, someone else took it. The fix is optimistic assignment: scan without locking, attempt assignment with a CAS, and retry on failure.
Red flag answer: “Just use a lock” without identifying the throughput problem, or proposing removing the lock entirely without addressing race conditions.Follow-ups:
  1. Walk me through the exact race condition that occurs if two threads simultaneously find the same spot available and both try to assign their vehicle.
  2. If you introduced floor-level locks, what happens when a car’s preferred floor is full and it cascades to the next floor — do you hold both locks simultaneously, and what deadlock risk does that introduce?
Strong answer:
  • This is a classic atomicity gap. The current code calls unpark_vehicle which marks the ticket as PAID and frees the spot before payment actually succeeds. If payment_method.process() returns False (card declined, network timeout), the vehicle is already unparked, the spot is freed, and the system has no record that money is owed.
  • The fix is to separate the concerns into phases: calculate fee (read-only), collect payment (external call), release vehicle (state mutation). Only after payment confirmation do you mutate state.
  • In practice I would introduce a TicketStatus.PENDING_PAYMENT state. The flow becomes: (1) calculate fee and set status to PENDING_PAYMENT, (2) call payment gateway, (3) on success set PAID and free spot, (4) on failure revert to ACTIVE and keep the gate closed.
  • For idempotency, attach a unique payment reference ID to each attempt so retries do not double-charge. This matters because payment gateways can return ambiguous failures — the charge may have gone through but the response timed out.
  • Example: Stripe recommends exactly this pattern — create a PaymentIntent, confirm it, and only fulfill (open the gate) on the payment_intent.succeeded webhook, not on the synchronous API response.
Red flag answer: “Payment always succeeds” or not recognizing the ordering problem at all. Also a red flag: suggesting to wrap both operations in a database transaction without acknowledging that the payment gateway call is an external side effect that cannot be rolled back.Follow-ups:
  1. What if the payment succeeds but your system crashes before marking the ticket as PAID — how do you prevent the customer from being charged but unable to leave?
  2. How would you design a reconciliation process that detects and resolves mismatches between your parking system state and the payment gateway’s records?
Strong answer:
  • Looking at the code, Car.can_fit_in returns True for COMPACT, REGULAR, and LARGE. But COMPACT spots are semantically meant for motorcycles and small vehicles. In a real parking lot, a standard sedan does not fit in a compact motorcycle spot. This is a domain modeling error — the naming suggests size tiers, but the fitting logic does not enforce them consistently.
  • The deeper design smell is that fitting logic is scattered across vehicle subclasses instead of being centralized. Each Vehicle subclass hardcodes which SpotTypes it fits, which means adding a new SpotType (e.g., HANDICAPPED, EV_ONLY) requires modifying every Vehicle subclass — a direct violation of the Open/Closed Principle.
  • A cleaner approach is a compatibility matrix or a size-based comparison: assign a numeric size to each VehicleType and each SpotType, then a vehicle fits if vehicle.size <= spot.size. This reduces the fitting logic to a single comparison and makes it trivially extensible.
  • Alternatively, you could use a FittingStrategy that encapsulates the rules and can be swapped or configured per parking lot — some lots may allow cars in compact spots (tight urban garages in Tokyo) while others do not.
  • Example: If the lot owner adds an EV_CHARGING spot type, with the current design you must add a case to Car, Motorcycle, and Truck. With a compatibility matrix, you add one row to a config table.
Red flag answer: “The inheritance hierarchy is correct because it uses polymorphism” without examining whether the actual fitting logic matches domain reality. Also a red flag: not recognizing the Open/Closed violation.Follow-ups:
  1. How would you refactor the fitting logic so that adding a new VehicleType or SpotType requires zero changes to existing classes?
  2. In the current design, can_fit_in lives on Vehicle and can_fit lives on ParkingSpot and both check compatibility — who should own this decision, and what is the risk of having it in two places?
Strong answer:
  • The current implementation is O(F x S) where F is floors and S is spots per floor. For a 10-floor garage with 200 spots per floor, that is 2,000 iterations per park operation. Under the coarse lock, this means every car waits while the system scans 2,000 spots.
  • Approach 1: Min-heap per spot type per floor. Maintain a heap of available spots keyed by spot ID (or proximity to entrance). park is O(log S) to pop the best spot; unpark is O(log S) to push the freed spot back. This is the classic answer and it works well.
  • Approach 2: Free-list with O(1) allocation. Maintain a set (or deque) of available spot IDs per (floor, spot_type). Park pops from the set in O(1), unpark pushes back in O(1). You lose ordering (nearest spot), but gain constant time.
  • Approach 3: Bitmap per floor. Each floor has a bitfield where bit i represents spot i. Finding the first free spot is a single ffs (find-first-set) CPU instruction on a 64-bit word, giving O(S/64) which is effectively O(1) for practical sizes.
  • The right choice depends on whether “nearest spot” matters. If drivers just want any spot quickly, the free-list wins. If the system should minimize walk distance, the heap wins.
  • Example: Airport garages use the free-list approach — they do not care which specific spot you get, they just need to know “are there any spots left on level 3?” and assign one instantly. Shopping malls with “nearest to elevator” preference would use a heap.
Red flag answer: “Use a HashMap” without explaining what the key and value are or how it improves the scan. Also a red flag: suggesting a database index without considering that this is an in-memory data structure problem.Follow-ups:
  1. If you use a min-heap and two cars arrive simultaneously, how do you prevent both from being assigned the same “best” spot?
  2. The requirement says “assign the nearest available spot” — nearest to what? How does the definition of nearest change the data structure choice?
Strong answer:
  • This is a direct test of the Open/Closed Principle. The answer should demonstrate extension through new classes and composition, not by editing Vehicle, ParkingSpot, or ParkingLot.
  • Step 1: Create EVChargingSpot extending ParkingSpot with additional fields: charger_type (Level 2, DC Fast), charging_rate_per_kwh, is_charging. Override can_fit to optionally check for EV capability on the vehicle.
  • Step 2: Create an EVVehicle mixin or interface (e.g., Chargeable) that Car or a new ElectricCar class implements. This avoids modifying the existing Car class — you create ElectricCar(Car, Chargeable) using multiple inheritance or composition.
  • Step 3: The fee calculation needs to account for charging. Instead of modifying ParkingTicket.calculate_fee, use a Decorator pattern: ChargingFeeDecorator wraps the base fee calculation and adds kwh_used * rate. Or use a FeeStrategy that the ticket delegates to.
  • Step 4: The spot finder in Floor needs to route EVs to charging spots preferentially. This is where the current design creaks — find_available_spot is tightly coupled to SpotType enum iteration order. A cleaner approach is a SpotAllocationStrategy that can be configured per vehicle type.
  • The honest caveat: In practice, you usually do need to touch the SpotType enum to add EV_CHARGING, unless you modeled spot capabilities as a set of tags from the start (e.g., capabilities: Set[str] = {"ev_charging", "covered", "handicapped"}). The tag-based approach is more extensible but adds complexity upfront.
Red flag answer: “Just add an ev_charging boolean to ParkingSpot” — this modifies an existing class for every new feature. Also a red flag: not mentioning fee calculation changes, which shows the candidate only thinks about the “happy path” of parking, not the full lifecycle.Follow-ups:
  1. If EV charging spots can also be used by non-EV vehicles when the lot is full, how do you prioritize EVs without starving regular cars?
  2. Charging sessions can outlast parking sessions — a car might finish charging in 2 hours but stay parked for 8. How does this affect your ticket and billing model?
Strong answer:
  • With the current coarse lock on park_vehicle, this is actually handled correctly but suboptimally. Thread 1 acquires the lock, finds the spot, assigns it, creates a ticket. Threads 2 and 3 block on the lock. When they eventually acquire it, _find_available_spot returns None and they get back a None ticket, meaning “lot full.”
  • The problem is user experience. The driver at gate 2 waits for threads 1’s full park operation (spot scan + assignment + ticket creation) before being told “sorry, no spots.” A better design would have a fast availability check outside the lock — a simple atomic counter: if available_count.get() <= 0: return None before even trying to acquire the lock. This turns away cars in O(1) when the lot is full.
  • There is also a fairness question. The current threading.Lock in Python (CPython) does not guarantee FIFO ordering. Thread 3 might acquire the lock before Thread 2 even though Thread 2 arrived first. If fairness matters (it does at physical gates where drivers see each other), you need a queue.Queue of pending requests processed in order.
  • At the physical level, a real system would have gate sensors and a reservation protocol: the gate sensor detects the car, reserves a spot tentatively via an API call, and only lifts the barrier on confirmation. If the reservation fails, the display shows “FULL” and the barrier stays down — no lock contention at the software level because the gate controller serializes per-lane.
  • Example: Costco parking lots use induction loop sensors at each entrance. The central system decrements a counter on entry and increments on exit. When the counter hits zero, all entrance displays flip to “LOT FULL” simultaneously — no per-spot scanning needed.
Red flag answer: “The lock handles it” without discussing throughput, fairness, or the user experience of blocking at a physical gate. Also a red flag: suggesting removing the lock to allow all three through.Follow-ups:
  1. What if you want to support “soft reservations” — a driver gets assigned a spot via a mobile app 5 minutes before arrival. How do you handle the TTL on that reservation if they never show up?
  2. The atomic counter approach can drift if a crash happens between decrementing the counter and completing the park operation. How do you keep the counter consistent?

ATM System

Similar State management

Hotel Booking

Similar slot allocation

Elevator System

Similar floor management