System-design interview · Extended interviews
Design maps and route planning
Design legal driving routes from a versioned road graph, then scale search and map delivery while keeping traffic updates and cached paths coherent.
You will learn to
- Explain a complete route request before adding acceleration or regional partitioning.
- Calculate compute and bandwidth separately and justify each scaling step.
- Defend shortest-path correctness, compatible graph publication and closure handling.
Practice in this chapter
8 interview questions with model answers and follow-ups.
Go to interview practiceUseful foundations: Design nearby place search and friend discovery · Database indexes: B-trees, composite keys and query access · Caching: cache hits, misses, write policies and invalidation · Data partitioning and sharding
Workload and timing examples are interview assumptions.
Dotted concept links open the relevant explanation in a new tab.
01Define what a route promises
A maps product has two different jobs. Map tiles draw the background the user sees; routing computes a legal sequence of roads from an origin to a destination. They can share source geography without sharing a serving system. We design driving directions for departure now, with map display and optional navigation refresh. Address geocoding, transit, lane guidance and offline navigation are separate extensions.
Before choosing an engine, ask: do we need driving directions for departure now or predictions for future departures, and how quickly must known closures affect answers? The worked scope below chooses departure now and an explicit closure-freshness target.
Represent intersections as vertices and permitted directed movements as edges. An edge carries a nonnegative travel-time cost. One-way streets remove reverse movements. A prohibited turn depends on the incoming road, so the search state must preserve that information rather than treating every intersection as an unrestricted connection.
Use one small graph throughout: A→B takes four minutes, B→D four, A→C three and C→D eight. The best permitted route is A-B-D at eight minutes, although A-C looks cheaper initially. Road snapping connects coordinates to plausible accessible road positions. The basic flow is coordinate validation → road snapping → search on one graph version → path reconstruction → closure validation → response. This is the complete product before any cache, hierarchy or regional shard is introduced.
02Functional requirements
Agree on what the service must do before choosing its components.
- Display the map. Serve versioned map tiles for the requested area and zoom level.
- Find driving directions. Accept origin, destination, a driving profile and avoidance preferences such as avoid-toll; return the road/turn sequence, geometry, distance and estimated duration. Connect coordinates to plausible accessible road positions: an overpass must not snap to the road underneath merely because it is close.
- Refresh a route. Recompute after a reported incident, with optional refresh notices for active navigation.
- Explain the result. Return data versions and freshness, distinguish an inaccessible endpoint from valid endpoints with no connecting route, and label historical-traffic fallback. Address geocoding, transit, lane guidance and offline navigation are outside the worked scope.
03Non-functional requirements
Use these hypothetical requirements for the worked interview. Confirm the assumptions with the interviewer; the numerical targets require testing and are not measured results. For latency, p95 and p99 mean that 95% and 99% of measured delays, respectively, are no greater than the reported value.
- Workload. Plan for ten million route requests/day and a peak of 2,000/s. Size repeated map-tile bytes separately from route computation.
- Latency and availability. Target route p95 below 300 ms and 99.9% routing availability under the admitted workload. Test long trips and incident spikes as well as short city routes.
- Freshness. Publish validated topology daily, ordinary traffic within one minute and trusted urgent closures within ten seconds. Return observation and validation versions so callers can interpret freshness.
- Route correctness. Every returned path must obey one compatible topology, turn and weight model, plus known closures checked at the stated final validation boundary. An estimated arrival time is an estimate under that model, not a promised physical arrival time.
- Honest failure behavior. Missing traffic may permit labeled historical weights. Missing legal connectivity must produce no route; stale urgent restrictions can require refusal or an explicitly permitted degraded result. A successful search cannot reveal an unreported physical incident.
- Access and privacy. Authenticate callers, bound expensive requests, restrict map/closure administration and keep navigation positions private with limited retention. Public tile caches must not expose private location history.
04Make one correct in-memory search work
One server loads the directed, turn-aware graph and spatial index. It validates the request, finds bounded accessible snap candidates and runs Dijkstra with fixed nonnegative costs. The algorithm keeps the cheapest currently known distance to each search state and repeatedly settles the smallest one. Settling means its minimum cost is established under these assumptions.
Dijkstra trace from A
| Step | Distances discovered |
|---|---|
| Start at A | Tentative B = 4 and C = 3. |
| Settle C | Discover D = 11 through C. |
| Settle B | Improve D to 8 through B. |
| Settle D | Establish the eight-minute path. |
Save predecessor edges whenever a distance improves, and follow them backward to reconstruct actual roads and turn instructions. Choosing C and committing to its entire continuation after the first cheap edge would be a greedy mistake.
An endpoint inside a road needs a permitted partial-edge connection with the appropriate cost; it does not require traversing the entire road or inventing a reverse movement. Use a bounded set of plausible candidates where necessary.
For updates, build a second complete graph, validate it, then switch an active pointer. Each query retains its selected graph until it finishes. Static tiles can initially be ordinary files. This baseline is useful and testable even without a specialized distributed routing system.
The search owns one graph reference from snapping through reconstruction.
Read each connection in order
- syncCoordinates and profileRoute client → Validate and snap
- syncAccessible endpointsValidate and snap → Dijkstra search
- syncRead one versionDijkstra search → Pinned graph and turns
- returnRoads, duration and versionsDijkstra search → Route client
05Separate search CPU from repeated tile bytes
The average route rate is 10M/86,400 ≈ 116/s, much lower than the chosen peak. If a representative route costs 50 milliseconds of CPU, 2,000/s requires 100 busy cores. At a planned 60% utilization ceiling that suggests about 167 cores before failure headroom. Benchmark long and cross-region trips: a short-city average does not bound their search work.
| Workload | Calculation | Design implication |
|---|---|---|
| Route output | 2,000/s × 20 KB = 40 MB/s | Compute and response bandwidth both matter |
| Tile delivery | 1B/day × 30 KB = 30 TB/day | Repeated immutable tiles fit a CDN |
| Raw directed-edge records | 100M × 32 B = 3.2 GB | Geometry, turns and indexes add substantial memory |
| Matrix request | 100 origins × 100 destinations = 10,000 pairs | Admit by estimated work, not one HTTP request |
Three loaded graph versions already require 9.6 GB for those raw edge records alone. Keep old versions for active requests and rollback, then release them safely. The dominant costs differ: tile reuse reduces network load, search acceleration reduces CPU, and version retention increases memory. More application servers cannot substitute for deciding which of these resources is exhausted.
06Name the graph and the result precisely
Route request
POST /routes
| Input | Meaning |
|---|---|
| Origin and destination | Requested start and end coordinates. |
| Mode and avoidance preferences | Driving profile and constraints such as avoiding tolls. |
| Departure assumption | The time assumption under which travel weights apply. |
Route response
| Returned information | Purpose |
|---|---|
| Snapped endpoints | Show the accessible road positions used for the search. |
| Road/turn sequence and geometry | Describe the actual route to follow. |
| Distance and duration | Report route length and estimated travel time. |
| Bundle ID, traffic observation time and closure-validation version | Identify the model and freshness boundary used for this answer. |
Validate coordinates and bound route alternatives and matrix dimensions. A request retry can be a fresh computation; if the client needs reproducibility, it explicitly requests a retained bundle.
Keep these model artifacts separate:
| Artifact | What it records |
|---|---|
| Directed roads | Permitted movements between road positions. |
| Turn rules | Allowed or prohibited incoming-road/outgoing-road combinations. |
| Travel weights | Costs used by the selected search model. |
| Spatial snap index | Candidate road positions near supplied coordinates. |
| Bundle manifest | Compatible versions of those artifacts. |
A shortcut speeds search by representing several real roads as one connection. Keep the list of those roads: a shortcut becomes invalid if a road it uses closes.
Cache identities
| Cache | Key must include |
|---|---|
| Map tile | Map/style version, zoom, x and y. |
| Route | Snapped endpoints, profile, preferences, departure assumptions and bundle. |
Rounding coordinates too aggressively can move a request across a divided highway. Navigation positions are private session data with short retention; they do not belong in public tile-cache keys or broadly readable request logs.
07Fix the measured bottleneck with the matching mechanism
First put immutable tiles behind a CDN. Their repeated bytes justify edge caching independently of route computation. This adds cache lifecycle and origin storage; it does not make traffic estimates fresher.
Next add warmed routing replicas. A query router sends each request to a worker that already loaded and verified the required bundle. Independent queries scale across workers while each search remains local. The cost is replicated graph memory and warm-up time. A cold process is not ready merely because its HTTP port responds.
The final interview design keeps each search local and scales requests with these warmed replicas. If long searches later dominate CPU, A* is a possible follow-up: it uses an estimate of remaining cost to explore promising paths first. That estimate and the implementation must preserve the shortest-path guarantee. Precomputed shortcuts are another extension, with their own update cost; neither is necessary to explain the working design.
Geographic partitioning is also optional. It becomes relevant when a full graph no longer fits economically on a worker. A route can cross several boundaries, so simply choosing the nearest border is incorrect. Keep this as a clearly separate expansion discussion; the main capacity plan uses full graph replicas and does not depend on a regional routing protocol.
08Publish traffic as a coherent release
A build pipeline accepts trusted map edits and consented traffic observations. Map matching associates a sequence of observations with plausible directed roads; one noisy GPS point is weak evidence. Reject implausible jumps, aggregate speeds and use historical estimates where current samples are sparse. Trusted closures remain hard restrictions, rather than merely very slow travel weights.
A candidate bundle contains topology, turn rules, weights, spatial indexes and any compatible shortcut structures. Check connectivity, access restrictions, checksums and representative routes against the reference search. Stage it on required workers before publishing its manifest. Failed uploads or half-built indexes must not become a serving release.
After validating compatibility, atomically change the active pointer to the new bundle. In-flight requests keep their old bundle, so cleanup waits until they finish. Requests may use different complete versions during rollout, but one request never mixes them. Retained releases also stay available.
Urgent closures additionally invalidate affected cached routes or prompt navigation recomputation. Validate their edge identities against the selected topology. A bundle name alone is not a correctness proof: the build and serving checks establish that its components can actually be used together.
Route requests use warmed workers that retain one complete graph bundle and check applicable closures. Tile requests use the CDN; map and traffic publication prepares the next verified bundle in the background.
Read each connection in order
- syncRequest routeMap client → Query router
- syncChoose ready workerQuery router → Warmed route workers
- asyncLoad complete versionVerified graph bundles → Warmed route workers
- asyncValidate then publishMap and traffic builder → Verified graph bundles
- syncValidate returned roadsWarmed route workers → Available closure data
- returnRoute and freshnessWarmed route workers → Map client
- syncRequest immutable tileMap client → Tile CDN
- syncFetch missing tileTile CDN → Tile origin
09Trace the final request and its cache boundary
Request q61 asks for A to D while bundle g12/w8 is active. Follow the request in order:
- Admit the request. Authenticate and apply a work budget.
- Choose one model. Capture the bundle reference, then snap the endpoints under the car profile.
- Search. Find A-B-D = 8.
- Reconstruct the roads. Build the real-road sequence before turns and geometry, because access checks concern those roads. An optional accelerated variant first expands shortcuts into their underlying roads.
Before releasing the response:
- Validate known closures. Check the expanded path against available closure data; record its version and validation time.
- Recompute if necessary. If B-D is closed, reject that path and search a compatible updated model, choosing A-C-D = 11 if permitted.
- Respect the remaining budget. If recomputation cannot finish in time, return an explicit retry/degraded outcome. Never label a forbidden old path as a successful current route.
A route-cache hit follows the same closure policy. Immutable bundle keys prevent accidental version mixing, but they do not override a deliberate urgent-closure invalidation. Return traffic freshness and whether historical weights were used. Release the graph reference after response construction.
An incident reported after the stated validation boundary can invalidate an already returned route. An active navigation session receives a refresh notice and requests another route. The service can act only on information it has received; it cannot recall an answer already sent.
A compatible original search does not bypass final closure validation.
Read each connection in order
- syncSearch A-B-D = 8Query worker → Pinned g12/w8
- syncValidate expanded roadsQuery worker → Closure authority
- returnB-D now prohibitedClosure authority → Query worker
- syncRecompute on compatible updateQuery worker → Query worker
- returnA-C-D = 11, or explicit retryQuery worker → Client
10Check legal movement, search and version consistency
Check three things: the graph represents legal movements, the search finds the right path, and the request uses compatible data throughout. Each item addresses a different source of bad directions. A fast algorithm cannot compensate for a missing one-way restriction, and an accurate map cannot compensate for greedy path selection.
Dijkstra works here because the selected costs stay fixed and nonnegative while the query runs. Once the cheapest unsettled state is selected, reaching it through another unsettled state cannot make it cheaper. In the example, C is explored first without committing to C-D; B then supplies the better route to D. Include disconnected destinations and zero-cost edges in tests.
For ordinary updates, build the replacement separately and let a query finish on its captured graph. New requests can select the replacement. Retaining the old object while a query references it prevents an update from changing the meaning of a path halfway through its calculation. Memory management details belong to implementation review.
Known closures require a separate response check because avoiding a prohibited road matters even when the search began earlier. This does not guarantee awareness of incidents never reported to the service. Future departures, continuously changing edge costs and specialized acceleration have additional assumptions; they are optional follow-ups, not hidden dependencies of this baseline.
11Recover service without inventing safe roads
A routing-worker crash loses an in-flight calculation, so the client retries, potentially on a newer bundle. A build crash leaves unadvertised artifacts. A bad release can roll back to a retained compatible bundle, while closure enforcement still follows its explicit freshness policy. If urgent restrictions are too stale to satisfy the contract, refuse affected requests or disclose the permitted degraded mode.
Measure route latency by length and region, CPU/query, snapping distance, no-route rate, traffic age, expanded-path restriction violations and ETA error. Aggregate ETA error can hide a poorly modeled road class, so inspect cohorts and compare predicted with observed trips carefully. Preserve only the location history actually needed and restrict administrative map/closure updates.
Test one-way roads, forbidden turns, overpasses and disconnected islands. Optional regional or accelerated variants also need region exit/reentry and closure-under-shortcut tests. Compare candidate engines with a trusted base search before a limited rollout. Load tests include matrices and long routes, not only easy urban paths. Reserve reroute capacity for active navigation during incident spikes.
The next scaling decision follows measurements: lower query CPU is of little value if a ten-minute rebuild cannot meet one-minute traffic freshness. Explain both serving and update cost before recommending an acceleration method.
12Check the design against its requirements
Before closing, check the final design against the agreed requirements. FR means functional requirement and NFR means non-functional requirement; the numbers refer to the lists above. These are proposed validation checks, not test results.
| Requirement | Mechanism in the final design | Validation and remaining limit |
|---|---|---|
| FR 1; NFR 1 | Versioned tiles use a CDN; warmed route workers handle search separately. | Measure tile bandwidth, cache misses and route CPU independently at the stated load. |
| FR 2, 4; NFR 4 | Turn-aware search retains one compatible graph bundle and returns its versions. | Test the eight-minute path, one-way roads, overpasses and disconnected endpoints against a trusted search. ETA remains an estimate. |
| FR 3; NFR 3, 5 | Validated releases, a final closure check and navigation refresh handle changes. | Close B-D during q61; require recomputation or an explicit failure. Measure update age; events after release cannot be recalled. |
| NFR 2 | Warmed replicas, bounded admission and retained releases support service continuity. | Load-test p95 below 300 ms with long routes and a failed worker; validate the 99.9% objective through monitoring. Capacity arithmetic alone proves neither. |
| NFR 6 | Authenticated work budgets and restricted location/administrative data protect access. | Try unauthorized closure changes and inspect cache/log scope; verify retention applies to location data. |
13Rapid revision
Rehearse the numbered functional requirements and non-functional targets first. Use this table to recall the mechanisms, then close with the requirements check above.
Remember: One road model; check closures before replying.
| Interview prompt | Recall the mechanism and limit |
|---|---|
| What is a route? | The lowest-cost legal path for the chosen vehicle rules and road-data version; arrival time is still estimated |
| Why separate tiles? | A CDN can reuse immutable map images; each route needs its own computation |
| Why keep incoming-road state? | Whether a turn is legal depends on which road brought the vehicle to the intersection |
| Why begin with Dijkstra? | It gives a correct comparison result for fixed, nonnegative road costs |
| What does a shortcut mean? | Its costs and restrictions must match the chosen road-data version; expand it into real roads |
| What does pinning achieve? | The query keeps one complete data bundle; cleanup waits until it finishes |
| What changes on a closure? | Check the route’s actual roads, recompute or fail, and invalidate affected cached routes |
| When shard geography? | When complete graph copies cost too much; routes must still support repeated border crossings |
A concise closing is: “I start with a correct local road search, separate tile delivery, and replicate warmed graph workers. Acceleration is justified by measured CPU and validated against the base search. Each request keeps one compatible bundle and checks known closures before release. I accept explicit freshness and recomputation limits rather than returning an invented legal path.”
Practise the interview questions
Say your answer aloud before opening the model answer. Then answer the follow-up and compare the reasoning.
What is the first distinction in a maps interview?
Reveal a model answer
Separate drawing a map from computing directions. Tiles are reusable visual objects, while a route is a legal weighted path for particular endpoints and preferences. That distinction explains why CDN bandwidth and search CPU need different capacity estimates. Define departure time and access rules before choosing an engine.
Interviewer follow-up
Does a nearby road make a valid snap?
Reveal the follow-up answer
No. Direction, vehicle access, turns and actual connections matter; an overpass can be geographically close but inaccessible.
What the answer must demonstrate: Separate drawing a map from computing directions.
Why does A-C-D lose even though A-C is cheapest?
Reveal a model answer
Its full cost is 3 + 8 = 11, while A-B-D costs 4 + 4 = 8. Dijkstra keeps alternative tentative distances, settles C and then B, and improves D before settling it. It does not commit to the cheapest first edge as an entire route. The proof assumes fixed nonnegative costs and a correct legal-state model.
Interviewer follow-up
Can changing live weights be read during this search?
Reveal the follow-up answer
Not under that static proof. Pin a compatible model or use a separately justified time-dependent algorithm.
What the answer must demonstrate: Its full cost is 3 + 8 = 11, while A-B-D costs 4 + 4 = 8.
What does 2,000 requests/s at 50 ms CPU imply?
Reveal a model answer
It requires 100 CPU-seconds per second, or 100 fully busy cores before headroom. At 60% utilization the rough planning value is 167 cores before redundancy. This is not a deployment guarantee: long routes, memory locality and traffic mix must be benchmarked. Tile delivery remains a separate high-byte workload.
Interviewer follow-up
Why not add HTTP threads first?
Reveal the follow-up answer
Threads do not create CPU capacity or reduce graph exploration. Determine whether CPU, memory or network is limiting.
What the answer must demonstrate: It requires 100 CPU-seconds per second, or 100 fully busy cores before headroom.
How can an update avoid corrupting an active route?
Reveal a model answer
Build and verify immutable compatible artifacts, then activate their manifest atomically. A request acquires and retains one bundle reference. Cleanup cannot reclaim that bundle until its readers finish. This prevents an active search from combining old shortcuts with incompatible new weights, while permitting later requests to use the new version.
Interviewer follow-up
Is the manifest itself proof of compatibility?
Reveal the follow-up answer
No. Build validation and serving checks establish compatibility; the manifest records the set they validated.
What the answer must demonstrate: Build and verify immutable compatible artifacts, then activate their manifest atomically.
B-D closes while the eight-minute route is running. What happens?
Reveal a model answer
The worker validates the expanded road sequence against the closure version at its final boundary. If that version forbids B-D, it recomputes using a compatible model or returns an explicit retry. It cannot release a known-invalid route merely because the original graph was pinned. An incident reported afterward may require a later navigation refresh.
Interviewer follow-up
Can a short cache TTL replace this?
Reveal the follow-up answer
No. A cached route may be invalid before its TTL ends. It needs the declared closure-check and invalidation policy.
What the answer must demonstrate: The worker validates the expanded road sequence against the closure version at its final boundary.
Why is choosing the nearest regional border unsafe?
Reveal a model answer
The minimum-cost legal route may cross a farther border, leave and reenter a region, or use a road that looks geometrically indirect. An overlay must represent valid interregional path costs and preserve the search problem. Regional boundaries are deployment boundaries, not road restrictions. Keep full graph replicas when their memory cost is acceptable.
Interviewer follow-up
What is the added operational cost?
Reveal the follow-up answer
Compatible overlay/local releases, cross-region coordination, more complex expansion and additional failure behavior.
What the answer must demonstrate: The minimum-cost legal route may cross a farther border, leave and reenter a region, or use a road that looks geometrically indirect.
How would you justify A* or preprocessing?
Reveal a model answer
First measure whether search CPU is the bottleneck. The chosen final design can scale independent local Dijkstra searches with warmed replicas. A* or precomputed shortcuts are further optimizations, tested against that reference. Explain their additional correctness and update assumptions only if the interviewer asks to extend the design.
Interviewer follow-up
What if updates rebuild too slowly?
Reveal the follow-up answer
Retain a correct fallback or choose an update-friendly method; faster queries do not compensate for failing the freshness contract.
What the answer must demonstrate: First measure whether search CPU is the bottleneck.
What do you say when traffic data is unavailable?
Reveal a model answer
Use historical travel weights only if the product permits it, label freshness and preserve known access restrictions. Invalid topology or no legal connection requires failure or a verified base-search fallback, not straight-line directions. Monitor traffic age separately from query success so an available endpoint cannot conceal obsolete estimates.
Interviewer follow-up
What do you test before release?
Reveal the follow-up answer
Turns, one-way roads, overpasses, disconnected endpoints, border reentry and closure invalidation, plus representative long-route load.
What the answer must demonstrate: Use historical travel weights only if the product permits it, label freshness and preserve known access restrictions.
Blank-page exercise · 45 minutes
Build the answer yourself
Design q61 from A to D, close B-D during the request, and defend your scaling decisions.
- Agree the numbered functional requirements and non-functional targets, including departure time, legal paths, freshness and privacy, before drawing components.
- Calculate route CPU and tile throughput.
- Draw the one-server route flow.
- Explain Dijkstra with the eight-versus-eleven-minute example.
- Add only justified caching, replicas and acceleration.
- Use the requirements check to validate routing, latency, freshness and failure behavior; state what the calculations or tests still have not established.
Check that each component and design decision follows from your requirements and workload.
Recall the key ideas
Answer from memory before opening each card. Explain why the choice works and what it costs. Revisit missed cards tomorrow.
Design maps and route planningq61 finds A-B-D, but B-D closes before the reply. What must happen?Recall first, then reveal
Keep one compatible road-data version for the search, then check known closures before replying. Recompute a legal route or return the stated failure; a cached route needs the same check.
One road model; check closures before replying.
Return to lessonDesign maps and route planningWhich resources grow when route queries, map viewing or retained road versions increase?Recall first, then reveal
Route queries need CPU, map tiles need network bandwidth, and retained road graphs need memory.
Compute, bytes, versions.
Return to lessonDesign maps and route planningDoes the displayed ETA promise when the driver will arrive?Recall first, then reveal
No. It estimates arrival using the chosen road model and the updates received so far.
Model time is estimated time.
Return to lessonFinal revision
Summary and interview notes
Find the lowest-cost legal route using one compatible road-data version, then check known closures before replying. Scale route computation and map-image delivery separately, and state how current each answer is.
Remember these points
- Agree the numbered functional requirements and non-functional targets before designing components; validate the final design against them.
- Then establish fixed-cost turn-aware search.
- Cache immutable tiles and replicate warmed route workers.
- Validate shortcuts against real-road expansions.
- Keep one bundle per request and protect its lifetime.
- Return versions and honest degraded states.
Interview tips
- Use the A-B-D versus A-C-D trace to explain the algorithm.
- Quantify update cost as well as query latency.
Important qualifications
- Future departures need a time-dependent model.
- Cross-region graph partitions require a correct overlay.
Continue after the core interview
Explore the advanced version
The advanced lesson keeps the full detailed design. Use these sections when you want to examine the stronger requirements and failure cases.
- A* reopening and heuristic conditions
Develop the formal search implementation when the interviewer asks beyond the baseline proof.
- Time-dependent FIFO routing
Future departures change edge costs and algorithm assumptions.
- Regional overlay construction
Necessary when the graph cannot be economically replicated.
- Artifact deletion versus publication
The full lifetime protocol matters for a storage-level implementation.
Technical references
- OSRM API documentationPrimary descriptions of route, nearest, table, and match operations in a routing implementation.
- pgRouting Dijkstra documentationVersioned primary Dijkstra cost API reference. The worked graph uses nonnegative travel costs; this link is not a claim about the newest pgRouting release.
- OSRM backend documentationOfficial extraction, MLD partition/customization and CH contraction pipelines; benchmark update compatibility rather than assuming arbitrary dynamic restrictions.
Practice marks stay in this browser.