> ## 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.

# Case Study: Parking Lot System

> Complete low-level design for a multi-level parking lot system

<Frame>
  <img src="https://mintcdn.com/devweeekends/sTu6A4whRFPJo0_g/images/LLD/case-parking-lot.svg?fit=max&auto=format&n=sTu6A4whRFPJo0_g&q=85&s=610087d51cdb8f443bbd4ec1d6bac8b7" alt="Parking Lot System Design" width="1080" height="1080" data-path="images/LLD/case-parking-lot.svg" />
</Frame>

<Info>
  **Difficulty**: 🟢 Beginner | **Time**: 45 minutes | **Patterns**: Singleton, Strategy, Factory
</Info>

## 🎯 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

<Tip>
  **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.
</Tip>

***

## 📋 Step 1: Clarify Requirements

<Warning>
  **Interview Tip**: ALWAYS ask clarifying questions before designing. Jumping straight to code is a red flag!
</Warning>

### Questions to Ask the Interviewer

| Category     | Question                                     | Possible Answer                |
| ------------ | -------------------------------------------- | ------------------------------ |
| **Vehicles** | What types of vehicles do we support?        | Cars, motorcycles, trucks      |
| **Spots**    | Are spot sizes different per vehicle type?   | Yes - compact, regular, large  |
| **Floors**   | How many floors? Multiple entry/exit?        | 3 floors, 2 entries, 2 exits   |
| **Pricing**  | Fixed rate or hourly? Different per vehicle? | Hourly, varies by vehicle type |
| **Payment**  | What payment methods?                        | Cash, card, mobile             |
| **Features** | Reservations? 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

<Note>
  **Technique**: List all nouns from requirements → potential classes. List all verbs → potential methods.
</Note>

<CardGroup cols={3}>
  <Card title="Vehicles" icon="car">
    Car, Motorcycle, Truck
  </Card>

  <Card title="Parking Spots" icon="square">
    Compact, Regular, Large
  </Card>

  <Card title="Infrastructure" icon="building">
    ParkingLot, Floor, Entry/Exit
  </Card>
</CardGroup>

### Entity-Responsibility Mapping

| Entity          | Responsibilities                                 |
| --------------- | ------------------------------------------------ |
| `Vehicle`       | Store type, license plate; check if fits in spot |
| `ParkingSpot`   | Track availability; assign/remove vehicles       |
| `Floor`         | Manage spots on one floor; find available spot   |
| `ParkingLot`    | Coordinate all floors; issue tickets             |
| `ParkingTicket` | Track entry time, spot; calculate fee            |
| `PaymentMethod` | Process 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

<Tabs>
  <Tab title="Enums & Config">
    ### Enums and Constants

    ```python theme={null}
    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

    ```python theme={null}
    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

    ```python theme={null}
    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

    ```python theme={null}
    @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

    ```python theme={null}
    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

    ```python theme={null}
    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]
    ```
  </Tab>

  <Tab title="Payment (Strategy)">
    ### Payment System

    ```python theme={null}
    class PaymentMethod(ABC):
        """Strategy Pattern: each payment method encapsulates its own processing logic.
        Adding a new payment method (e.g., Apple Pay, UPI) requires only a new class,
        not modifications to existing code -- this is the Open/Closed Principle in action."""
        @abstractmethod
        def process(self, amount: float) -> bool:
            pass

    class CashPayment(PaymentMethod):
        def process(self, amount: float) -> bool:
            print(f"Processing cash payment: ${amount:.2f}")
            return True

    class CardPayment(PaymentMethod):
        def __init__(self, card_number: str):
            self.card_number = card_number
        
        def process(self, amount: float) -> bool:
            print(f"Processing card payment: ${amount:.2f}")
            return True

    class PaymentProcessor:
        def process_payment(self, ticket_id: str, 
                           parking_lot: ParkingLot,
                           payment_method: PaymentMethod) -> bool:
            fee = parking_lot.unpark_vehicle(ticket_id)
            if fee is None:
                return False
            
            return payment_method.process(fee)
    ```
  </Tab>
</Tabs>

***

## ▶️ Step 5: Usage Example

```python theme={null}
# 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

<AccordionGroup>
  <Accordion title="Why Singleton for ParkingLot?" icon="1">
    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.
  </Accordion>

  <Accordion title="Why Strategy Pattern for Payment?" icon="credit-card">
    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.
  </Accordion>

  <Accordion title="Why Thread Locking?" icon="lock">
    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.
  </Accordion>

  <Accordion title="Why Vehicle Hierarchy?" icon="car">
    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.
  </Accordion>
</AccordionGroup>

***

## 🚀 Interview Extensions

<Warning>
  **Interviewer Favorite**: "How would you extend this design to support..."
</Warning>

<AccordionGroup>
  <Accordion title="Reserved/VIP Spots" icon="star">
    ```python theme={null}
    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)
    ```
  </Accordion>

  <Accordion title="Electric Vehicle Charging" icon="bolt">
    ```python theme={null}
    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
    ```
  </Accordion>

  <Accordion title="Monthly Pass Holders" icon="calendar">
    ```python theme={null}
    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
    ```
  </Accordion>

  <Accordion title="Lost Ticket Handling" icon="triangle-exclamation">
    ```python theme={null}
    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]
    ```
  </Accordion>

  <Accordion title="Multi-Level Validation" icon="shield">
    ```python theme={null}
    # 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
    ```
  </Accordion>
</AccordionGroup>

***

## 📊 Complexity Analysis

| Operation             | Time Complexity | Space 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*

<Tip>
  **Optimization**: Use a heap/priority queue per floor for O(log S) spot finding!
</Tip>

***

## ✅ What Makes This Design Good?

| Principle                 | How It's Applied                                                                        |
| ------------------------- | --------------------------------------------------------------------------------------- |
| **Single Responsibility** | Each class has one job: Vehicle stores data, Spot manages allocation, Floor coordinates |
| **Open/Closed**           | Add new payment methods without changing existing code                                  |
| **Liskov Substitution**   | Car, Motorcycle, Truck all substitute for Vehicle                                       |
| **Interface Segregation** | PaymentMethod is focused, not a fat interface                                           |
| **Dependency Inversion**  | ParkingLot depends on abstract Vehicle, not concrete types                              |

***

## Interview Deep-Dive Questions

<AccordionGroup>
  <Accordion title="Why did you choose Singleton for ParkingLot, and when would it backfire?">
    **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?
  </Accordion>

  <Accordion title="The park_vehicle method locks the entire ParkingLot. What concurrency problems does this create, and how would you fix them?">
    **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?
  </Accordion>

  <Accordion title="The PaymentProcessor calls unpark_vehicle and then processes payment. What happens if the payment fails after the vehicle is already unparked?">
    **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?
  </Accordion>

  <Accordion title="A Car's can_fit_in method says it fits in COMPACT spots. Is that correct, and what design smell does the Vehicle hierarchy have?">
    **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?
  </Accordion>

  <Accordion title="find_available_spot iterates all spots on all floors. How would you make spot lookup O(1) or O(log n)?">
    **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?
  </Accordion>

  <Accordion title="How would you add an EV charging feature without modifying any existing classes?">
    **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?
  </Accordion>

  <Accordion title="Walk me through what happens when the system has 1 spot left and 3 cars arrive at 3 different entry gates simultaneously.">
    **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?
  </Accordion>
</AccordionGroup>

***

## 🔗 Related Case Studies

<CardGroup cols={3}>
  <Card title="ATM System" icon="building-columns" href="/lld/case-atm">
    Similar State management
  </Card>

  <Card title="Hotel Booking" icon="hotel" href="/lld/case-hotel-booking">
    Similar slot allocation
  </Card>

  <Card title="Elevator System" icon="elevator" href="/lld/case-elevator">
    Similar floor management
  </Card>
</CardGroup>
