Build a Last-Mile Delivery Tracking System
distributed-systems scalability performance
System Design Deep Dive
Live Delivery Tracking System
Turn noisy phone GPS into a smooth dot and an ETA that doesn't lie, for thousands of drivers at once.
Picture a marathon broadcast trying to track ten thousand runners with nothing but a stopwatch and a pair of binoculars. Every few seconds a spotter catches a glimpse of a runner’s bib number through the crowd, jots down where they were standing, and has to guess both how fast that runner is actually moving and when they will cross the next mile marker, even though the glimpse itself is imprecise, the runner might have ducked behind a water station, and by the time the spotter’s radio call reaches the broadcast booth, the runner has already moved somewhere else entirely.
That is the shape of the problem underneath last-mile delivery tracking, except the runners are drivers, the glimpses are GPS pings arriving every few seconds from a phone bouncing around in a cupholder, and the audience is not one broadcast booth but hundreds of thousands of customers each watching a single driver’s dot crawl across their own screen in real time. A driver’s phone reports its own noisy estimate of where it is, that estimate needs to be cleaned up and pinned to the actual road network, an ETA has to be recomputed continuously as traffic and remaining distance change, and the result has to reach the customer’s app the moment it changes, not the next time the customer happens to refresh.
A naive version of this looks deceptively simple: store the driver’s raw latitude and longitude, compute a straight-line distance to the destination, divide by an average speed, and have the customer’s app poll a REST endpoint every few seconds for the latest numbers. That falls apart on contact with reality. Raw GPS coordinates drift by tens of meters near tall buildings and inside parking garages, so a straight rendering of raw pings makes the delivery dot visibly teleport across buildings and jump backward across intersections it never crossed. Straight-line distance ignores that roads bend, that a highway on-ramp adds distance a bird’s-eye line does not see, and that traffic on one road can be crawling while a parallel road two blocks over is empty. And polling does not scale gracefully: at hundreds of thousands of concurrent trackers, even a modest poll interval means tens of thousands of requests per second hitting a stateless read endpoint for information that mostly has not changed since the last poll.
We need to solve for three things simultaneously: turning a noisy, occasionally out-of-order stream of raw GPS pings into a smooth, road-accurate position signal; recomputing an ETA against live traffic conditions that is honest about its own uncertainty instead of pretending to a precision it cannot support; and pushing state changes to the customer the instant they happen over a connection that stays open, rather than making every customer’s app ask over and over whether anything is new.
Requirements and Constraints
Functional Requirements
- Ingest GPS location pings from each driver’s mobile app roughly every 3 to 5 seconds for the duration of an active delivery
- Smooth raw, noisy GPS coordinates and map-match them onto the underlying road network, so the rendered position tracks an actual street rather than empty space or the inside of a building
- Maintain an explicit state machine for every delivery (
assigned,en_route_to_pickup,arrived_at_pickup,picked_up,en_route_to_dropoff,arriving,delivered,failed) and reject illegal state transitions - Recompute each active delivery’s ETA on a regular cadence and immediately after a significant deviation, using live traffic conditions rather than static historical averages alone
- Express every ETA as a confidence band (for example, 12 to 16 minutes) rather than a single number, widening the band when the underlying signal is less certain
- Push location and ETA updates to the customer’s app over a persistent connection the moment something meaningfully changes, with no polling required
- Sequence every ping and state event correctly per delivery even when the network delivers them out of order or the driver app retries a send
- Retain full location and event history per delivery for support investigations, delivery disputes, and offline analytics
Non-Functional Requirements
- Scale: 50,000 concurrent active deliveries at peak, each driver pinging roughly every 4 seconds, for a sustained ingestion rate near 12,500 pings/sec, bursting toward 20,000 pings/sec during regional demand spikes
- Customer fan-out: up to 200,000 concurrent customer WebSocket connections at peak, since a delivery is often watched by more than one viewer (customer app plus a support dashboard)
- Ingestion latency: p99 under 250ms from a raw ping leaving the driver’s device to a smoothed, map-matched position being available to downstream consumers
- Push latency: under 2 seconds end to end, from a driver’s raw ping to the corresponding update rendering on the customer’s screen
- ETA recompute cadence: at least once every 15 seconds per active delivery, and immediately on a significant route or traffic deviation
- Durability: at-least-once ingestion with idempotent processing; no ping is silently and permanently lost even if it arrives duplicated or out of order
- Availability: 99.95% for the ingestion and push path; on partial failure the system serves the last known good location and ETA rather than showing nothing
- Retention: a full year of ping and event history per delivery for analytics and dispute resolution, with older raw pings eligible for downsampling
Constraints and Assumptions
- We assume the driver’s mobile app already owns GPS hardware access and reports pings in a standard schema; we do not design the device-level location API integration
- We assume turn-by-turn route geometry between two points comes from an external routing and mapping provider; “distance remaining along the route” is treated as a lookup against that provider’s data, not something this system computes from raw map tiles
- We assume live traffic conditions are supplied by a third-party traffic feed that reports a congestion factor per road segment; we consume that feed, we do not build the sensor network or the traffic model behind it
- Driver-to-delivery assignment, deciding which driver is offered which delivery, is owned by a separate dispatch and matching service; this system starts once a delivery is already assigned to a driver
High-Level Architecture
The system has six major components: the GPS Ingestion Gateway, the Location Smoothing and Map-Matching Service, the Driver and Delivery State Machine, the ETA Recalculation Engine, the Delivery Event Sequencer, and the WebSocket Fan-out Gateway.
The GPS Ingestion Gateway is the front door for every raw ping: it validates the payload, rejects pings whose reported accuracy is too poor to trust, and deduplicates retried sends using a per-delivery device sequence number. The Location Smoothing and Map-Matching Service takes a validated ping and turns it into a stable, road-accurate position, first smoothing the raw coordinate with a motion model and then snapping the result onto the correct road segment. The Driver and Delivery State Machine is the source of truth for where a delivery sits in its lifecycle, from assigned through delivered, and it is the only place that lifecycle state actually changes. The ETA Recalculation Engine combines the smoothed position, the remaining route geometry, and a live traffic feed to produce an ETA expressed as a confidence band rather than a single number. The Delivery Event Sequencer takes every state change, location update, and ETA update and imposes a strict per-delivery order on them before anything is treated as final, since the network and the driver app’s retry logic do not guarantee order on their own. The WebSocket Fan-out Gateway holds a live subscription per delivery from every customer app and support dashboard watching it, and pushes an update only when something worth showing has actually changed.
A ping’s life looks like this: the driver’s app sends a raw coordinate, the Ingestion Gateway validates and deduplicates it, and the Smoothing and Map-Matching Service turns it into a clean position pinned to a real road. That position feeds two things in parallel: the Driver and Delivery State Machine, which checks whether this position implies a lifecycle transition (arriving at the pickup address, for instance), and the ETA Recalculation Engine, which combines the position with the live traffic feed to produce an updated ETA range. Both the state change and the ETA update are handed to the Delivery Event Sequencer, which applies them in the correct order even if they arrive out of sequence, and only then does the WebSocket Fan-out Gateway decide whether the net effect is worth pushing to the customer’s screen.
The single most important architectural decision is decoupling how often a driver's phone reports its position from how often the customer's screen updates. Ingestion runs on the driver's own cadence, every few seconds; ETA recompute runs on a cadence bounded by the traffic feed's freshness; and the WebSocket push only fires when a meaningful-change filter decides something worth showing has actually changed. Coupling all three to the same clock either floods a customer's mobile connection with noise or starves the tracking view of updates exactly when something important happens between ticks.
Component Deep Dives
The GPS Ingestion Gateway
This component’s job is to accept a flood of raw GPS pings from thousands of driver devices, reject the ones that cannot be trusted, and guarantee that a duplicate or out-of-order retry is never applied twice.
The obvious assumption, that pings arrive once, in order, and roughly on time, does not hold on a mobile network. Phones lose signal in tunnels and parking structures, buffer pings locally, and flush a burst of them the moment connectivity returns; flaky cellular connections cause the same ping to be retried by the client before an acknowledgment comes back. Treating every inbound payload as new and authoritative would let a retried ping double-count a driver’s movement, and a batch of delayed pings flushing out of order could make the smoothing filter downstream briefly believe the driver teleported.
We accept a ping only if its reported accuracy radius is tight enough to be useful and its device-assigned sequence number is strictly greater than the last one accepted for that delivery. The sequence number, not a timestamp, is what protects against duplicates and reordering, because a device’s own clock can be skewed or wrong in ways a monotonic per-delivery counter is not.
# GPS ping ingestion: validation, dedup, and out-of-order rejection
# Demonstrates: idempotent handling of retried/duplicate pings using a
# per-delivery monotonic device sequence number, not wall-clock time alone
from dataclasses import dataclass
from typing import Optional
@dataclass
class RawPing:
delivery_id: str
driver_id: str
device_seq: int # monotonic counter set by the driver app, resets per delivery
lat: float
lng: float
accuracy_m: float # GPS accuracy radius reported by the device
client_ts_ms: int
MAX_ACCEPTABLE_ACCURACY_M = 100.0
MAX_CLOCK_SKEW_MS = 30_000
class PingIngestor:
def __init__(self, seq_store):
self.seq_store = seq_store # last accepted device_seq per delivery_id
def accept(self, ping: RawPing, server_ts_ms: int) -> Optional[RawPing]:
if ping.accuracy_m > MAX_ACCEPTABLE_ACCURACY_M:
return None # too imprecise to trust, drop before it pollutes smoothing
skew = abs(server_ts_ms - ping.client_ts_ms)
if skew > MAX_CLOCK_SKEW_MS:
ping.client_ts_ms = server_ts_ms # do not trust an unreliable device clock
last_seq = self.seq_store.get(ping.delivery_id, -1)
if ping.device_seq <= last_seq:
return None # duplicate or out-of-order retry, already applied or superseded
self.seq_store.set(ping.delivery_id, ping.device_seq)
return ping
Think of this like a mail sorting facility that recognizes a letter it already sorted yesterday because of a duplicate tracking label, and sets aside a letter whose address is illegible rather than guessing where to route it. What breaks without the sequence check: a retried ping replaying an old position would look like a legitimate new sample to every downstream consumer, and the smoothing filter would interpret the sudden jump backward as real, unnecessary movement, momentarily distorting both the rendered position and the velocity estimate it depends on.
This is the same idea behind idempotent producers in Kafka, where each producer attaches a monotonically increasing sequence number per partition so the broker can silently drop a retried, already-committed write instead of accepting it twice. Delivery telemetry SDKs from companies like Uber and DoorDash use the same trick at the client, tagging every location sample with a per-trip sequence number rather than relying on the device clock alone.
Premium Content
Unlock the full article along with everything else in the archive — all in one place.