The problem
You are designing a ride matching platform that operates in multiple cities. Riders request rides via a mobile app, and the system must match them to the best available nearby driver within seconds, tracking driver locations in real time. The platform must remain available even if a whole city or region's infrastructure fails.
You are the principal engineer tasked with architecting the core matching and tracking service.
If the system cannot scale or tolerate regional failures, riders and drivers will be stranded, causing reputational and financial damage.
Must hold true
- Matching latency must be under 2 seconds for 99% of requests.
- Location updates from drivers arrive every 5 seconds.
- The system must tolerate the loss of any single region without losing data or availability.
- Data privacy laws require that location data is only stored as long as needed for matching.
Functional Requirements
What the system must DO.
| Requirement | Priority |
|---|---|
| Match a rider to the best available nearby driver within 2 seconds. | Must have |
| Track driver locations in real time, updating every 5 seconds. | Must have |
| Continue matching and tracking even if an entire region fails. | Must have |
| Remove or anonymize location data as soon as it is no longer needed for matching. | Must have |
| Support simultaneous matching in at least 100 cities. | Nice to have |
| Provide an admin dashboard to view system health and city-level stats. | Nice to have |
Non-Functional Requirements
How well it must do it — latency, availability, scale. Measurable, not vibes.
| Requirement | Priority |
|---|---|
| 99% of match requests must complete in under 2 seconds. | Must have |
| System must have 99.99% availability, including during regional failures. | Must have |
| Support up to 1 million drivers and 500,000 concurrent riders. | Must have |
| Comply with data privacy laws for location data. | Must have |
What you learn by asking
In an interview these facts are NOT volunteered — the candidate earns them with clarifying questions. These are the answers behind this design.
| If you ask about… | The answer |
|---|---|
| Peak concurrent drivers | 1 million drivers online at peak, each sending location updates every 5 seconds. |
| Peak concurrent riders | 500,000 riders may be searching for matches at the same time. |
| Average ride requests per second | During peak, the system receives about 10,000 ride requests per second globally. |
| Average driver location update size | Each location update is about 200 bytes (driver ID, lat/lon, timestamp, status). |
| Region failure | A 'region' is a cloud region or datacenter serving 10-20 cities; if it fails, requests must be routed elsewhere and no data should be lost. |
| Data retention | Location data must be deleted or anonymized within 1 minute after a match is completed. |
Capacity Estimation
Rough arithmetic turns the requirements into engineering decisions — it tells you which parts need scaling machinery and which are trivially easy. Only the order of magnitude matters.
| Quantity | Answer | How it is derived |
|---|---|---|
| Peak location updates per second | ~200K updates/sec | 1 million drivers / 5 seconds = 200,000 location updates per second |
| Peak inbound bandwidth for driver locations | ~40 MB/sec | 200,000 updates/sec × 200 bytes = 40,000,000 bytes/sec ≈ 40 MB/sec |
| Peak ride match requests per second | ~10K requests/sec | Given directly: 10,000 ride requests per second |
| Storage needed for all driver locations (in memory) | ~200 MB | 1 million drivers × 200 bytes = 200,000,000 bytes = 200 MB |
Quick check
Without scrolling back: roughly what did we work out for peak location updates per second?
The architecture, built up
Nobody designs the final diagram in one stroke, so this design is built up one component at a time rather than shown finished. Start with the simplest thing that works, then add each piece and see where it attaches.
Each stage lets the numbers break the design, then fixes exactly one bottleneck with exactly one new component — and at each break you get a chance to call the fix yourself before it is revealed. You can stop reading after any stage and still hold a complete, working system in your head — that is the point.
The simplest thing that works
Start with a single application server and a persistent store for driver locations and matches.
The most basic design has clients (riders and drivers) communicating directly with a single application server. The server handles all business logic: receiving driver location updates, processing ride requests, and storing match results.
All driver locations and match data are stored in a single persistent store (e.g., a database or key-value store). This setup is simple and easy to reason about but cannot scale or tolerate failures.
The design so far
Where it breaks
At 200,000 location updates/sec and 10,000 match requests/sec, a single server cannot keep up.
Quick check
Which single component would you add to fix that?
One app server is now the weak point
The fix: Add a load balancer and scale out application servers horizontally.
With the estimated write and request rates, a single server is quickly overwhelmed. Introducing a load balancer allows us to run multiple application servers in parallel, distributing the load and improving reliability.
Each app server is stateless, so any server can handle any request, as long as they all have access to the same data store.
The design so far
Where it breaks
Direct client access to the load balancer lacks authentication and city-aware routing.
Quick check
Which single component would you add to fix that?
Clients need authentication and routing
The fix: Add an API gateway to handle authentication, routing, and rate limiting.
To improve security and manageability, we introduce an API gateway in front of the load balancer. The gateway authenticates requests, enforces rate limits, and can route requests based on city or region.
This also helps with future multi-region routing and provides a single entry point for all clients.
The design so far
Where it breaks
At peak, frequent queries for nearby drivers overload the location store.
Quick check
Which single component would you add to fix that?
Location store can't serve all queries fast enough
The fix: Introduce a cache for frequent nearby driver lookups.
Matching requires fast, repeated queries for nearby drivers. To avoid overloading the main location store, we add a cache that holds the most frequently accessed nearby driver results.
This reduces latency for common queries and lowers the load on the underlying store, helping to meet the sub-2s SLA.
The design so far
Where it breaks
Transient storage of matches is insufficient for audits, analytics, and recovery.
Quick check
Which single component would you add to fix that?
Match data needs durability and analytics
The fix: Add a persistent match database for ride match records and status.
While driver locations are ephemeral, match records must be durable for analytics, audits, and customer support. We introduce a persistent match database to store match results and status.
This separation allows us to optimize location storage for speed and match storage for durability and queryability.
The design so far
Where it breaks
If a region goes down, location and match data become unavailable for affected cities.
Quick check
Which single component would you add to fix that?
Regional failure threatens availability
The fix: Add replica app servers and location stores in another region for failover.
To meet the requirement for regional failover, we deploy replica application servers and replicate the location store to another region.
If a primary region fails, traffic is automatically routed to the replica infrastructure, ensuring continued matching and tracking. Replication ensures no data is lost and the system remains available.
The finished design
The full reference design, with the reason behind every connection. When you practice, you will rebuild this from a palette that includes decoys.
Why each connection exists
- Mobile ClientAPI GatewayClients send all requests through the gateway for routing and auth
- API GatewayLoad BalancerGateway forwards requests to the load balancer for distribution
- Load BalancerApplication ServerLoad balancer spreads requests among app servers
- Application ServerDriver Location StoreApp server reads/writes driver locations for matching
- Application ServerMatch DatabaseApp server writes match results and status
- Application ServerNearby Drivers CacheApp server queries cache for frequent nearby driver lookups
- Nearby Drivers CacheDriver Location StoreCache is refreshed from the main in-memory location store
- Application ServerReplica Application Server(optional)Failover path to replica app server in another region
- Driver Location StoreReplica Location StoreLocation data is replicated for regional failover
The design in one glance
Each stage fixed exactly one problem. If you can retell this table from memory, you can derive the whole architecture on a whiteboard.
| The design so far | What broke | The fix |
|---|---|---|
| The simplest thing that works | — the starting point | Start with a single application server and a persistent store for driver locations and matches. |
| One app server is now the weak point | At 200,000 location updates/sec and 10,000 match requests/sec, a single server cannot keep up. | Add a load balancer and scale out application servers horizontally. |
| Clients need authentication and routing | Direct client access to the load balancer lacks authentication and city-aware routing. | Add an API gateway to handle authentication, routing, and rate limiting. |
| Location store can't serve all queries fast enough | At peak, frequent queries for nearby drivers overload the location store. | Introduce a cache for frequent nearby driver lookups. |
| Match data needs durability and analytics | Transient storage of matches is insufficient for audits, analytics, and recovery. | Add a persistent match database for ride match records and status. |
| Regional failure threatens availability | If a region goes down, location and match data become unavailable for affected cities. | Add replica app servers and location stores in another region for failover. |
Good to know — What this design leaves out — on purpose
Every real system also needs monitoring and metrics, logging and tracing, alerting and on-call, CI/CD, auth hardening and abuse limits, and a cost model. None of them appear above, and that is a choice: they sit beside EVERY system in much the same shape, so drawing them here would add the same five boxes to every problem in this library while crowding out the part that is actually specific to this one — the path a request takes and where it breaks. This is also why a metrics or monitoring block is a wrong answer in the practice canvas: it is never the thing that makes this design work. In a real interview, name these in one sentence once the data path is settled — "I would put metrics on the cache hit rate and alert when it drops" — and move on. Reaching for them before the core flow is drawn reads as avoiding the question.
API Design
Every operation traces back to a requirement — nothing extra, nothing missing.
| Endpoint | Purpose |
|---|---|
| POST /drivers/{driverId}/location | Receive the latest location and status for a driver. |
| POST /riders/{riderId}/request | Rider requests a ride, triggering a match. |
| GET /matches/{matchId} | Retrieve the status of a ride match. |
| GET /admin/city/{cityId}/stats | Admin dashboard endpoint for city-level operational stats. |
Quick check
Which one would you call to receive the latest location and status for a driver?
Data Model
| Entity | Fields |
|---|---|
| DriverLocation | driverId: string (Unique driver identifier), latitude: float (Driver's latitude), longitude: float (Driver's longitude), timestamp: int64 (Update time (epoch ms)), status: string (e.g., available, busy) |
| RiderRequest | riderId: string (Unique rider identifier), pickupLat: float (Pickup latitude), pickupLon: float (Pickup longitude), timestamp: int64 (Request time) |
| Match | matchId: string (Unique match identifier), riderId: string (Matched rider), driverId: string (Matched driver), status: string (pending, accepted, completed, cancelled) |
Defending the Design
Interviewers close by stress-testing the design. Strong answers follow one shape: which component → what breaks there → the fix and its cost. Here are this problem's probes and the points a strong answer hits:
How would you monitor and tune the Nearby Drivers Cache to avoid it becoming a bottleneck at peak load? Track cache hit/miss rates and latency. Adjust cache size and eviction policy based on city-level traffic. Pre-warm cache for high-demand areas. Monitor for uneven load across cache nodes.
Describe in detail what happens when an entire region (including app servers and location store) goes down. How does your design ensure continued operation? Traffic is routed to replica app servers and location stores in another region. Replicated location data ensures drivers are still visible for matching. DNS or gateway reroutes requests automatically. No data loss due to synchronous replication or fast catch-up.
If the number of cities and drivers grows 10x, what part of your design breaks first? How would you evolve the architecture? Location store memory and lookup performance become bottlenecks. Partition location data by city or region. Scale out app servers and caches horizontally. Consider sharding match database by city.
What are the trade-offs between immediate deletion versus anonymization of location data after a match? Immediate deletion maximizes privacy but removes ability to audit or debug. Anonymization allows aggregate analytics but risks re-identification. Retention policy must balance compliance, business needs, and user trust.
How do you ensure consistency of driver location data between primary and replica location stores across regions? Use synchronous or near-real-time replication. Resolve conflicts based on latest timestamp. Monitor replication lag and alert on delays. Design for eventual consistency if strict real-time is not feasible.
Tip — Key takeaway
Designing a real-time, multi-region ride matching service requires careful balancing of low-latency data access, high write throughput, and strong failover guarantees. In-memory data stores and caches enable fast matching, while replication and stateless components ensure resilience. Privacy and compliance must be engineered into data flows, not bolted on.
Common trap — Common mistakes on this problem
Storing all driver locations in a single database, which cannot handle the required write and query rates for real-time matching. · Relying on asynchronous queues for matching, which violates the strict latency requirement for synchronous matches. · Neglecting regional failover, leading to total outages when a region fails.
You've seen the whole design — now build it yourself
The practice run walks the same steps, but YOU do the work: gather the requirements, run the numbers, wire the architecture — with an AI interviewer and per-step feedback.