CMD Guide
HomeSystem DesignSystem Design Problems

Designing Google Maps — Tiles, Routing Tiles & ETA, Traced

Three services wearing one app

"Design Google Maps" is really three loosely related systems, and conflating them is the most common way the question goes wrong:

They have different shapes, different bottlenecks and different failure modes. The connective tissue between them is one idea used twice: precompute into immutable tiles, addressed by coordinates.

Scope worth pinning down

First, the projection problem

The Earth is a sphere and screens are flat, so a map projection is required and every projection distorts something. Web maps almost universally use Web Mercator, which preserves angles (shapes look right locally, and a road meeting another at 90° renders at 90°) at the cost of grossly inflating area far from the equator — the reason Greenland looks continental. Mercator is chosen because local shape-correctness is what navigation needs, and because it maps the world onto a square that subdivides cleanly into four quadrants — which is precisely what makes the tile scheme below work.

Panel A shows the map tile pyramid: zoom level 0 is one 256 by 256 pixel tile of the whole world, zoom 1 is four tiles, and each zoom level quadruples the count until zoom 20 has about ten to the twelfth tiles totalling hundreds of terabytes of images. Because a tile is immutable and identified by z, x and y coordinates it becomes a cacheable URL served from CDN, moving rendering off the request path. Panel B shows hierarchical routing for a route from A to B: detail tiles with local roads near each endpoint, arterial tiles next, and a coarse highway layer with few nodes spanning the long middle. Routing tiles carry a graph of intersections and road segments with travel-time weights plus boundary nodes so tiles stitch together without loading the whole planet.
Panel A shows the map tile pyramid: zoom level 0 is one 256 by 256 pixel tile of the whole world, zoom 1 is four tiles, and each zoom level quadruples the count until zoom 20 has about ten to the twelfth tiles totalling hundreds of terabytes of images. Because a tile is immutable and identified by z, x and y coordinates it becomes a cacheable URL served from CDN, moving rendering off the request path. Panel B shows hierarchical routing for a route from A to B: detail tiles with local roads near each endpoint, arterial tiles next, and a coarse highway layer with few nodes spanning the long middle. Routing tiles carry a graph of intersections and road segments with travel-time weights plus boundary nodes so tiles stitch together without loading the whole planet.

Rendering: why tiles, and why they are static files

The naive design renders a custom image for each user's viewport. That is catastrophic: you would run a rendering farm sized for peak concurrent panning, and cache nothing, because every viewport is slightly different.

Instead, quantize. The world is divided into a grid of tiles, typically 256×256 pixels, at discrete zoom levels. Zoom 0 is one tile containing the whole world; each level subdivides every tile into four, so level z has 4ᵢ tiles. A client computes which tiles its viewport covers and requests them by (z, x, y) — a pure function of coordinates, which means a tile is a URL.

Everything good follows from that:

The cost is the 4ᵢ explosion: zoom 20 implies ~10¹² tiles, hundreds of terabytes. Nobody renders all of them — you pre-render deep zooms only where people actually look (cities), and render rare deep tiles on demand, caching the result. The long tail of the planet at maximum zoom is mostly ocean and desert that no one requests.

Two refinements worth naming: vector tiles ship geometry rather than pixels, letting the client restyle, rotate and label without new downloads — smaller over the wire, more client CPU. And tiles split by layer (base map, traffic overlay, labels) so the volatile traffic layer can have a 1-minute TTL while the base map has a 30-day TTL. Mixing a fast-changing overlay into the base tile would destroy its cacheability, which is the same change-rate-boundary rule that governs cache tiers in a news feed.

Navigation: the road graph, and why plain Dijkstra loses

Roads are a weighted directed graph: nodes are intersections, edges are road segments, and edge weight is travel time (not distance — a 2 km jam is worse than 5 km of open highway). Direction matters for one-way streets, and turn restrictions are edge-to-edge constraints rather than node properties.

Now the scale problem. A continental road network has hundreds of millions of nodes. Dijkstra explores outward uniformly and would visit most of them for a long route; A* with a straight-line-distance heuristic prunes toward the destination and helps materially, but neither is remotely fast enough for a cross-country route at interactive latency, and neither fits in one machine's memory.

Two structural ideas fix it:

This is why "use Dijkstra" is an incomplete answer: the algorithm is not the hard part, the graph representation is. Real systems go further with precomputation such as contraction hierarchies, which shortcut whole paths offline so queries become near-lookups — the same precompute-then-serve instinct as the map tiles, applied to paths.

ETA, and why it needs the location service

An ETA is the sum of edge travel times along the route, but the useful version is time-dependent: an edge's weight changes with time of day and with live traffic. So edge weights come from two sources — historical speed profiles per road per time-of-day-and-weekday, and a live speed estimate derived from current device telemetry. The genuinely hard part is that a long route finishes in the future: for the final leg, you must predict conditions at the time the driver will arrive there, not conditions now. That is why ETA is a prediction problem layered on a graph problem, and why it can be confidently wrong in ways routing cannot.

Location ingestion: the write-heavy half

Traffic data comes from users' devices reporting position and speed. This is the highest-volume path in the system and the design choice is batching versus streaming:

The right answer is both, chosen by role: batch from passive users (whose value is aggregate traffic, where seconds do not matter), stream for the actively-navigating user (where they do). Ingested points land on a message queue, then feed two consumers — a stream processor updating live road speeds, and a warehouse accumulating history for the speed profiles.

Partitioning: location writes are partitioned geospatially, with geohashing turning a latitude/longitude pair into a string prefix so nearby points share a prefix and land on the same shard. That gives locality for the "what is the speed on this road right now" query. It also inherits geohash's known weaknesses — neighbouring points can straddle a prefix boundary and land far apart, and dense cities create hot shards while oceans create empty ones. (Both are covered in this guide's Geohashing & Quadtrees page.)

Privacy is a design constraint here, not a footnote

A continuous location feed keyed to a user is among the most sensitive data any system holds. The mitigations are architectural: aggregate early so the traffic layer stores road speeds rather than trajectories; retain raw per-device points only briefly; and require a minimum number of contributing devices before publishing a segment's speed, so an individual's movement cannot be inferred from a lightly-travelled road. A design that stores raw trajectories indefinitely has created a liability that no amount of access control fully contains.

Which approach, when

DecisionOptionChoose whenBreaks when
RenderingPrecomputed raster tiles + CDNMass-market map viewingClient needs restyling/rotation; you re-render for every theme
RenderingVector tilesClient restyling, rotation, offline mapsWeak client CPU; more complex renderer
RenderingRender per requestCustom cartography, low traffic, internal toolsConsumer scale — you cache nothing
RoutingHierarchy + routing tilesContinental routing at interactive latencyComplex build pipeline; hierarchy can miss clever local shortcuts
RoutingA* on the full graphCity-scale, or a single metro areaLong routes — search space and memory both explode
RoutingContraction hierarchiesStatic weights, fastest possible queriesLive traffic — changing weights invalidates precomputation
LocationBatched uploadPassive users feeding traffic aggregatesActive navigation — rerouting must be immediate
LocationStreaming uploadActively navigating usersApplied to everyone — battery and ingest cost explode

Note the tension in rows 6 and 3 together: the more you precompute, the worse you handle live traffic. Contraction hierarchies give near-instant routes over a graph whose weights you promised not to change; live traffic changes weights constantly. Production systems resolve this by precomputing over stable weights and applying live traffic as a correction on a limited set of affected edges — accepting that a route during an unusual jam is good rather than provably optimal.

Pitfalls

Cost model — what dominates the bill

Maps is egress-dominated, and by a wide margin. Tiles are images requested constantly by hundreds of millions of devices, which makes CDN hit rate the single most important number in the entire cost structure.

Rough BOTE. Say 100 million daily active users, each loading ~50 tiles per session at ~20 KB per tile: that is 100M × 50 × 20 KB = 100 TB/day of tile traffic, ~3 PB/month. At roughly $0.02/GB CDN egress, 3 PB would be about $60,000/month if every byte were billable — and this is precisely why the numbers that matter are client cache and CDN offload. A 90% client-cache hit rate turns that into $6,000/month; the same traffic served from origin instead of CDN would cost several times more and saturate the origin.

Storage is large but cheap and one-time-ish: hundreds of TB of pre-rendered tiles at ~$0.02/GB-month is on the order of several thousand dollars monthly, and it does not grow with traffic. The location pipeline is the surprising one: at 100 million devices reporting a batch every 30 seconds, that is 100,000,000 / 30 ≈ 3.3 million writes/second into the ingest tier — a genuinely large streaming system whose cost is compute and queue throughput, not storage, since raw points are discarded quickly.

Dominant line items: CDN egress for tiles; then location-ingest stream processing; then tile storage; then routing compute (surprisingly modest, because the hierarchy makes each query cheap).

Levers, in order of leverage: (1) aggressive client-side tile caching with long TTLs on the base map — every cache hit is a request that costs nothing; (2) vector tiles, which are smaller over the wire and let one download serve multiple styles and rotations; (3) batching location uploads, which cuts ingest volume proportionally to the batch window; (4) rendering deep-zoom tiles on demand rather than pre-rendering the planet.

Operability: the fingerprints of a broken maps stack

The three subsystems fail in recognizably different ways. CDN hit rate dropping while traffic is flat is nearly always a cache-key or TTL change — a new query parameter on the tile URL, or traffic data leaking into base tiles — and it shows up as an origin bandwidth spike and a cost alarm rather than as a user-visible error. Blank or checkerboard tiles at high zoom in one region means the pre-render pipeline failed for that area or the on-demand renderer is timing out; the region-shaped blast radius is the giveaway.

Routes that refuse to cross a particular line on the map is the boundary-node fingerprint — a routing tile built without correct boundary stitching, which manifests as absurd detours or outright failures for origin/destination pairs that straddle one specific edge. ETAs accurate for short trips and badly wrong for long ones means the model is applying current conditions across the whole route rather than predicting forward.

On the ingest side, one geospatial shard running far hotter than its peers is a dense-city hot shard, which is inherent to geohash partitioning rather than a bug — the fix is finer subdivision for that prefix, not more hardware. Live road speeds going stale in a region while ingest volume looks healthy usually means the minimum-device threshold is suppressing publication, which is the privacy guard behaving correctly on a quiet road and incorrectly if the threshold is set too high for rural coverage.

Signals worth having: CDN hit rate split by tile layer and zoom, origin egress, tile-render queue depth and on-demand render latency, route failures grouped by tile pair, ETA error distribution bucketed by trip duration, ingest writes/second with per-shard skew, and road-segment staleness with suppressed-by-threshold counts.


Authored for this guide to cover the Google Maps design (Alex Xu Vol. 2, ch. 18 — not present in the Vol. 1 PDF); tile-pyramid and hierarchical-routing diagram hand-authored as SVG. Builds on this guide's Geohashing & Quadtrees, CDN, and (DSA) Dijkstra / A* pages; see also "Designing Uber — Geospatial Matching, Traced" for the matching side of geospatial systems.

🤖 Don't fully get this? Learn it with Claude

Stuck on Designing Google Maps — Tiles, Routing Tiles & ETA, Traced? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Designing Google Maps — Tiles, Routing Tiles & ETA, Traced** (System Design) and want to truly understand it. Explain Designing Google Maps — Tiles, Routing Tiles & ETA, Traced from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Designing Google Maps — Tiles, Routing Tiles & ETA, Traced** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Designing Google Maps — Tiles, Routing Tiles & ETA, Traced** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Designing Google Maps — Tiles, Routing Tiles & ETA, Traced** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes