Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

System Design (High-Level Design)  Module 25 – Case studies: location, mobility and logistics

Design ride hailing: trips, pricing, payments

Design the trip side of ride hailing like Uber or Ola: requirements, estimates and a first design for trips, fares, surge pricing and payments.

  • Advanced
  • 30 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0 and quickjs 0.32.0
  • By MySmartCoPilot
Free intro, Pro: 6 of 30 minutes free
Free: About 1,390 trip changes a second, and none may happen twice. With a pass: the other 14 sections and 3 interview answers, about 24 minutes.

What you will learn

  • Estimate trip changes, storage, payment calls and pricing cells for a national ride-hailing service
  • Implement a trip as a state machine whose commands are idempotent and conditional on its version
  • Compute surge multipliers from supply and demand per cell, smoothed so that they do not flap
  • Design card payments as a saga that holds at booking, captures once and compensates on cancellation
  • Record wallet, cash and pay-after-ride payments in a balanced ledger that receipts and payouts read

Before you start

What you’ll learn · Before you start

What you will learn

  • Estimate trip changes, storage, payment calls and pricing cells for a national ride-hailing service
  • Implement a trip as a state machine whose commands are idempotent and conditional on its version
  • Compute surge multipliers from supply and demand per cell, smoothed so that they do not flap
  • Design card payments as a saga that holds at booking, captures once and compensates on cancellation
  • Record wallet, cash and pay-after-ride payments in a balanced ledger that receipts and payouts read

Before you start

On this page

Once a driver accepts, a ride becomes a trip: a record that changes state about 6 times in half an hour, carries a fare the rider agreed to, and ends with money moving between the rider, the driver and the platform. Across 8 million trips a day that is about 1,390 changes a second in the busiest hour, and none of them may happen twice.

The design rests on three ideas. The trip is a durable state machine: every change is a command with an idempotency key, applied only if it is legal and the trip is still at the version the sender saw. The fare is quoted and locked when the rider confirms, with a surge multiplier smoothed so that it does not flap. And payment is a saga: the money is held at booking, captured when the trip ends and released on cancellation.

Not affiliated

Based on public information about Uber and Ola. MySmartCoPilot is not affiliated with Uber and Ola, and Uber and Ola’s real architecture may differ from what this lesson describes.

About 1,390 trip changes a second, and none may happen twice

Ask these first, and note the answer you assume:

  • Where does it start? When a driver accepts an offer, where matching ends.
  • How big? 8 million trips a day, each changing state about 6 times; 60 % paid by card, the rest from a wallet, in cash or through a bank app after the ride.
  • How are fares set? A quote before booking, from distance, time and a surge multiplier, honoured unless the trip changes materially.
  • What goes wrong? Phones retry and drop off the network, riders and drivers cancel, payments fail or report late, and riders dispute fares.
  • What must never happen? A rider charged twice, a driver unpaid for a completed trip, or a fare different from the quote without a stated reason.

So every state change must survive a retry or a lost reply without happening twice, a quote must still be honoured minutes later, and a failure of the payment gateway must never stop trips from ending.

Quantity Assumption Result
Trip state changes, busiest hour 8 million × 6 ÷ 86,400 s × 2.5 about 1,390 a second
Trip records 5 KB each, 3 copies, a year about 44 TB a year
Route traces a point every 4 s for 20 minutes, 16 bytes each about 4.8 KB a trip, 38 GB a day
Payment gateway calls, busiest hour 60 % of trips by card, a hold and a capture each about 280 a second
Pricing cells per city 600 km² at H3 resolution 8, 0.737 km² a cell about 815

The cell size comes from H3’s table of average hexagon areas (H3, cell statistics). None of these numbers is large; what makes the trip side hard is correctness under retries, money that moves in several steps, and prices that change by the minute.

Exercise · Easy · Python

Size trip events, storage, payment calls and pricing cells

Write four functions in sizing.py for the estimate sheet of the trip side of ride hailing. A day has 86,400 seconds, a year 365 days, and 1 TB is 10¹² bytes.

  • trip_events_per_second(trips_per_day, events_per_trip, peak_factor): state changes a second in the busiest hour, when that hour runs at peak_factor times the day's average rate.
  • trip_storage_tb_per_year(trips_per_day, bytes_per_trip, copies): the storage a year of trip records needs, kept in copies copies.
  • payment_calls_per_second(trips_per_day, card_share, calls_per_card_trip, peak_factor): calls to the payment gateway a second in the busiest hour, when a share card_share of trips is paid by card.
  • pricing_cells(city_km2, cell_km2): how many cells cover a city, rounded up, so that surge can be computed for each.

The sample tests accept each figure within 10 %, as an estimate should.

Starter code · sizing.py

import math

DAY_SECONDS = 86_400


def trip_events_per_second(trips_per_day, events_per_trip, peak_factor):
    """Trip state changes a second in the busiest hour."""
    # Replace this line with your code.
    return 0


def trip_storage_tb_per_year(trips_per_day, bytes_per_trip, copies):
    """TB a year of trip records, in `copies` copies."""
    # Replace this line with your code.
    return 0


def payment_calls_per_second(trips_per_day, card_share, calls_per_card_trip, peak_factor):
    """Payment gateway calls a second in the busiest hour."""
    # Replace this line with your code.
    return 0


def pricing_cells(city_km2, cell_km2):
    """Cells that cover the city, rounded up."""
    # Replace this line with your code.
    return 0
The sample tests · test_sizing.py
import math

from sizing import payment_calls_per_second, pricing_cells, trip_events_per_second, trip_storage_tb_per_year


def near(actual, expected):
    """Within 10%, as an estimate should be."""
    return math.isclose(actual, expected, rel_tol=0.10)


def test_trip_events():
    """state changes spread over the day, times the peak factor"""
    assert near(trip_events_per_second(8_000_000, 6, 2.5), 1_389)
    assert near(trip_events_per_second(500_000, 8, 3), 139)


def test_trip_storage():
    """a year of records in every copy"""
    assert near(trip_storage_tb_per_year(8_000_000, 5_000, 3), 43.8)
    assert near(trip_storage_tb_per_year(500_000, 4_000, 2), 1.46)


def test_payment_calls():
    """only card trips call the gateway, twice each"""
    assert near(payment_calls_per_second(8_000_000, 0.6, 2, 2.5), 278)
    assert near(payment_calls_per_second(500_000, 0.3, 3, 3), 15.6)


def test_pricing_cells():
    """cells that cover the city"""
    assert near(pricing_cells(600, 0.737), 815)
    assert near(pricing_cells(600, 5.16), 117)
A hint

Rates are trips_per_day * per_trip / 86_400 * peak_factor; the payment rate also multiplies by card_share. Storage multiplies trips, bytes, 365 and copies, then divides by 10**12. For cells, divide the areas and round up with math.ceil.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Checkpoint

Check the estimate

  1. Question 1 of 2 8 million trips a day each change state about 6 times, and the busiest hour carries 2.5 times the average rate. How many trip changes a second is that in the busiest hour?

    Type a number.

    A hint

    A day has 86,400 seconds.

    Show the answer: Question 1 of 2

    Answer: 1389 changes a second (anything from 1250 to 1528 counts)

    8,000,000 × 6 = 48 million changes a day; ÷ 86,400 ≈ 556 a second on average, and × 2.5 ≈ 1,389 in the busiest hour. A small number for a database: the hard part is that none of those changes may happen twice.

  2. Question 2 of 2 60 % of the 8 million daily trips are paid by card, each with a hold and a capture at the payment gateway. With the busiest hour at 2.5 times the average, how many gateway calls a second is that?

    Type a number.

    Show the answer: Question 2 of 2

    Answer: 278 calls a second (anything from 250 to 306 counts)

    8,000,000 × 0.6 × 2 = 9.6 million calls a day; ÷ 86,400 ≈ 111 a second, and × 2.5 ≈ 278 in the busiest hour, about 280.

The first design: a trip store, a pricing service and a payment saga

Trips: apps, API gateway, pricing with supply and demand counts, trip service and store, event log, payment saga, gateway and ledger.Rider and driver appsAPI gatewaySupply and demandper cell, each minutePricingquotes and surgeTrip servicestate machineTrip storeversions and keysTrip event logPayment sagaPayment gatewayLedgerReceipts, payouts,disputesquotecommandsconditionalwritesoutboxhold, capture,cancel

The first complete design of trips, pricing and payments

Text description of the diagram

The diagram shows the parts of the trip side of ride hailing from top to bottom.

  1. The rider and driver apps call the API gateway.
  2. For a quote, the gateway calls the pricing service, which reads supply and demand counts per cell, refreshed each minute.
  3. Commands that change a trip go to the trip service, a state machine that writes to the trip store with conditional writes; the store keeps each trip's version and the idempotency keys it has seen.
  4. Every change also writes an outbox row, which is published to the trip event log.
  5. The payment saga reads the log and asks the payment gateway to hold, capture or cancel payments, recording every movement of money in the ledger.
  6. Receipts, driver payouts and disputes also read the trip event log.

Apps get a quote from the pricing service, which reads supply and demand counts per cell. Commands that change a trip go to the trip service, which writes them to the trip store with conditional writes and an outbox row for each change. The trip event log feeds the payment saga, which talks to the payment gateway and records every movement of money in the ledger, and the services that issue receipts, pay drivers and handle disputes. Follow one trip through it:

Interactive · step by step One trip through the first design Step through one trip, from the quote the rider confirms to the receipt, and see which part does each step.
One trip through the first design, step 8 of 8. Showing Rider and driver apps, Pricing service, Trip service (trip booked), Trip store (arrived, with an outbox row), Trip event log, Payment saga, After the trip, Payment gateway (captured), Ledger. In focus: After the trip.quotecommandsoutboxRider and driver appsPricing servicecounts per cellTrip servicetrip bookedTrip storearrived, with an outbox rowTrip event logPayment sagaAfter the tripreceipts, payouts, disputesPayment gatewaycapturedLedgertrip events

The drawing shows the whole scene; its 8 steps are listed below.

Every step as text
  1. A quote. The rider's app asks the pricing service for a fare. Pricing reads the counts of requests and drivers in the rider's cell and returns a quote.
  2. Booked. The rider confirms the quote, a driver accepts, and the trip begins. Every change from now on is a command to the trip service.
  3. A command. The driver's app sends a command, such as arrived. The trip service writes it to the trip store with a conditional write, and an outbox row with it.
  4. An event. The outbox row is published as an event in the trip event log, where everything after the trip reads it.
  5. A hold. The payment saga reads the booking and places a hold on the rider's card through the payment gateway.
  6. A capture. When the completion event arrives, the saga captures the fare through the gateway.
  7. The ledger. Every movement of money is recorded in the ledger, entry by entry.
  8. After the trip. Receipts, driver payouts and disputes are built from the same events, without touching the trip.

The questions this design has to answer:

  • Trip states: commands that are retried, reordered or sent by two people at once each apply once or not at all.
  • Fares: a quote is locked when the rider confirms and honoured unless the trip changes materially.
  • Surge: supply and demand per cell set a multiplier that follows real changes without flapping every minute.
  • Payments: a hold at booking, a capture at the end and a release on cancellation form a saga.
  • Cash and paying later: a ledger keeps what each driver and rider owes exact.
  • After the trip: receipts, payouts and disputes are built from the same events.

An answer is judged on the estimates, the trip’s states, fares and surge, payments by each method, what follows the trip, and failures.

Checkpoint

Check the first design

  1. Question 1 of 2 In the first design, how do the payment saga and the receipts learn that a trip has changed?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: From the trip event log, fed by the outbox row the trip service writes with each change

    The trip service writes a change and its outbox row to the trip store; the row is published to the trip event log, which feeds the payment saga and the services for receipts, payouts and disputes.

  2. Question 2 of 2 In the first design, when does the payment saga capture the fare from the rider's card?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: When the completion event arrives, at the end of the trip

    A hold at booking, a capture at the end and a release on cancellation: the payment saga's three moves for a card, each started by an event in the trip event log.

A trip applies each command once, at the version its sender saw Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • A quote locked at booking is the fare unless the trip changes
  • Smoothing keeps surge from changing 26 times an hour
  • A card payment holds at booking and captures once at the end
  • Wallets, cash and paying later meet in one ledger that sums to zero
  • Receipts, payouts and disputes read the trip’s events, never the trip
  • What gives way at 10× and 100×
  • Failure drills: trips end even when payments fail
  • Alternatives considered and rejected
  • What each level is expected to cover
  • A sample answer and a 45-minute self-mock
  • Practise: the whole design, one question at a time
  • Interview questions
    • On a bad network a driver's phone sends "complete trip" three times, and one copy arrives ten minutes later, after the rider has already disputed the fare. Design the trip's state changes so that this can never charge twice or reopen a settled trip.
    • During evening rain, the surge multiplier in one area jumps between 1.0 and 2.1 from minute to minute, and riders complain that the price changes every time they open the app. Design the surge computation so that prices follow real changes without flapping.
    • Design payment for trips that can be paid by card, from a wallet or in cash, where the final fare is known only at the end and the rider may cancel at any point. Where are the points of no return, and what does each failure undo?
  • Key takeaways

3 diagrams · 4 runnable examples (JavaScript, Python) · 3 interview answers · about 50 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Read a complete sample first: Design ride hailing like Uber or Ola: matching

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.