System Design · step by stepDesign Uber
Step 1 / 9

Design Uber (Ride-Hailing) — the walkthrough in full

A written version of the interactive walkthrough above — the same steps, decisions and trade-offs, laid out for reading, reference and search.

The big idea

What is Uber, really?

Strip away the brand and Uber does one deceptively simple thing: connect a person who wants a ride with a nearby car — in seconds, anywhere, all at once.

That tiny sentence hides three brutally hard problems. Where is everyone right now? Who is the best match? And how do we do this for millions of people at the same time without falling over?

How to read this: Each step opens with a real design decision — you make the call before I show you what ships. Then watch the diagram on the right grow. Hover any box, replay the flow, and at the end kill the matcher to see what breaks. Hit Begin.

Step 1 · The skeleton

Two apps and a server

The rider’s phone and the driver’s phone can’t talk to each other directly — phones move, change networks, and can’t be trusted to coordinate fairly. How should they connect?

Design decision: How do the two phones coordinate a ride?

The call: A server in the middle decides, and a database remembers. — A neutral backend holds the single source of truth, makes the matching decision, and survives any one phone going offline. This is the spine of every app you use.

We put a server in the middle. Both apps send requests to it; it does the thinking and remembers everything in a database. This is the client → server → database pattern under every app you use.

Client–server model: The phones are clients (they ask). The server is the brain (it decides). The database is memory (it never forgets). Keep these three roles separate and everything else gets easier.

Step 2 · Eyes on the road

Where is everyone right now?

To match a rider with a car, we need to know where every driver is — and they’re moving. A location from 2 minutes ago is useless. Where does a position that changes every few seconds belong?

Design decision: ~5M drivers each ping their GPS every ~4s. Where do you keep "current position"?

The call: Overwrite the latest position in an in-memory store (Redis). — Live positions are tiny, change constantly, and don’t need to survive a crash. RAM gives microsecond writes; you keep only "now", not history. Storage matched to behaviour.

Each driver app sends a GPS ping every ~4 seconds to a dedicated Location Service. It overwrites the driver’s "current position" in a super-fast in-memory store (Redis). We don’t keep history — we only ever care about now.

Hot path vs durable data: Live positions change constantly and don’t need to survive a crash, so they live in RAM (Redis), not a slow disk database. Match the storage to how the data behaves.

Step 3 · The clever bit

Who’s near me?

A rider taps the screen. We can’t scan all 5 million drivers to find nearby ones — that would take forever, on every single request. How do you make "who’s near this point?" fast?

Design decision: A rider needs nearby drivers. How do you avoid scanning all 5M of them?

The call: Bucket the map into cells and only look at nearby cells. — A spatial index (Uber’s H3 hexagons, or a geohash/quadtree) turns "search everyone" into "look in my cell + its neighbours" — a few dozen drivers instead of millions.

We chop the world into small cells (Uber uses hexagons called H3; the textbook version is a geohash or quadtree). Every driver lives in a cell. "Near me?" becomes "who’s in my cell and the 6 around it?" — a handful of cells instead of millions of drivers.

Geospatial indexing: An index turns "search everything" into "look in the right drawer." By indexing drivers by location cell, a nearby-search drops from millions of comparisons to a few dozen.

Step 4 · The match

Pick the right driver

Five cars are nearby. Which one gets the trip? Closest isn’t always best — a car 2 min away facing the wrong way down a highway loses to one 3 min away pointed right at you. How do you choose?

Design decision: The geo index returns 5 nearby drivers. Who gets the trip?

The call: Score on real ETA, heading and rating; offer to the best, fall back if declined. — Dispatch optimizes the thing riders actually feel — time-to-pickup — and respects driver choice. Decline → instantly offer the next best. This is the heart of the product.

The Matching Service (Uber calls it dispatch) asks the geo index for nearby drivers, scores them on real ETA, direction, and rating, then offers the trip to the best one. If they decline, it instantly offers the next.

This is the heart: Everything else exists to feed this decision. A good match means a short wait for riders and steady earnings for drivers — the core promise of the whole product.

Step 5 · Don’t fall over

Now do it 25 million times a day

One server was fine for a demo. At real scale it melts: too many requests, and if it dies, all of Uber goes dark. How do you carry 25M trips a day and survive a machine dying?

Design decision: One server can’t hold 25M trips/day and is a single point of failure. What now?

The call: Load balancer + many copies of each service (scale out). — Run many small stateless instances behind a load balancer, split into microservices so location, matching and trips scale — and fail — independently. One dead box no longer takes the system down.

Put a load balancer in front and run many copies of each service (horizontal scaling). The single server becomes an API Gateway that routes traffic. Split work into independent microservices so location, matching, and trips can scale — and fail — on their own.

Scale out, not up: Instead of one giant machine ("scale up"), run many small ones behind a load balancer ("scale out"). Cheaper, and one dying machine no longer takes the system with it.

Step 6 · Make it feel alive

The car on the map that moves

After matching, the rider stares at a map watching the car approach. Asking the server "where’s my car now?" every second, for everyone, would crush it. How do live updates reach the phone?

Design decision: The rider wants the car to move on the map in real time. How?

The call: One persistent WebSocket per phone; the server pushes updates. — An always-open connection lets the server talk first — it sends a new position the instant it arrives, no request needed. Meanwhile services emit events to Kafka so they don’t block each other.

Open one persistent connection (a WebSocket) per phone through a Realtime Gateway, so the server can push updates the instant they happen. Meanwhile every service writes events to a Kafka stream — a buffer that lets services react without waiting on each other.

Push & decouple: WebSockets flip the model: the server talks first. Message queues like Kafka let a slow service catch up later instead of blocking a fast one — the system bends instead of breaking.

Step 7 · The trip & the money

From 'requested' to 'paid'

A trip isn’t one moment — it’s a journey through states, and money is involved. Lose a payment or double-charge someone and trust evaporates. How do you model the ride and handle the cash?

Design decision: A trip moves requested → accepted → in-progress → completed, then bills. How do you build it?

The call: A Trip Service runs a strict state machine in SQL; a separate Payment Service bills. — A state machine makes illegal transitions impossible; SQL gives the transactional guarantees money demands; isolating payments means billing bugs can’t break dispatch. Right tool, right job.

A Trip Service walks each ride through a strict state machine and stores it in a reliable SQL database, sharded by city so Delhi’s load never touches London’s. A separate Payment Service handles fares and payouts so billing bugs can’t break dispatch.

Right tool, right job: Live locations → fast in-memory store. Money & trips → strict SQL with guarantees. Sharding splits one giant database into per-city pieces so each stays small and fast.

You did it

You just designed Uber.

  • C — l
  • L — i
  • G — e
  • M — a
  • L — o
  • W — e
  • T — r
built to be driven, not memorized — make the calls, kill the matcher, run the gauntlet.
Finished this one? 0 / 50 System Designs done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More System Designs