LLD CASE STUDIES / OBJECT DESIGN BRIEF

Design a car rental system

Design a car rental system.

IntermediatePhase 09 / Topic 9 of 10ResponsibilitiesCollaborationsExtensibility
01

Overview

Design a car rental system. Requirements: branches have cars of different types (economy, SUV, luxury); customers search availability by branch, type, and date range; reserve a car; pick up and return it (possibly at a different branch); pay based on duration, car type, add-ons (insurance, GPS), and late fees; the system tracks each car's status and mileage.

Key modeling ideas: separate CarModel/CarType from individual Car (with license plate and status); a Reservation holds a date range, customer, and car; availability means no overlapping active reservation; pricing is a strategy (daily rate, weekend rate, add-ons as decorators or line items); and a reservation lifecycle state machine (reserved, picked up, returned, cancelled).

A hotel for cars

Like booking a hotel room, you reserve a type for certain dates, get a specific one at check-in, and pay for nights stayed plus extras. The desk must never give the same room to two guests on overlapping nights.

02

When to use it

  • LLD interview for reservations with date ranges.
  • Practicing overlap checks and pricing strategies.
  • Similar to hotel rooms and equipment rentals.
03

Where it shows up in interviews

Date-range reservations

Recognize it when: book an item for a period without overlaps.

  • Design a car rental system
  • Design a hotel booking system
  • Design a meeting room scheduler
04

Where it is used in real software

Hertz, Avis, Zipcar

Fleet management with branch inventory, one-way rentals, and dynamic pricing.

Hotel property management systems

Room types vs room numbers, overlapping stay checks.

Calendar booking (My Calendar, LeetCode 729)

The same interval overlap logic.

05

Key terms

Car vs CarType
Individual vehicle vs category with a rate.
DateRange
Value object with overlaps().
Reservation
Customer, car, range, status, add-ons.
Late fee
Charge for returning after the end date.
One-way rental
Return at a different branch.
06

How it works, step by step

  1. 1
    Clarify

    Search filters, one-way returns, add-ons, late fees, cancellation.

  2. 2
    Entities

    Branch, CarType, Car, Customer, Reservation, DateRange, PricingStrategy, AddOn.

  3. 3
    Availability

    Cars of type at branch with no overlapping active reservation.

  4. 4
    Reserve atomically

    Re-check overlap under lock.

  5. 5
    Pickup and return

    Update status, mileage, branch, and compute final charge.

07

Overlap check

Car KA-01 reserved Jan 10 to Jan 13 (end exclusive)

Step 1 / 4
Requested rangeOverlaps?Reason
Jan 13 - Jan 15NoStarts when the other ends
Jan 12 - Jan 14YesJan 12 is inside
Jan 8 - Jan 11YesJan 10 is inside
Jan 5 - Jan 10NoEnds when the other starts

NOWRequested range: Jan 13 - Jan 15 | Overlaps?: No | Reason: Starts when the other ends

Two half-open ranges overlap if startA < endB and startB < endA.

08

Implementation

import java.time.*;import java.util.*; enum CarType { ECONOMY(4000), SUV(7000), LUXURY(15000); final long dailyCents; CarType(long d) { dailyCents = d; } } record DateRange(LocalDate start, LocalDate end) {                // end exclusive    DateRange { if (!end.isAfter(start)) throw new IllegalArgumentException("end must be after start"); }    boolean overlaps(DateRange o) { return start.isBefore(o.end) && o.start.isBefore(end); }    long days() { return Duration.between(start.atStartOfDay(), end.atStartOfDay()).toDays(); }} final class Car {    final String plate; final CarType type; String branch; int mileage;    Car(String plate, CarType type, String branch) { this.plate = plate; this.type = type; this.branch = branch; }} final class Reservation {    enum Status { RESERVED, PICKED_UP, RETURNED, CANCELLED }    final String id = UUID.randomUUID().toString();    final Car car; final String customer; final DateRange range; final Set<String> addOns;    Status status = Status.RESERVED;    Reservation(Car car, String customer, DateRange range, Set<String> addOns) { this.car = car; this.customer = customer; this.range = range; this.addOns = addOns; }    boolean active() { return status == Status.RESERVED || status == Status.PICKED_UP; }} interface PricingStrategy { long quote(CarType type, DateRange range, Set<String> addOns); } final class DailyPricing implements PricingStrategy {    private static final Map<String, Long> ADD_ONS = Map.of("insurance", 1500L, "gps", 500L, "child-seat", 700L);    public long quote(CarType type, DateRange r, Set<String> addOns) {        long perDay = type.dailyCents + addOns.stream().mapToLong(a -> ADD_ONS.getOrDefault(a, 0L)).sum();        return perDay * r.days();    }} final class RentalService {    private final List<Car> cars = new ArrayList<>();    private final Map<String, List<Reservation>> byPlate = new HashMap<>();    private final Map<String, Reservation> byId = new HashMap<>();    private final PricingStrategy pricing;    private final long lateFeePerDayCents;     RentalService(PricingStrategy pricing, long lateFeePerDayCents) { this.pricing = pricing; this.lateFeePerDayCents = lateFeePerDayCents; }    void addCar(Car c) { cars.add(c); }     synchronized List<Car> available(String branch, CarType type, DateRange range) {        return cars.stream().filter(c -> c.branch.equals(branch) && c.type == type)            .filter(c -> byPlate.getOrDefault(c.plate, List.of()).stream().noneMatch(r -> r.active() && r.range.overlaps(range)))            .toList();    }     synchronized Reservation reserve(String customer, String branch, CarType type, DateRange range, Set<String> addOns) {        Car car = available(branch, type, range).stream().findFirst()            .orElseThrow(() -> new IllegalStateException("No " + type + " available at " + branch));        Reservation r = new Reservation(car, customer, range, Set.copyOf(addOns));        byPlate.computeIfAbsent(car.plate, k -> new ArrayList<>()).add(r);        byId.put(r.id, r);        return r;    }     synchronized void pickUp(String id) {        Reservation r = byId.get(id);        if (r.status != Reservation.Status.RESERVED) throw new IllegalStateException("Cannot pick up in " + r.status);        r.status = Reservation.Status.PICKED_UP;    }     synchronized long returnCar(String id, LocalDate returnedOn, String branch, int newMileage) {        Reservation r = byId.get(id);        if (r.status != Reservation.Status.PICKED_UP) throw new IllegalStateException("Car not picked up");        r.status = Reservation.Status.RETURNED;        r.car.branch = branch;                                   // one-way rentals supported        r.car.mileage = newMileage;        long lateDays = Math.max(0, Duration.between(r.range.end().atStartOfDay(), returnedOn.atStartOfDay()).toDays());        return pricing.quote(r.car.type, r.range, r.addOns) + lateDays * lateFeePerDayCents;    }     synchronized void cancel(String id) {        Reservation r = byId.get(id);        if (r.status != Reservation.Status.RESERVED) throw new IllegalStateException("Only reserved bookings can be cancelled");        r.status = Reservation.Status.CANCELLED;    }}
09

Complexity and performance

AvailabilityO(cars x reservations per car)

Index by car and date for scale.

Overlap checkO(1)

Two comparisons.

10

Trade-offs

Assign car at booking vs at pickup

Assigning at pickup maximizes fleet flexibility; assigning at booking simplifies availability but fragments inventory.

Simple pricing vs dynamic pricing

A strategy interface lets you start with daily rates and later plug in demand-based pricing.

11

Variants and related techniques

Interval tree per car

Faster overlap queries with many reservations.

Hotel or meeting room booking

Same design with rooms instead of cars.

12

Common mistakes

  • Inclusive end dates causing off-by-one overlaps.

    Fix: Use half-open ranges [start, end).

  • Checking availability and reserving without a lock.

    Fix: Re-check under the same lock that creates the reservation.

  • Status as free text.

    Fix: Enum with guarded transitions.

13

Interview questions

How do you check if a car is available for a date range?

A car is available if none of its active reservations overlap the requested half-open range, where overlap means requested.start < existing.end and existing.start < requested.end.

How do you support one-way rentals?

Update the car's branch on return, and include a drop-off fee strategy; availability queries then use the car's current branch and future reservations' pickup branches.

14

Practice problems

ProblemDifficultyWhat it trains
My Calendar I (LeetCode 729)MediumInterval overlap.
Car rental with add-ons and late feesMediumPricing strategy.