How to Design a Parking Garage System?
Learn how to design a parking garage system with atomic spot allocation, multi-level counters, caching, and scalable entry/exit flows.
Expected Interview Answer
A parking garage system models floors, rows and spots as a hierarchy with real-time availability counters, uses atomic decrement/increment operations at entry and exit gates to allocate a spot without conflicts, and separates the fast allocation path from slower reporting and billing concerns.
At the core is a spot-allocation service backed by a data store that supports atomic updates (e.g., a compare-and-swap or a single UPDATE with a WHERE status = "free" clause) so two cars arriving at the same instant cannot be assigned the same spot. Availability is tracked at multiple granularities — per spot, per row, per floor, and per garage — with counters updated transactionally as spots are taken or freed, and a display board reads a cached, slightly-lagging aggregate rather than hitting the transactional table directly. Entry and exit are modeled as two separate flows: entry issues a ticket (or reads a license plate) and atomically claims a spot, while exit computes the fee from duration and payment method and atomically releases the spot. For scale, sharding by garage/level and caching read-heavy “how many free spots” queries keeps the hot path fast, while billing, reporting and analytics run asynchronously off an event stream of entry/exit events.
- Atomic spot allocation prevents two cars from ever being assigned the same spot
- Multi-level counters (spot/row/floor/garage) make “how many free” queries cheap and fast
- Separating entry/exit from billing/reporting keeps the hot path low-latency
- Sharding by garage/level lets the system scale to many independent facilities
AI Mentor Explanation
Designing a parking garage system is like a stadium’s player-of-the-match display board that must always show an accurate, quickly-updated tally without slowing down the actual scoring at the crease. Each run scored (a car entering) atomically updates the official scorebook first, and only afterward does the big screen refresh from a cached snapshot so fans do not stare at a frozen board. If two scorers tried to log the same run simultaneously, the official book’s atomic update ensures only one recording succeeds. That separation of fast, atomic ground-truth updates from a cached public display mirrors exactly how a parking garage tracks live availability.
Step-by-Step Explanation
Step 1
Model the hierarchy
Represent the garage as floors containing rows containing spots, each with a status and a size class (compact, large, handicap).
Step 2
Atomically allocate on entry
An entry gate reads or issues a ticket and atomically claims a free spot (or the nearest matching size) with a conditional update.
Step 3
Serve cached availability reads
Digital signage and apps read a cached, periodically refreshed aggregate rather than querying the live transactional table on every request.
Step 4
Atomically release on exit
The exit flow computes the fee from duration, processes payment, and atomically marks the spot free, incrementing the relevant counters.
What Interviewer Expects
- Describes atomic, conditional updates for claiming/releasing a spot to avoid double-allocation
- Separates the fast allocation path from slower billing/reporting/display concerns
- Mentions multi-level counters (spot/row/floor/garage) for cheap availability queries
- Considers scale via sharding by garage/level and caching for read-heavy display queries
Common Mistakes
- Allowing two entry requests to read-then-write availability non-atomically, causing overbooking
- Making the live “spots available” sign query the transactional table on every render instead of a cache
- Ignoring different spot sizes/classes (compact, handicap, EV charging) when allocating
- Coupling billing computation into the same transaction as the entry/exit spot update
Best Answer (HR Friendly)
“A parking garage system needs to instantly and correctly hand out spots as cars arrive, without ever assigning the same spot to two cars at once. We do that with atomic, conditional updates at the entry gate, keep counters at the spot, row, floor and garage level so availability queries are fast, and let the public display show a slightly cached number instead of hitting the live database on every glance.”
Code Example
def allocate_spot(garage_id, vehicle_size):
row = db.execute(
"UPDATE spots SET status = 'occupied' "
"WHERE id = ("
" SELECT id FROM spots "
" WHERE garage_id = %s AND status = 'free' AND size >= %s "
" ORDER BY floor, row LIMIT 1 FOR UPDATE SKIP LOCKED"
") RETURNING id, floor, row",
[garage_id, vehicle_size],
)
if row is None:
raise GarageFullError(garage_id)
db.execute(
"UPDATE garage_counters SET free_count = free_count - 1 "
"WHERE garage_id = %s AND floor = %s",
[garage_id, row.floor],
)
return {"spotId": row.id, "floor": row.floor, "row": row.row}
def release_spot(garage_id, spot_id, floor):
db.execute("UPDATE spots SET status = 'free' WHERE id = %s", [spot_id])
db.execute(
"UPDATE garage_counters SET free_count = free_count + 1 "
"WHERE garage_id = %s AND floor = %s",
[garage_id, floor],
)Follow-up Questions
- How would you handle a garage with multiple spot sizes (compact, large, handicap, EV)?
- How does the “spots available” sign stay fast if thousands of clients poll it every second?
- What happens if the payment step fails during exit — should the spot stay occupied?
- How would you shard this system across hundreds of garages in a city?
MCQ Practice
1. Why must spot allocation use an atomic, conditional update instead of a plain read-then-write?
A non-atomic read-then-write has a race window where two entries could both see a spot as free and both claim it.
2. Why does the public “spots available” display read from a cache instead of the live table?
Read-heavy display queries are served from a cached aggregate so they do not contend with the low-latency, high-integrity allocation path.
3. What is the benefit of tracking availability at spot, row, floor and garage levels?
Multi-level counters let the system answer availability questions at any granularity via a fast counter read instead of scanning individual spots.
Flash Cards
How do you prevent double-allocating a parking spot? — An atomic, conditional update (compare-and-swap or SELECT ... FOR UPDATE) claims the spot only if it is still free.
Why cache the “spots available” display? — To keep high-volume read traffic off the low-latency allocation path; the display can lag slightly.
Why track counters at multiple levels? — So spot/row/floor/garage availability queries are O(1) reads instead of scans.
Why separate billing from the entry/exit transaction? — To keep the hot allocation path fast; billing/reporting can run asynchronously off an event stream.