Full Mock Interview: Designing a Ride-Sharing Matching Engine
You'll learn to
- -Work an unfamiliar LLD prompt end to end, live: clarify requirements, design classes, apply patterns, and defend trade-offs
- -See what separates a strong 45-minute answer from a weak one on a problem outside the case-study list you already studied
Every prior case study in this course was picked because it is a well-known problem you may have already seen the shape of. This chapter deliberately picks a less rehearsed prompt - matching riders to nearby drivers - and works it end to end, live, the way you would actually encounter it: with no prior exposure to the "expected" answer.
Minute 0-5: Clarify Requirements
- -What triggers a match? A rider requests a ride, and the system finds a nearby available driver.
- -What does "nearby" mean - do we need real geospatial search, or can we simplify to a reasonable approximation for this exercise?
- -Can a driver decline a match? If so, does the system need to try the next-nearest driver automatically?
- -Is pricing/surge in scope, or purely the matching logic? (Assume: matching only, pricing out of scope.)
- -Single matching request at a time, or do we need to handle many simultaneous ride requests? (Assume: many, concurrently.)
Notice the shape of these questions: each one either changes what gets built or surfaces a hidden concurrency/scale concern. Five minutes spent here is the highest-leverage five minutes of the entire round.
Minute 5-15: Identify Classes and Relationships
class Location:
def __init__(self, lat: float, lng: float):
self.lat = lat
self.lng = lng
class Driver:
def __init__(self, driver_id: str, location: Location):
self.driver_id = driver_id
self.location = location
self.status: "DriverStatus" = AvailableStatus()
class RideRequest:
def __init__(self, rider_id: str, pickup: Location):
self.rider_id = rider_id
self.pickup = pickup
class Match:
def __init__(self, ride_request: RideRequest, driver: Driver):
self.ride_request = ride_request
self.driver = driverDriver.status being a DriverStatus object rather than a plain string is a deliberate, up-front decision - it previews the State pattern from Module 6, since "can this driver accept a match" behaves differently across Available, EnRoute, and Offline, exactly the shape that pattern was built for.
Minute 15-30: The Matching Algorithm and Concurrency
The core matching question - "find the nearest available driver" - is itself a swappable algorithm (nearest-by-distance today, but a real system might weigh driver rating, ETA, or surge balancing), which is a clean Strategy opportunity, matching the reasoning from the Strategy chapter directly.
class MatchingStrategy(ABC):
@abstractmethod
def find_driver(self, request: RideRequest, drivers: list[Driver]) -> Driver | None: ...
class NearestAvailableStrategy(MatchingStrategy):
def find_driver(self, request: RideRequest, drivers: list[Driver]) -> Driver | None:
available = [d for d in drivers if isinstance(d.status, AvailableStatus)]
if not available:
return None
return min(available, key=lambda d: distance(d.location, request.pickup))
class MatchingEngine:
def __init__(self, strategy: MatchingStrategy):
self._strategy = strategy
self._lock = threading.Lock()
def match(self, request: RideRequest, drivers: list[Driver]) -> Match | None:
with self._lock: # atomic: find-and-claim, not find-then-claim
driver = self._strategy.find_driver(request, drivers)
if driver is None:
return None
driver.status = EnRouteStatus()
return Match(request, driver)This is the exact same check-then-act discipline from the Concurrency module and the Parking Lot case study, applied here without being prompted - which is precisely the transfer this course is built to produce: recognizing the shared-mutable-resource shape on a brand-new problem, not just on the problems it was originally taught with.
Minute 30-40: Handling Decline and Retry
When asked "what if the matched driver declines," the answer builds directly on what already exists rather than restarting: MatchingEngine.match() already excludes non-Available drivers, so a decline just needs to set the driver back to AvailableStatus() and re-invoke match() with the same request, excluding the driver who just declined from this specific retry - no new architecture required, only a small addition to existing logic.
Minute 40-45: Trade-offs and What Was Deliberately Left Out
- -Real geospatial indexing (a quadtree or geohash-based lookup) was skipped in favor of a linear scan over all drivers - correct for a small fleet, and named explicitly as the first thing to fix before real-world scale.
- -Nothing here is distributed - this is a single-process matching engine, consistent with this course's LLD scope (an HLD round would ask how this scales across regions/servers).
- -Pricing, ETA calculation, and driver ratings were explicitly scoped out at minute 0, and are named again here as natural next layers.
Closing with a deliberate "here's what I'd improve with more time" list, rather than letting the round just end when the clock runs out, is itself a strong signal - it shows awareness of the design's limits, not just its features.
Interview Signal is part of Pro
See a real weak answer next to a real strong one for this exact topic.
Quiz is part of Pro
Test what you just read with a short quiz, and bank the XP.