System design interviews are where great engineers separate themselves from good ones.
Anyone can name-drop Kafka or Elasticsearch. But the engineers who get offers are the ones who can reason — who can start from a blank page, ask the right questions, estimate the scale, and build a coherent architecture piece by piece.
This guide is the complete playbook for designing a map and navigation system like Google Maps. It covers everything from scoping the problem to answering follow-up questions at the end. Every section is structured the way you should actually speak in an interview.
The 10-Step Framework
Before diving in, here is the full structure of how to approach this problem:
- Requirements Gathering
- Data Model
- API Design
- High-Level Design
- Deep Dive: Map Tile Rendering (Vector vs. Raster)
- Deep Dive: Spatial Indexing & POI Search
- Deep Dive: Routing Algorithms at Scale
- Complete Architecture
- Failure Handling & Resilience
- Interview Closing & Follow-up Questions
Step 1 — Requirements Gathering
When the interviewer says "Design Google Maps," do not start drawing boxes. Start by asking clarifying questions to establish explicit boundaries.
"Before I start designing, can I ask a few clarifying questions to understand the scope of the system we are building?"
The interviewer will always say yes.
Functional Requirements
"What are the core features we need to support?
Should I focus on the map rendering and visualization? Or POI (Point of Interest) search and autocomplete? What about routing and navigation?
Is offline mode or public transit in scope for this design?"
Then, actively summarize the scope to demonstrate alignment:
"Okay, so I'll focus on these three core features:
- Map Rendering: Visualizing the world map at various zoom levels.
- POI Search & Discovery: Searching for businesses, restaurants, or coordinate bounds.
- Routing and Navigation: Calculating the optimal paths between point A and B with dynamic traffic-aware ETAs.
I'll keep offline mode and public transit timetables out of scope for our initial system. Does that sound correct?"
Non-Functional Requirements
Ask these six targeted questions to quickly establish scale, latency, availability, and consistency constraints:
1. Scale:
"How many active users are we expecting to support globally? What's the anticipated queries per second (QPS) for POI search versus routing calculations? And how much geographical data are we storing?"
2. Latency:
"What are our latency budgets? Should tile loading and search queries return in under 100ms? And should routing calculation complete in under 200ms?"
3. Availability:
"How critical is system uptime? Are we targeting a 99.99% availability SLA, particularly for the map tile rendering infrastructure?"
4. Consistency:
"How strictly do we need to guarantee data consistency? If a new business (POI) is added, does it need to propagate globally in real-time, or is eventual consistency (e.g., minutes to hours) acceptable?"
5. Read vs. Write Ratio:
"Is the traffic primarily read-heavy or write-heavy? Typically, map usage is extremely read-heavy (1000:1 reads to writes) for loading tiles and searching, except for telemetry streams from active drivers, which will be highly write-heavy. Is that assumption correct?"
6. CAP Theorem:
"Since this is a globally distributed system, we will experience network partitions. I want to prioritize Availability and Partition Tolerance (AP) for the tile and POI search components—since slight data staleness is acceptable. However, for live navigation and active routing, we must ensure high accuracy. Does that align with your expectations?"
Final Summary Before Moving On
Summarize the established boundaries on the whiteboard:
"Let me summarize our requirements before I begin the architecture design:
Functional Scope:
- In Scope: Map Rendering (worldwide, multi-zoom), POI Search & Autocomplete, Routing & Dynamic ETAs.
- Out of Scope: Offline map downloads, public transit routing.
Non-Functional Scope:
- Scale: 1 Billion monthly active users (MAUs), 50,000 search QPS, 10,000 routing QPS.
- Latency:
< 100msfor tiles and searches,< 200msfor route computation. - Availability: 99.99% (highly available AP model for reads).
- Consistency: Eventual consistency for POI metadata; real-time updates for telemetry weights.
- Access Patterns: Read-heavy (1000:1 read-to-write ratio for standard users).
With these explicit targets, let me proceed to the Data Model."
Step 2 — Data Model
Start by identifying the core entities.
"For a high-scale map and navigation system, I can identify these five core data entities:
- Map Tile: Lightweight segments representing visual grid geometry.
- Node (Intersection): Connection points on roads, defined by coordinates.
- Road Segment (Edge): Connects two nodes, containing topological and traffic weights.
- POI (Point of Interest): Retail stores, restaurants, or businesses with search tags.
- User Route: Dynamically solved navigation paths for current sessions."
Entity Definitions
Point of Interest (POI):
poi_id,name,description,category(e.g., restaurant, petrol pump)lat,lng(geolocational coordinates)rating,address,phone_number,business_hourss2_cell_id(64-bit integer spatial index)
Road Network Graph:
- Node (Intersection):
node_id,lat,lng - Road Segment (Edge):
segment_idstart_node_id(FK),end_node_id(FK)distance_meters,speed_limit_kphcurrent_travel_time_seconds(dynamic, updated by live telemetry)one_way_flag,geometry(polyline coordinates for visual map rendering)
Map Tile:
tile_id(usually encoded asz/x/y),zoom_level,x_coordinate,y_coordinatetile_data(vector geometry bytes or raster PNG)
Database Choices
This is the most important part of the data model discussion. Never pick one database for everything.
| Entity | Database | Reason |
|---|---|---|
| POI Search & Metadata | Elasticsearch | Rich, full-text search, autocomplete, and geospatial fuzzy matching |
| POI Relational Data | PostgreSQL | ACID storage for reviews, business details, owner metadata |
| Road Network Graph | Custom In-Memory Graph / Neo4j | Routing requires millions of graph operations. A traditional database is too slow; we keep the active routing graph loaded in memory (e.g., using Contraction Hierarchies in RAM) |
| Live Traffic / Speeds | Redis | Microsecond updates for current road segment speeds |
| Map Tiles | Object Storage (S3 / GCS) + CDN | Static assets accessed globally; CDN edge caching is crucial |
| Telemetry / Logs | Cassandra | High-throughput, write-heavy time-series storage for raw GPS logs |
Using a single database for Google Maps is an absolute bottleneck. Real systems employ a specialized polyglot persistence architecture:
- Map Tiles: Served via global CDNs backed by Cloud Storage (S3/GCS).
- Search & Autocomplete: Elasticsearch clusters for fuzzy token indexing and spatial filter range lookups, with hot paths cached in Redis.
- Road Networks & Routing: In-memory customized graphs using Contraction Hierarchies, bypassing disk lookups entirely.
- Telemetry Speeds: Redis in-memory storage for super high frequency, sub-millisecond edge weight overrides.
- Reviews & Metadata: ACID-compliant PostgreSQL database for structured data.
- Telemetry logs: Cassandra wide-column store for massive GPS log ingestion.
Step 3 — API Design
Map every user action to an API. Here are the three core ones.
GET /map/tiles
Fetch map tiles dynamically based on coordinate grids.
GET /map/tiles?z=15&x=18274&y=12984&format=vector
Response:
Returns binary vector data (Protocol Buffers / Mapbox Vector Tile format)
containing raw geometry of roads, buildings, and landuse polygons for client-side rendering.
GET /places/search
Autocomplete and POI search.
GET /places/search?query=pizza&lat=23.02&lng=72.57&radius=3000
Response:
{
"status": "success",
"data": {
"results": [
{
"poi_id": "poi_987",
"name": "La Pino'z Pizza",
"lat": 23.0245,
"lng": 72.5712,
"rating": 4.5,
"address": "Iscon Cross Roads, Ahmedabad",
"distance_meters": 350
}
]
}
}
Status codes: 200 results found, 400 missing location parameters, 404 no matches found.
GET /routing/directions
Calculate paths and ETAs.
GET /routing/directions?origin=23.02,72.57&destination=23.05,72.55&mode=driving
Response:
{
"status": "success",
"data": {
"route_id": "rt_8765",
"total_distance_meters": 5400,
"eta_seconds": 720,
"legs": [
{
"instruction": "Head north on SG Highway",
"distance_meters": 3200,
"duration_seconds": 400,
"polyline": "g`v}EexuyO...[encoded_polyline]"
}
]
}
}
Status codes: 200 route found, 400 invalid coordinates, 422 routing impossible (e.g., across oceans without ferry).
Step 4 — High-Level Design
Walk through the system using a layered approach.
"My high-level design separates concerns into distinct architectural tiers:
- Client Tier: Dynamic iOS, Android, and Web applications.
- Global CDN Edge: Distributed edge caching for static Vector Tile delivery.
- API Gateway: Central security, rate limiting, and routing orchestrator.
- Tile Service: Geo-distributed engines accessing precomputed static assets.
- POI Search Service: Elasticsearch clusters solving full-text geo-bounded queries.
- Routing Service: Ultra-high-speed, in-memory graph solvers utilizing dynamic traffic pings.
- Telemetry Ingestion: High-speed streaming engine calculating real-time edge speeds."
Microservices Setup
- Tile Service: Stateless, serving geo-split vector data. Highly cached.
- POI Service: Manages POI CRUD and indexes locations into Elasticsearch.
- Routing Service: Loads the road network graph into memory. Integrates live edge-weight corrections.
- Telemetry Ingestion: Receives anonymous GPS coordinates from active navigators to analyze traffic conditions.
Dynamic Request Flow (Map View & Search)
- Load Map: Client launches the application and requests vector tiles corresponding to their viewport bounds directly from the nearest CDN Edge.
- Edge Cache Miss: If missing at the edge, the stateless Tile Service loads the precomputed vector grid coordinates from Cloud Object Storage, updates the CDN cache, and responds to the client.
- Trigger Search: The user searches for "pizza near me". The API Gateway sanitizes and routes this query to the POI Search Service.
- Spatial Filter: The POI Search Service maps the client's coordinates to S2 ranges, performing a highly optimized spatial database query in Elasticsearch.
- GPU Render: The client receives the POI results and renders dynamic, clickable visual markers directly on top of the local vector layout.
Step 5 — Deep Dive: Map Tile Rendering (Vector vs. Raster)
When the interviewer asks "How do you store and display the whole world's map?"
"We cannot load the entire planet as a single huge raster image; that would consume petabytes. Instead, we divide the globe recursively using a Hierarchical Tile Pyramid structured on the standard Web Mercator projection.
The world map is sliced into 256x256 pixel grids:
- Zoom Level 0: Exactly $1$ tile representing the entire spherical Earth.
- Zoom Level 1: Divided into $4$ tiles (a $2 \times 2$ coordinate matrix).
- Zoom Level z: Represented by $4^z$ unique grid tiles.
- Zoom Level 20: Houses over 1 trillion distinct tiles, rendering individual buildings, residential paths, and streets."
Vector Tiles vs. Raster Tiles
- Raster Tiles (PNGs): Locked, pre-rendered 20-50KB assets that degrade when zoomed, requiring massive bandwidth and server processing to re-generate styles.
- Vector Tiles (Protobufs): Ultra-lightweight 2-5KB geographic geometries rendered directly by the client's GPU. This enables smooth infinite zoom scaling, dynamic visual styles, and always-upright label rotation.
Let's compare them:
| Metric | Raster Tiles (PNG) | Vector Tiles (Protobuf / MVT) |
|---|---|---|
| Data Type | Pre-rendered images | Raw geometry (lines, polygons, points) |
| Payload Size | Large (typically 20KB-50KB per tile) | Extremely Small (typically 2KB-5KB per tile) |
| Zoom Experience | Pixelated during transitions | Smooth, infinite vector scale |
| Styling | Locked. Server-side rendering required | Dynamic. Client changes styling via CSS/JSON |
| Label Rotation | Static (labels rotate with map, upside down) | Dynamic (labels stay readable, upright) |
| Client Overhead | Negligible (just display image) | Medium (CPU/GPU rendering needed) |
Vector Tile Serving Architecture
"I will design an scalable Vector Tile serving pipeline:
- Spatial Representation: Road lines, park boundaries, and POIs are stored as spatial geometries (polylines, polygons, points).
- Tiered Preparation: Static assets for low-to-mid zoom ranges (0–14) are pre-rendered offline and stored inside Amazon S3 or Google Cloud Storage.
- On-the-fly Detail: For dense local zooms (15–20), tiles are served dynamically by a Tile Service retrieving coordinates from PostgreSQL/PostGIS.
- Aggressive Edge Caching: Since geographic boundaries are highly stable, we cache these binary protobuf tiles at the global CDN level, yielding a
> 99%cache hit rate."
Step 6 — Deep Dive: Spatial Indexing & POI Search
When the interviewer asks "How does the search find 'restaurants near me' in milliseconds?"
"A naive relational database query would look like this:
SELECT * FROM poi WHERE distance(user_lat, user_lng, lat, lng) < 2000;This query is a complete performance killer. It runs in $O(N)$ time and requires computing complex spherical trigonometry on every single record in the table. Standard B-Tree databases cannot index multi-dimensional coordinates effectively. To achieve sub-10ms search speeds, we must implement a Spatial Indexing system."
The Spatial Solutions
- R-Tree (Rectangle-Tree): Indexes spatial objects by grouping them in bounding boxes. Great for complex polygons (like country or state borders), but complex to scale and shard horizontally.
- Quadtree: Recursively divides space into four quadrants (North-West, North-East, South-West, South-East) until each quadrant has fewer than a threshold number of elements. Excellent for dynamic density, but difficult to represent as a simple database query key.
- S2 Geometry / Geohash: Maps the 2D surface of the Earth onto a 1D line using a space-filling curve (like the Hilbert Curve).
Why S2 is King
"I recommend using Google's S2 Geometry library for our spatial index due to these five pillars:
- 1D Transformation: S2 maps two-dimensional spherical coordinates into a single 64-bit integer Cell ID.
- Hilbert Curve Locality: By using a Hilbert space-filling curve, points that are geographically close on the 2D Earth remain adjacent on the 1D line.
- Trivial Range Queries: Finding places within a 2km radius converts a complex spherical search into a highly efficient range query:
code
SELECT * FROM poi_table WHERE s2_cell_id BETWEEN range_start AND range_end; - Supercharged Speeds: This allows us to use simple integer indices in B-Trees or Elasticsearch, avoiding costly real-time trig math.
- Dynamic Hierarchies: S2 offers 31 hierarchy levels, letting us zoom from entire continents down to sub-centimeter grids dynamically."
POI Search & Autocomplete Pipeline
- Autocomplete: A distributed Trie structure (cached in Redis) resolves typing prefixes (e.g., "piz" → "pizza", "pizzeria") in under 10ms (
< 10ms). - Geographical Relevance: Elasticsearch filters candidates by
s2_cell_idrange and ranks results using a scoring function combining:- Text Relevance: Match score of the query against POI name/tags.
- Geographic Distance: Physical distance from the user.
- Popularity/Rating: User reviews and search frequency.
Step 7 — Deep Dive: Routing Algorithms at Scale
When the interviewer asks "How do you calculate directions across a continent in 50ms?"
"A continental-scale road network contains hundreds of millions of intersections (vertices) and streets (edges). Standard graph traversal algorithms are completely computationally infeasible in real-time. We must optimize our search space."
The Evolution of Shortest Path
- Dijkstra: Computes shortest path from source to all nodes. Complexity is $O(V \log V + E)$. Explores every local street and cul-de-sac in the wrong direction. Unusable. ❌
- A* Search: Introduces a heuristic (straight-line distance to destination) to bias the search direction. Improves speed, but still explores millions of irrelevant nodes for long trips. ❌
- Contraction Hierarchies (CH): The industry standard for real-time routing. ✅
"To achieve sub-50ms routing, I would implement Contraction Hierarchies (CH). It executes in two key phases:
1. Offline Pre-processing (Hierarchy Construction)
- Node Importance: Every node in our graph is ranked by topological and functional importance (e.g., Highway intersections have far higher importance than residential cul-de-sacs).
- Contraction: We systematically remove (contract) nodes from lowest to highest importance.
- Shortcut Addition: When contracting a node, we construct direct 'shortcut edges' between its neighbors if that node was part of their shortest path.
- Hierarchical Graph: This precomputes high-speed bypasses, creating an ordered multi-layered routing graph.
2. Online Real-time Query
- Bidirectional Search: We initiate a bidirectional Dijkstra search starting from both the origin and the destination simultaneously.
- Upward Traversal: Crucially, the forward search only travels 'up' to nodes of equal or higher hierarchy, and the backward search does the same.
- Meeting at the Peak: The two searches meet at a mutual peak node (such as a highway corridor).
- Path Unpacking: We trace back and unpack the precomputed shortcut edges back into their constituent street nodes.
This shrinks the routing search space from millions of nodes down to a few thousand, dropping execution times from seconds to under 10ms!"
Dynamic Traffic Pipeline
Static routes are useless without traffic updates.
- Telemetry: Active app users send location pings containing
segment_id,speed, andtimestampevery 5 seconds. - Flink Stream Aggregation: Aggregates pings per road segment over a sliding 1-minute window to calculate current average speed.
- Weight Adjustments: If a road segment becomes congested (e.g., speed drops from 80kph to 10kph), the Flink job updates the edge weight in the Routing Graph Engine.
- Live ETA: The routing service dynamically queries the weighted graph, ensuring ETAs adapt to live traffic changes instantly.
Step 8 — Complete Architecture
Let's assemble our individual services, caches, pipelines, and databases into a unified, end-to-end architecture that is fully redundant and optimized for zero-downtime scaling:
Subsystems Summary
- Static Tile Subsystem: Serves vector tile assets directly from Cloud Storage through a CDN. Extremely fast, lightweight.
- Search Subsystem: Handles place queries. Utilizes Trie for autocomplete suggestions and Elasticsearch for spatial, geo-bounded keyword matching.
- Routing Subsystem: Computes directions. Graph representation is stored in memory across multiple routing nodes.
- Traffic Subsystem: Ingests live telemetry. Uses Kafka to absorb massive write loads, Apache Flink for real-time traffic computations, and writes back into the routing graph weights.
Step 9 — Failure Handling & Resilience
"A system of this scale must be designed to withstand failures at any tier. If our API Gateway or regional databases go offline, the clients must degrade gracefully without displaying an empty screen."
Offline Map Failbacks
If a client loses network connectivity, the system degrades gracefully:
- Client Cache: The client app stores vector tiles of the local area in a local SQLite/IndexedDB cache.
- Offline Packs: Users can download regional maps offline. The client contains a micro-routing engine (compiled to WebAssembly/Native C++) that calculates paths locally without contacting the cloud.
Partitioning & Sharding the Graph
"We cannot fit the entire planet's graph on a single routing machine's RAM. We must partition the graph. I would implement geographic sharding coupled with high-level transit border gateways."
- Geographic Partitioning (Country-Level): The graph is sharded by country or continent (e.g., North America Graph, Europe Graph, India Graph) because most drives do not cross oceans.
- Transit Nodes (Cross-Border): For international trips (e.g., driving from France to Germany), we use a high-level "border transit graph" connecting country gateways. The router solves the local path to the border, the border-to-border path, and then the path inside the destination country. This keeps routing nodes completely isolated.
Resilience Patterns
- Caching ETAs: If the routing engine fails or becomes slow, the system falls back to cached historic ETAs between major coordinates.
- Rate Limiting: API Gateway applies Token Bucket rate limiting per IP to prevent DDoS or aggressive scraper bots from scraping map tiles.
- Circuit Breakers: If the Telemetry service fails, routing continues using historical average speeds rather than crashing.
Step 10 — Interview Closing & Follow-up Questions
Complete System Recap
"To recap, we have designed a complete, production-grade map and navigation system:
- Tile Rendering: Pyramid-structured Vector Tiles served via dynamic cache pools on CDN edges.
- POI Search: Low-latency autocomplete cached in Redis with spatial Elasticsearch searches utilizing S2 cells.
- Continental Routing: Real-time Directions in under 10ms using bidirectional Dijkstra over Contraction Hierarchies.
- Dynamic Traffic: Scalable Kafka telemetry pings aggregated via Apache Flink to recalculate routing edge weights in real-time.
- Resilient Architecture: Multi-tier partitions dividing continental graphs connected by isolated transit border gateways.
This setup represents the architecture patterns powering industry systems like Google Maps and Apple Maps."
Common Follow-up Questions
"How do you update the road graph when new roads are built or closed?"
Road changes are processed in an offline batch pipeline. Changes come from government databases, satellite imagery updates, and driver telemetry anomalies (e.g., 50 drivers crossing a blank spot on the map suggests a new road). The map data is updated in the central PostGIS database, and a batch job regenerates the vector tiles and re-calculates the Contraction Hierarchies graph nightly.
"How do you handle GPS noise in tunnels?"
We use Dead Reckoning and Map Matching.
- Dead Reckoning: The client app uses device sensors (accelerometer, gyroscope, and compass) to estimate movement when GPS is lost.
- Map Matching: Since cars cannot drive through buildings, we use a Hidden Markov Model (HMM) to snap noisy or floating GPS coordinates back to the nearest plausible road segment in the graph.
Final Principles
These five principles appear throughout every layer of this design:
Separate Read and Write Channels — Serve map reads via CDN; ingest telemetry writes via Kafka. Do not mix them.
Leverage Client-Side Power — Use vector tiles to offload rendering to the client's GPU, minimizing server bandwidth.
Geospatial Simplification — S2 geometry converts complex spherical trigonometry into blazing fast integer database range lookups.
Precompute the Hard Math — Build contraction hierarchies offline so real-time routing is just a lightweight tree traversal.
Degrade Gracefully — Fail down from live traffic to historical traffic, and from online servers to local client caches.
Complete Series Summary
| Step | Topic | Key Concept |
|---|---|---|
| 1 | Requirements | Focus core features, establish latency/scale bounds |
| 2 | Data Model | Polyglot storage: CDN, Elasticsearch, In-memory Graph |
| 3 | API Design | Vector tile endpoints, autocomplete, routing |
| 4 | High-Level Design | Multi-tier flow, decouple static tile paths from dynamic routing |
| 5 | Tile Rendering | Hierarchical Tile Pyramids, Vector vs. Raster Tiles |
| 6 | Spatial Search | Geohashes, S2 Cells, Hilbert space-filling curve |
| 7 | Routing | Dijkstra limits, A* heuristics, Contraction Hierarchies |
| 8 | Architecture | Integrated data flows, Kafka ingest, Flink stream processing |
| 9 | Failure Handling | Geo-partitioned graphs, offline caches, transit nodes |
| 10 | Closing | Edge-cases (GPS noise, new roads), Dead Reckoning |
You now have everything needed to walk into a system design interview for Google Maps or Apple Maps and answer with confidence, structure, and depth.