Skip to content

44. On-demand services: rides and delivery

The archetype. An app in which a customer asks for a ride or a delivery, a provider nearby accepts the job in a second app, both follow its progress on a live map, and the customer is charged when the job is done.

An on-demand service makes one promise. You ask for something now, you watch it come to you, and you pay without thinking.

Each clause of that promise costs something.

  • Ask now. The request is matched to a provider within seconds or minutes. The customer waits on work that only the backend can do.
  • Watch it come. A moving marker and an arrival time stay current for the whole job. The position comes from another person’s phone, on another network, in another app.
  • Pay without thinking. No payment screen appears at the end. The backend charges a saved payment method with the customer absent.

A fourth clause is silent. The provider’s side must keep working all day, through tunnels, basements and stairwells.

Four concerns dominate the design.

ConcernWhy it dominatesChapter
LocationThe product is a position that moves. One side uploads it in the background, the other draws itChapter 24
Real-time deliveryA state change or a position that arrives a minute late breaks the promiseChapter 14
PaymentsThe price is not known for certain until the end, and the customer is not there when it is chargedChapter 26
Network resilienceThe provider’s app changes the state of a job from places with no networkChapter 13

Two further chapters supply mechanisms. Chapter 23 covers the user-visible work that keeps the provider’s app uploading. Chapter 12 covers the outbox that holds a provider’s state changes through a network gap.

This chapter uses one word, trip, for the unit of work, whether it carries a person or a parcel. Where deliveries differ, the text says order.

The archetype has two main variants, set by the number of parties.

VariantPartiesWhat movesWhat the customer watches
RidesTwo: customer and providerThe customerThe provider coming, then the shared trip
DeliveriesThree: customer, courier and merchantGoodsThe merchant preparing, then the courier coming

A third party adds a third app, a second handover and a second clock. Section 44.6 covers the further axes on which products differ.

These are requirements as a user experiences them. None of them names a design.

What the app does, for the customer

  • Set a pickup point and a destination, or choose a merchant and fill a basket.
  • See a price and an arrival time before confirming.
  • Confirm once, and see the request accepted by a named provider.
  • Follow the provider on a map, with a state and an arrival time that stay current.
  • Call or write to the provider, and be called or written to.
  • Pay with a saved payment method, with no action at the end. Tip and rate afterwards.
  • See a receipt and a history of past trips, and cancel within stated rules.

What the app does, for the provider

  • Go online and offline.
  • Receive an offer with a time limit, and accept or decline it.
  • Be guided to the pickup point and then to the destination.
  • Report each step: arrived, picked up, dropped off.

In a delivery, a merchant also receives the order, accepts it, states a preparation time and marks it ready.

Qualities it must have

  • A request is made once. A second tap, a retry or a crash never produces two trips or two charges.
  • Every party sees the same state. The customer never sees “on the way” while the provider sees “canceled”.
  • The marker moves smoothly and is rarely more than a few seconds behind reality.
  • The arrival time is believable.
  • The provider’s steps are never lost, whatever the network does.
  • The provider’s phone lasts a working day.
  • The price is the product’s decision. Neither app can alter it.
  • Neither party learns the other’s phone number.

Of the seven constraints of Chapter 2, four bind hardest. Unreliable network and limited resources shape the provider’s app. Untrusted client shapes price, payment and the handling of positions. Platform rules decide whether an app may read location in the background at all.

You can run probes on the customer’s app only. The provider’s and merchant’s apps need accounts that you do not have, and you should not create one for a teardown. What this chapter says about those apps is read from what the customer’s app shows.

One rule governs every probe in this chapter. A request sends a real person toward you. Never make a request that you do not intend to complete and pay for. The probes either stop before the confirm control, or observe a trip that you were going to take anyway. Chapter 4 describes the probe families and how to run a probe cleanly.

Run these from Chapter 24 on the customer’s app.

ProbeTypical outcome for this archetypeWhat it tells you
P24.1 Location indicator on screenOn while a map screen is open and off after leaving itContinuous while on screen. The customer’s position is used to set the pickup point
P24.2 Location indicator in the backgroundOff within seconds and it stays offThe customer’s app does not track the customer. Some ride products keep it on during a trip, as a stated safety feature
P24.4 Location deniedIt asks you to type an address or pick a placeManual entry is always present, because people order for other places

P24.5 and P24.6 describe the provider’s side, which you cannot run. P44.5 watches their result from the customer’s side.

Run these from Chapter 14 during a trip that you were going to take anyway.

ProbeTypical outcomeWhat it tells you
P14.1 Foreground latencyA state change appears within about two secondsA held connection or a platform push while the trip screen is open
P14.2 Update rhythmMarker corrections at a short, similar intervalPositions are pushed, or polled on a short timer
P14.4 Background deliveryA notification arrives and the trip is current on openingPlatform push for each state change, and a fresh fetch on return
P14.8 Reconnect catch-upThe current state appears with no gestureThe client fetches the current trip record on reconnect. There is no backlog to replay, because only the latest state matters

Run these from Chapter 26 on the payment settings, not on a request.

ProbeTypical outcomeWhat it tells you
P26.1 Read the paywallPhysical goods or services, paid by card or walletThe external route. Store billing does not apply to a ride or a meal
P26.2 Start a checkout and cancelRun it on the screen that adds a payment method, and stop before saving. A provider web page, or fields in the provider’s styleCard details go to an external payment provider and not to the backend
P26.9 Saved payment methodsA list with a brand and the last four digitsThe backend holds a payment token and a few display details

Do not run P26.3 here. It asks you to abandon a checkout, and in this archetype the checkout is the request.

ProbeTypical outcomeWhat it tells you
P12.1 Offline read, from Chapter 12Past trips and receipts appear if visited. The first screen shows a map with no providersLevel 1 for history. Nothing live is kept
P12.2 Offline write. Stop at the confirm control, which is disabled offlineThe control is disabled or an error appearsA request is Level 0. It is never queued, for the same reasons as a payment
P13.1 Cut mid-request, from Chapter 13, on the trip screenAn offline message within about two secondsThe client watches connectivity and says so, because a frozen marker would mislead
P23.9 Offline, then reconnect, from Chapter 23One notification for the latest stateA replacement key for each trip, so that old states do not pile up
P25.5 Live status after a force-quit, from Chapter 25It keeps updatingA live status fed by a dedicated push

P44.1 and P44.2 stop before any request. P44.3 to P44.8 observe a trip you already intended to take. Run at most two or three of them on any one trip, and never when the provider is about to arrive.

Open worksheet
Do
  1. Enter a pickup point and a destination, or fill a basket, and go as far as the screen that shows the price.
  2. Note the price, the arrival-time estimate and the time on your clock.
  3. Leave the screen open and untouched for ten minutes. Note any change, and any message about the price.
  4. Go back, enter the same request again and compare the price.
  5. Turn on airplane mode and try to reach the price screen once more. Then turn it off.
Watch

Whether the price is one figure or a range, whether it changes or expires while you wait, and whether any price appears offline.

Caution. Stop at the screen that shows the price. Do not confirm. A confirmed request sends a real provider toward you.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

Do
  1. Open the first screen, the one with the map, in a busy area. If the app has no such map, record that and stop.
  2. Watch the provider markers for two minutes without touching the map.
  3. Time the gaps between movements of one marker.
  4. Turn on airplane mode and watch for one more minute. Then turn it off.
Watch

Whether markers glide, move in steps at a regular interval, or stand still, and what they do with no network.

Caution. Do not confirm a request. This probe only watches the first screen.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

Do
  1. Confirm a request you were going to make anyway.
  2. As soon as the app says it is looking for a provider, or waiting for the merchant, go to the home screen and lock the device.
  3. Leave it locked until something happens, or for five minutes.
  4. Note what told you that a provider accepted, then open the app.
Watch

Whether you learn of the match while the app is in the background, and what the app shows when it returns.

Caution. Run this only on a ride or order you intend to complete and pay for. Do not make a request for the sake of the probe.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

Do
  1. Wait until a provider is assigned and on the way.
  2. Note the state shown and the arrival-time estimate.
  3. Force-quit the app and wait two minutes.
  4. Open the app from its icon, not from a notification.
Watch

Which screen appears first, how long the trip takes to appear, and whether its state has moved on.

Caution. Run this only on a ride or order you intend to complete. Keep notifications on, so the provider can still reach you. Skip this probe if you are standing at a pickup point where you must be reachable at once.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

Do
  1. Open the tracking map while the provider is moving.
  2. Turn on airplane mode for one minute and keep watching the marker and the arrival-time estimate.
  3. Turn airplane mode off and keep watching for thirty seconds.
Watch

What the marker does while offline, whether the app says it is offline, and how the marker gets to its new position afterwards.

Caution. Run this only on a ride or order you already have. Keep the gap to one minute, and do not run it in the last minutes before the provider arrives.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

Do
  1. During an active trip, sign in to the same account on a second device, or open the trip's share link there.
  2. Place the two devices side by side.
  3. Compare the state, the arrival-time estimate and the marker position.
  4. Keep watching through one change of state, such as the provider arriving.
Watch

Whether both devices show the same state and estimate, and how far apart in time a change appears on the two.

Caution. Use a trip you already have, and a second device of your own. If the app offers only a share link, send it to yourself.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

Do
  1. During an active trip, open the control for contacting the provider.
  2. Note what is offered, a call, a chat, or both.
  3. Tap the call control and note what opens. Stop there unless you have a reason to call.
  4. If the device's own dialer opened, note the number. Compare it with the number from an earlier trip with a different provider, if you have one in your call history.
  5. After the trip ends, check whether the contact control is still there.
Watch

Whether a call leaves the app, which number is dialed, and whether contact is still possible after the trip.

Caution. Use a trip you already have. You do not need to complete a call. If you do call or write, keep it to something the provider needs to know.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

Do
  1. Before confirming a request you intend to make, note the price shown.
  2. After confirming, watch for a notification from your bank or card, and note its amount and time.
  3. When the trip or order is complete, note the final price and how soon it appears in the app.
  4. Over the next few days, compare the entries on your statement with the receipt.
Watch

When the first bank entry appears, whether a pending entry later changes or disappears, and how the final amount relates to the first price.

Caution. Use a trip or order you were going to pay for anyway. This probe only reads your receipt and your own bank or card notifications.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

Each subsection states the design this archetype usually takes and the reason. The components are those of the universal app model in Chapter 3.

The product is not one app. It is one backend with a client for each party. The customer’s app and the provider’s app share an account system and a trip record, and almost nothing else.

Customer’s appProvider’s appMerchant’s app
Used forMinutes at a timeA working dayA working day, often on a fixed tablet
LocationContinuous while on screenContinuous in the backgroundNone
Background workNone of its ownUser-visible work for the whole shiftMust alert loudly for a new order
NetworkWhatever the customer hasPoor and changingUsually fixed
Offline level0 for a request, 1 for history2 for state changes0 or 1

The app you can install is the simpler one. Its job is to display a record that others change.

The trip as a state machine that the backend owns

Section titled “The trip as a state machine that the backend owns”

A trip is a record on the backend that moves through states. Each state allows a few transitions, and each transition may be made by one party only.

StateEntered whenWho causes it
RequestedThe customer confirmsCustomer
MatchingThe backend starts to look for a providerBackend
AcceptedA provider takes the offerProvider
ArrivedThe provider reaches the pickup pointProvider
In progressThe customer or the goods are on boardProvider
CompletedThe provider reports the drop-offProvider
PaidThe charge is confirmedBackend, on a provider callback
CanceledA party gives up, or no provider is foundCustomer, provider or backend

A delivery adds the merchant’s states: accepted, preparing, ready.

The backend is the source of truth for the state. A client never decides that a trip is in a state. It asks for a transition, and the backend allows or refuses it.

function requestTransition(tripId, to, actor, key):
transaction(backendStore):
trip = load(tripId)
if seen(key): return trip // a retry of a transition already applied
if not allowed(trip.state, to, actor):
return Rejected(trip) // the caller receives the real state
trip.state = to
trip.version = trip.version + 1
remember(key)
appendToChangeLog(trip)
notifyParties(trip) // connection first, platform push as well
return trip

Three reasons put the machine on the backend.

  • Several writers. A customer cancels at the same moment a provider accepts. One place must decide which came first.
  • Money. Each state has a price attached, such as a cancellation fee after acceptance. A client that set the state would set the price.
  • Process death. The customer’s app is in the background for most of the trip, and the trip must advance without it.

The client side follows from this. The customer’s app holds a copy of the trip record with its version, and replaces it whenever a higher version arrives. It does not replay events. If it misses three changes, it needs only the latest. That is why P14.8 shows a current state with no visible catch-up, and why P44.4 can rebuild the screen from one request at launch.

The request carries an idempotency key, so that a lost response and a retry produce one trip. It is never queued for later. A request made five minutes late, at an old price, is worse than an error.

Matching is the backend’s work. The customer’s client sends a request and waits. The backend chooses candidates, sends one of them an offer, waits for an answer, and moves on to the next if the offer is declined or runs out.

The offer has a time limit, which makes offer delivery the most demanding real-time path in the product. A persistent connection carries it while the provider’s app is open, and a platform push carries it as well.

On the customer’s side, matching is a waiting state that does not depend on the app staying open. P44.3 tests this. When a match arrives while the device is locked, the search ran on the backend and the result came by platform push.

What you cannot see is how the backend chooses. It may take the nearest provider, or the best assignment across many open requests at once. A wait that is always a few seconds long, even where providers are plentiful, is a Speculative hint that requests are matched in batches.

The provider’s app reads positions continuously, in the background, as user-visible work. The platform shows a persistent indicator on the provider’s device for as long as the provider is online. This is the most expensive location mode in Chapter 24, and the archetype cannot avoid it.

The pipeline is the one in that chapter’s anatomy. Readings pass through a filter, are written to a buffer in the local store, and are uploaded in batches by the sync engine. During a trip a batch is sent every few seconds. While the provider is online and idle, the interval can be longer, because nobody is watching a marker.

On the backend, each batch feeds two things. The latest position becomes an ephemeral signal, kept in memory with an expiry and fanned out to whoever is watching the trip. The full path is stored, because it is the measurement that the final price and any dispute rest on.

The customer’s client receives a position every few seconds and must draw smooth movement. It has three techniques, and a product may use all of them.

  • Animate between positions. The marker travels from the previous position to the new one over the length of the interval. The display runs one interval behind reality, and it never shows a position that did not happen.
  • Predict along the route. The client knows the planned route and the provider’s speed, and moves the marker forward until the next position corrects it. The display is current, and occasionally wrong.
  • Snap to roads. A raw position drifts onto buildings. The position is moved to the nearest road, on the backend or on the client.

P44.5 separates the first two. A marker that stops when the customer’s device goes offline is animated between real positions. A marker that keeps going is predicted.

The markers on the first screen, before any request, are a different matter. They show that supply exists, and many products draw them from a list fetched on a timer. P44.2 measures that screen. Do not carry its result over to the tracking screen.

Positions come from an untrusted client. A false position could collect offers or inflate a fare, so the backend checks speed and distance between points, as Chapter 21 describes. None of this is visible from outside.

The arrival time is computed on the backend. It needs the provider’s position, a route, and live road speeds, and no client holds all three.

There are three estimates, and they are separate numbers.

  • Before the request. Estimated from nearby supply. It describes no particular provider.
  • To pickup. For the assigned provider. In a delivery it includes the merchant’s remaining preparation time.
  • To destination. The time to the end of the trip.

The backend sends each estimate with the trip record or with the position updates. A careful client receives it as a time of arrival and derives the minutes itself, so that the figure counts down between updates. An estimate that rises as well as falls shows the backend recomputing. P44.6 exposes the split: two devices that agree on the state and differ on the estimate are each doing part of the arithmetic.

The provider’s app is the client that must not fail. It has three kinds of data to move, and each takes a different design.

  • Positions. Buffered in the local store and uploaded in batches. A failed batch stays buffered and is sent again with the same batch id.
  • State changes. Queued in an outbox as mutations. This is Level 2 of Chapter 12.
  • Proof. A photo of a parcel at a door, or a signature. Uploaded apart from the state change that refers to it, as a resumable transfer.

A courier in a basement taps “delivered”. The app must accept the tap, show the next job step, and send the change when it can.

function reportStep(trip, step):
change = StateChange(id: newUniqueId(), trip, step, deviceTime: now(), position: lastKept)
transaction(localStore):
trip.localState = step // the provider's screen moves on at once
outbox.append(change)
syncEngine.requestRun()
function onStepResult(change, result):
match result:
Applied(trip): outbox.remove(change)
Rejected(trip): // for example, the customer canceled meanwhile
outbox.remove(change)
showRealState(trip) // rollback, with an explanation

The change carries the time and position the device recorded. The backend records its own receive time beside them, because it cannot trust the device’s clock. It accepts a late change if the trip is still in a state that allows it, and rejects it otherwise.

This design has a signature that the customer can see. The state “delivered” appears some minutes after the event, and the time printed beside it is the earlier one. It is the best evidence available that the provider’s app queues.

Accepting an offer is never queued. It is a race against a time limit, so it needs the network at that moment.

In terms of the steps in Chapter 13, the provider’s app sits at Step 3 or Step 4. The customer’s app needs Step 1 and a clear offline message.

The backend decides the price twice.

Before the trip, it issues a quote. The quote is computed from an estimated route, the time of day, current demand and any discount. It has an identity and a time limit. The confirm request refers to the quote by its identity. The client never sends an amount, because an amount from an untrusted client is worth nothing. If the quote has run out, the backend refuses the request and issues a new one. P44.1 looks for that expiry.

After the trip, the backend computes the final price from what happened. Products take one of three positions.

PositionBefore the tripAfter the trip
Fixed quoteAn exact priceThe same price, unless a stated rule applies, such as a changed destination or a long wait
EstimateA range, or a figure marked as an estimateComputed from measured distance and time
BasketItem prices and feesAdjusted for items that were out of stock or replaced, or sold by weight

In every position, the measurement is the backend’s. Distance and time come from the stored path of positions. The provider’s app does not report a fare. A tip is added afterwards by the customer, as a second amount on the same payment or a payment of its own.

Payment takes the external route of Chapter 26. The customer adds a card or a device wallet once. An external payment provider keeps the card and gives the backend a payment token. From then on the backend can charge with the customer absent.

The payment is a state machine of its own, attached to the trip. Its difficulty is timing. The amount is certain only at the end, and the customer is present only at the start.

  • Hold, then capture. At confirmation, the backend places a hold for the quoted or estimated amount. If the bank demands a bank challenge, the customer is there to answer it. After completion, the backend captures the final amount and releases the rest. This is the careful design.
  • One charge after completion. Nothing is reserved. The backend charges the token at the end. It is simpler, and a refusal leaves the product with a debt to collect.
  • Charge at confirmation. Common for deliveries. The basket is paid before the merchant starts, and changes become refunds.

P44.8 separates the three from your own bank notifications.

A failed charge after a completed trip becomes a debt on the account. The next request is refused until the customer pays, and that refusal is a strong signal of charging after completion.

The client’s part is small. It shows a receipt when the backend says the payment is confirmed, and the backend learns that from a provider callback. Paying the provider is a separate backend flow that no probe reaches.

Notifications and contact between the parties

Section titled “Notifications and contact between the parties”

The customer’s app is in the background for most of a trip. State changes reach the customer as notifications, and the choice follows Chapter 23.

The push carries its content, because “your driver has arrived” must appear whether or not the app can run. Successive states use one replacement key. Where the platform offers a live status, the trip appears on the lock screen, fed by a dedicated push.

The parties also need to reach each other, and must not learn each other’s phone numbers. Three designs exist.

DesignHow it worksWhat you see
Relayed numberA telephony third party owns a pool of numbers. For each trip, the backend maps a number to the two parties. Each calls the number and is connected to the otherThe device’s dialer opens, with a number that is not personal and may repeat
Call inside the appAudio travels over the data network between the two apps, as in Chapter 19A call screen in the app, and a call push on the other side
ChatMessages through the backend, tied to the tripA conversation that closes when the trip ends

All three are scoped to the trip. The mapping is removed, or the conversation is closed, a short time after completion. A contact control that disappears after the trip is the visible trace of that scope. P44.7 records which design is in use.

A relayed number works on a poor data network and costs money for each call. A call inside the app is free and fails where data is weak.

Figure 44.1 shows the components that the probes imply for the ride variant. A delivery adds a third client, the merchant’s, which connects to the same trip service.

Figure 44.1. Two clients on one backend. The provider’s client has an outbox and a point buffer. The customer’s client displays what the trip service sends.

The figure is lopsided on purpose. In the terms of the universal app model, the trip service and dispatch are services, the trip store holds the backend store and a change log of transitions, and the gateway with its connections is async delivery.

Figure 44.2 follows one ride from request to receipt.

Figure 44.2. One ride. The customer’s app sends two requests of its own, and every later change comes from the provider’s app or the backend.

When a client is in the background, each dashed line to it is replaced by a platform push, and by a fetch of the trip record on return.

Products in this archetype differ on five axes. Chapter 5 describes how a signal becomes a claim with a confidence word.

AxisPositionsDiscriminating probe
PartiesTwo, three or morePassive observation of the states shown
PriceFixed quote, estimate, basketP44.1, then the receipt in P44.8
When the money movesHold then capture, one charge after, charge at confirmation, paid in personP44.8
Marker movementAnimated between positions, predicted along the routeP44.5
ContactRelayed number, call inside the app, chat onlyP44.7

Rides against deliveries. The states tell you the parties. A delivery shows states that no courier causes, and a period in which no marker moves. Some products assign the courier late on purpose, so that travel time and preparation time end together. A courier who appears only when the order is nearly ready points that way. It is Speculative, since a shortage of couriers looks the same.

Scheduled and shared jobs. A request for a later time is stored and enters matching shortly before it is due. A provider may also carry two orders at once, and a product that shows “one stop before yours” is exposing that batch. Neither changes the design.

How the marker gets its positions. At an interval of a few seconds, a persistent connection, a server stream and polling look alike. Record the mechanism as Speculative unless P14.2 shows a plain rhythm.

The signal table collects the behaviors from all eight probes and from normal use.

SignalDesign it points toConfidence
The trip screen reappears after a force-quit with a state that advanced while the app was closedThe backend owns the trip as a state machine and every client only displays itInferred
A second device shows the same trip state within seconds of the firstOne trip record on the backend with fan-out to every watcherInferred
A provider is assigned while the customer's device is lockedMatching and dispatch run on the backend and the client waits on the resultInferred
The provider marker glides smoothly and now and then corrects its heading in a small stepPositions arrive every few seconds and the client animates between themInferred
The marker follows the road even where a raw position would drift onto buildingsPositions are snapped to roads, on the backend or on the customer's clientSpeculative
The marker keeps moving while the customer's device is offlineThe client predicts movement along the planned routeInferred
The marker freezes for a while and then moves along the missed roadsThe provider's app buffered readings through a network gap and uploaded them as a batchSpeculative
A state such as delivered appears late and carries an earlier time than the moment it appearedThe provider's app queued the state change offline with the device's time, and the backend accepted it laterInferred
The arrival-time estimate goes up as well as down while the provider is on the wayThe backend recomputes the estimate from live position and traffic and the client displays itInferred
The price screen shows a notice that the price was updated or has expiredA quote with an identity and a time limit issued by the backendInferred
The final price differs from the quote and the receipt lists the reasonThe backend reprices from the measured trip under stated rulesInferred
A pending bank entry at confirmation later changes to another amountA hold placed at the start and captured after completion through a payment tokenInferred
The app refuses a new request until an earlier failed charge is settledCharges are made after completion with the customer absent and failures become a debt on the accountInferred
A call to the provider goes to a number that is the same for every providerA relayed number from a telephony third party mapped to the tripInferred
The chat and call controls disappear soon after the trip endsContact between the parties is a resource scoped to the tripInferred
A lock-screen status keeps advancing after the app is force-quitA live status updated by a dedicated push from the backendInferred
Tapping confirm twice on a weak connection creates one requestAn idempotency key on the request. Weak evidence, because the client may simply disable the buttonSpeculative
The customer's app shows the location indicator only while a map is on screenContinuous location while on screen on the customer's side, with background location only on the provider's sideObserved

Search the signals from every chapter in the signal lookup.

The client designs are the same in one city and in a thousand. The backend changes, and the work is local: a trip in one city has nothing to do with a trip in another.

The same at every size

  • A trip record with a state machine and a version, owned by the backend.
  • A request with an idempotency key, never queued.
  • A provider’s app with a point buffer and an outbox.
  • A customer’s app that displays the trip record, and a payment token charged by the backend.

What changes

  • Partitioning by place. A large product partitions matching, pricing and live positions by city or by map cell.
  • Location ingest. Every online provider sends a batch every few seconds, all day. This is the largest stream of writes, and it is separated from the trip service early.
  • Dispatch. At volume, requests are gathered for a short window and matched together, which makes each customer wait a little.
  • The first screen. Showing every nearby provider to every open app is a large fan-out for a decorative feature. At size it is sampled, polled slowly or drawn by the client.
  • Peaks. Demand moves with weather, events and the hour. You see a higher price, a longer estimate, or a message that no provider is available.

Each of these is a Speculative signal of scale, since a product decision explains it as well.

Chapter 6 describes the worksheet and the order in which to fill it in. The fields in this form are specific to the archetype. The probes of Section 44.3 fill most of them.

Open worksheet

The fields that the Part II chapters add take these typical values for the customer’s app. A value that differs in your teardown is a finding. Record it with the probe that produced it.

FieldTypical valueConfidence
Location mode on screen, Chapter 24Continuous on that screenObserved
Location in the background, Chapter 24None on the customer’s sideObserved
Foreground mechanism, Chapter 14Held connection or push, or polling at a few secondsSpeculative
Role of platform push, Chapter 14Carries contentInferred
Payment route, Chapter 26External payment providerObserved
Saved payment methods, Chapter 26Masked card list, and a device walletObserved
Offline level, Chapter 120 for a request, 1 for historyInferred
Live status updates, Chapter 25Updated by pushInferred
Backend components impliedGateway with connections, trip service with a change log, dispatch, pricing, location ingest, payment service, platform push, an external payment provider and a telephony third partyInferred
The provider’s appContinuous location in the background, a point buffer and an outboxSpeculative, from the customer’s side alone

The question is usually asked as “design a ride-hailing app” or “design live order tracking for a delivery app”. It joins location, real-time delivery and payments, and its two clients have opposite needs.

Clarify first. Ask four things.

  1. Which client: the customer’s, the provider’s, or both. The answer changes the whole interview.
  2. Two parties or three.
  3. Whether the price is fixed before the trip.
  4. Whether matching is in scope, or you may treat it as a backend service that returns a provider.

Then state the promise from Section 44.1, and say that the backend owns the trip. Everything else hangs on that sentence.

Go deep on three concerns.

  • The trip as a state machine. States, who may cause each transition, a version on the record, and an idempotency key on the request. Explain why the customer’s client holds the latest record and not an event history.
  • The location pipeline. Continuous location as user-visible work, a filter, a buffer in the local store, batched uploads, an ephemeral latest position for watchers, and smoothing on the customer’s map. Draw Figure 44.2 from memory.
  • The provider’s app offline. Which actions are queued, which are refused, and what the backend does with a change that arrives late.

Trade-offs an interviewer expects to hear.

  • A persistent connection against polling for the tracking screen. Polling every few seconds is defensible for the customer’s side, and not for offers to providers.
  • Animation between positions against prediction along the route: a display that is late and true, or current and sometimes wrong.
  • Upload interval: freshness for the watcher against the provider’s battery and data.
  • A hold then capture against one charge at the end: a bank challenge answered while the customer is present, against a simpler flow with debts.
  • A relayed number against a call inside the app: reach on a poor data network against cost.

Name the untrusted client without being asked: neither app ever states a price or a state.

  • An on-demand service promises that you ask now, watch it come and pay without thinking. Location, real-time delivery, payments and network resilience dominate its design.
  • The product is two or three apps on one backend. The customer’s app is a display. The provider’s app is the one that must survive poor networks.
  • A trip is a state machine that the backend owns. Clients ask for transitions and display the latest record. The request carries an idempotency key and is never queued.
  • Matching is backend work that the client waits on, and the result arrives by platform push if the app is away.
  • The provider’s app reads location continuously as user-visible work, buffers readings and uploads batches. The customer’s app smooths the marker by animating, predicting or both.
  • The provider’s state changes are queued in an outbox with the device’s time. A state that appears late with an earlier time is the visible trace.
  • The backend decides the price before the trip as a quote and after it from the stored path. It charges a payment token with the customer absent, often as a hold that is captured later.
  • Contact between the parties is scoped to the trip and hides phone numbers, through a relayed number, a call inside the app or a chat.