All chapters in this track
- 01Chapter 1: What HLD and LLD Are, and How They Differ
- 02Chapter 2: Gathering and Clarifying Requirements Before Designing Anything
- 03Chapter 3: HLD Deliverables: Architecture, Components, Technology Choices, Data Flow
- 04Chapter 4: LLD Deliverables: Class Diagrams and Sequence Diagrams
- 05Chapter 5: LLD Deliverables: Database Schema and API Contracts
- 06Chapter 6: Documenting Decisions: ADRs and Communicating Trade-offs to Stakeholders
- 07Chapter 7: Moving Cleanly from HLD to LLD Without Losing Intent
- 08Chapter 8: Common Mistakes: Over-Designing, Under-Specifying, and Skipping Non-Functional Requirements
- 09Chapter 9: When LLD Contradicts Its Own HLD
- 10Chapter 10: HLD and LLD in Design Reviews: What Reviewers Actually Look For
- 11Chapter 11: Worked Example: Ride-Sharing Dispatch — Requirements Through HLD to LLD
- 12Chapter 12: Worked Example: Hotel Booking — Requirements Through HLD to LLD
- 13Chapter 13: Worked Example: Food Delivery — Requirements Through HLD to LLD
- 14Chapter 14: How HLD and LLD Show Up in Interviews and Design Reviews
- 15Chapter 15: Common Interview Questions About HLD and LLD
Chapter 11 of 15
Chapter 11: Worked Example: Ride-Sharing Dispatch — Requirements Through HLD to LLD
Why this matters
This chapter walks one system completely through every stage covered in Parts 1-3 — requirements clarification, HLD with all four deliverables, and LLD with class/sequence diagrams and a schema — demonstrating the full, real process end to end rather than any single stage in isolation.
Requirements (Chapter 2)
Clarifying questions: "Is this matching riders to nearby available drivers in real time? What's the matching criteria — nearest driver, or something more sophisticated (rating, vehicle type)? What happens if no driver is available? What's the expected scale?"
Assumed answers: real-time matching to the nearest available driver within a reasonable radius; if none available within the radius, expand the search radius progressively; assume a large metro area with ~50,000 active drivers and ~200,000 ride requests/day.
HLD
Architecture diagram:
+----------+ +----------------+ +-------------------+
| Rider |---->| API Gateway |---->| Dispatch Service |
| App | +----------------+ +-------------------+
+----------+ ^ |
| v
+----------+ | +-------------------+
| Driver |----(location updates)------->| Location Service |
| App | | (tracks driver |
+----------+ | positions) |
+-------------------+
|
v
+-------------------+
| Geospatial Index |
| (e.g., Redis with |
| geospatial commands)|
+-------------------+
Component responsibilities: Dispatch Service owns matching a ride request to a specific driver and managing the ride lifecycle; Location Service owns ingesting and storing real-time driver position updates; the Geospatial Index provides fast "nearest drivers to this point" queries.
Technology choice, with reasoning: "Redis with geospatial commands (GEOADD, GEORADIUS) for the driver location index, specifically because nearest-neighbor geospatial queries need to be extremely fast (sub-second) and driver positions update very frequently (every few seconds per active driver) — a general-purpose relational database's spatial query support would be comparatively much slower under this specific access pattern's write frequency."
Data flow — "request a ride":
1. Rider requests a ride with their current location.
2. API Gateway routes to Dispatch Service.
3. Dispatch Service queries the Geospatial Index for nearby available drivers.
4. Dispatch Service sends a ride offer to the nearest driver (via push
notification, asynchronously).
5. Driver accepts (or the offer times out and goes to the next-nearest driver).
6. Dispatch Service confirms the match to the rider.
Capacity estimate: 200,000 rides/day ≈ 2.3 requests/second average, with a peak factor (companion System Design book, Chapter 4) likely higher than typical (rush-hour concentration) — assume peak of ~15-20 requests/second. Driver location updates: 50,000 drivers × 1 update/5 seconds ≈ 10,000 updates/second — this is actually the dominant write load on the system, far exceeding ride-request volume, and should drive the Location Service and Geospatial Index's capacity planning specifically.
LLD — deep-dive on the Dispatch Service (Chapter 13's "identify the hardest part" principle, applied here)
Class diagram:
+---------------------------+
| RideRequest |
+---------------------------+
| - id: string |
| - riderId: string |
| - pickupLocation: GeoPoint |
| - status: RideStatus |
+---------------------------+
| + assignDriver(driverId) |
| + expandSearchRadius() |
+---------------------------+
| ─── (association: a request is matched to a driver)
v
+---------------------------+
| Driver |
+---------------------------+
| - id: string |
| - currentLocation: GeoPoint|
| - status: DriverStatus |
+---------------------------+
Sequence diagram — the progressive radius expansion (the genuinely tricky part):
RiderApp DispatchService GeospatialIndex DriverApp
| | | |
|--requestRide-->| | |
| |--nearbyDrivers(1km)>| |
| |<--[] (none found)---| |
| |--nearbyDrivers(3km)>| |
| |<--[driver_42]-------| |
| |--sendOffer--------------------------->|
| | | (driver has 15s to accept)
| |<---------------accept()----------------|
|<--matched------| | |
Database schema (partial):
CREATE TABLE ride_requests (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
rider_id UUID NOT NULL REFERENCES riders(id),
pickup_lat DOUBLE PRECISION NOT NULL,
pickup_lng DOUBLE PRECISION NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'searching',
assigned_driver_id UUID REFERENCES drivers(id),
created_at TIMESTAMP NOT NULL DEFAULT now()
);
CREATE INDEX idx_ride_requests_status ON ride_requests(status)
WHERE status = 'searching'; -- partial index: only searching rides
-- need frequent lookup by this status
Tying it together: HLD-LLD consistency check (Chapter 9)
"The LLD's progressive radius expansion directly implements the HLD's stated matching behavior ('if none available, expand the search radius progressively') — I traced this explicitly to confirm no contradiction. The LLD's driver offer/accept flow uses an asynchronous push notification, consistent with the HLD's data flow step 4, rather than a synchronous call that would risk blocking Dispatch Service on a slow or unresponsive driver app."
Common mistakes
- Not identifying driver location updates as the dominant write load, focusing capacity planning entirely on ride-request volume when location updates are actually ~4,000x more frequent in this estimate.
- Choosing a general-purpose relational database for the geospatial index without recognizing the specific, extreme write-frequency and low-latency-query requirements that favor a specialized geospatial data structure/store.
- Not producing a sequence diagram for the progressive radius expansion, missing the chance to make this genuinely tricky, multi-step, timeout-involving flow explicit and reviewable before implementation.
- Skipping the explicit HLD-LLD consistency check, missing a chance to catch a potential contradiction (like an accidentally-synchronous driver offer flow) before implementation.
Chapter summary / cheat sheet
- Ride-sharing dispatch's dominant load is driver location updates, not ride requests — this should drive capacity planning and technology choice (a geospatial-optimized store) specifically.
- The genuinely hard LLD problem: progressive radius expansion with driver offer timeouts — worth a dedicated sequence diagram given its multi-step, timing-dependent nature.
- This worked example demonstrates the complete requirements → HLD (all four deliverables) → LLD (class diagram, sequence diagram, schema) → consistency-check pipeline from Parts 1-3, applied concretely to one real system.