← Back
Home › Systems › Transport, logistics and mobility

last-mile-delivery

Turns a day's parcels into one ordered route per driver and keeps those routes valid as deliveries go wrong, for delivery operators.

Amazon Logistics

Open the last-mile-delivery pagelast-mile-delivery Hdr histogramdata-structures: Log-linear bucketed histograms with bounded relative error for latency percentilesDeterministic simulationtestingdistributed: Run the whole system in one thread under a seeded scheduler with simulated network, disk and clock, so every failure replays from its seed.›Primary-backup replicationdistributed: One node orders all writes and ships them to standbys; the acknowledgement rule (none, one, all) sets what a crash loses, and an external arbiter decides who takes over.›Event sourcingfinance: Persist state changes as an append-only stream of immutable events per entity; current state is a fold over them, guarded by expected-version appends.›Idempotency keysfinance: Client-chosen unique key per logical operation, stored with a request fingerprint and the final response, so retries replay the first result instead of acting twice.›Address geocodinggeometry: Parse, normalize and fuzzy-match postal addresses to coordinates and backGeodesic distance (haversine,Vincenty, Karney)geometry: Shortest-path distance between two lat/lon points on the Earth: haversine on a sphere (about 0.5% error), Vincenty or Karney on the WGS84 ellipsoid (sub-millimetre).›Geospatial cell index (geohash,S2, H3)geometry: Map a lat/lon to a 64-bit cell id on a hierarchical grid, so "near" becomes an integer range scan in any ordinary sorted index.›Map matching hmmgeometry: HMM/Viterbi map matching of GPS traces onto the road graphAssignment problem (Hungarian /Jonker–Volgenant)graphs: Optimal one-to-one matching of rows to columns in O(n³) by growing a matching along shortest augmenting paths over dual potentials.›Contraction hierarchies(shortcut-based shortest paths)graphs: Rank the nodes, remove them one by one while adding shortcut edges that keep distances intact, then answer queries with two tiny searches that only climb the ranking.›Process supervisionhardware-os: A parent process starts services, reaps their exits and restarts them under a delay and burst-limit policy, then stops them with SIGTERM before SIGKILL.›Gradient-boosted decision trees(GBDT)machine-learning: Add small regression trees one at a time, each fitted to the loss gradient; histogram splits over 256 bins make it the strongest tabular default.›k-means clusteringmachine-learning: Partition points into k clusters by alternating nearest-centroid assignment and centroid averaging; k-means++ seeding makes it dependable.›WebSocketsnetworking: Full-duplex message framing over one upgraded HTTP connection; the standard way to push real-time data to browsers.›Large neighbourhood search(ruin and recreate)optimization: Repeatedly remove a chunk of a solution and rebuild it with a heuristic or exact solver; learn which operator pairs pay off.›Local search 2opt or optoptimization: 2-opt, Or-opt, relocate and swap neighborhoods with fast delta evaluation for routesTime-window feasibilityoptimization: Keep earliest/latest start and slack per segment so "does this insertion or move keep every window?" costs O(1) instead of a walk down the sequence.›Coverage-guided fuzzingsecurity: Mutate inputs, run an instrumented target, and keep any input that reaches a new code edge; feedback turns blind guessing into incremental search.›Mutual TLSsecurity: TLS where the client also proves a certificate key, so every connection carries a verified, rotatable machine identity.›Time-series forecasting(baselines, model families, ba…statistics: The end-to-end recipe: fix horizon and uncertainty, beat naive baselines, choose a model family, and validate with rolling-origin backtests and scale-free metrics.›Point-in-time recoverystorage: Periodic base backups plus a continuously archived change log let you rebuild the state as of any instant, then branch from there.›Graceful drainingsystems: On a termination signal, stop taking new work, finish in-flight work until a deadline, tell clients to reconnect elsewhere, then exit; clients resume from a token.›Cooperative schedulingconcurrency: Green threads/coroutines yielding at await points on a user-space schedulerFault injectiondistributed: Inject latency, errors, kills and partitions via proxies, netem or hooksRecord replaydistributed: Record every nondeterministic input (time, randomness, I/O, model and tool outputs, per-tick player input) and re-execute deterministically for debugging, replays and state rebuildHealth checkingsystems: Decide per backend whether to route to it using probes and real-traffic errors, with consecutive-count hysteresis, capped ejection and a panic fallback.›Leasesdistributed: A lock that expires on its own: bounded-time ownership that survives crashes and partitions, given bounded clock drift and fencing tokens on writes.›Optimistic concurrency controlstorage: Run transactions without locks, record what they read, and validate at commit that nothing changed; fastest when conflicts are rare.›Cryptographic hashes (SHA-2,BLAKE3)security: Collision-resistant fingerprints of data; SHA-256 for interoperability, BLAKE3 for speed, HMAC/HKDF for keyed uses.›Exponential backoff with jittersystems: Retry after delays that grow geometrically and are randomized, so failed clients spread out instead of hammering a recovering service in synchronized waves.›Address interpolationgeometry: Estimate a house-number position along a street segment from known address rangesAddress parsinggeometry: Split free-text addresses into components (house, street, city, postcode) with rules or a sequence modelFuzzy string matchingsearch-sort: Edit distance, Jaro-Winkler and trigram indexes for approximate name and address matchingInverted indexstorage: Map each term to a sorted, compressed list of the documents containing it; the core of full-text search and other fast set queries.›+ 27 moreTransparent interceptionnetworking: Redirect traffic to a local proxy (iptables TPROXY/REDIRECT, eBPF) without app changesSyscall interceptionsecurity: Intercept syscalls via ptrace, seccomp user notify or a user-space kernelFencing tokensdistributed: A number that grows with every lock grant and is checked by the protected resource, so a paused ex-holder's late write is rejected.›Named entity recognitionmachine-learning: Find the character spans of entities in text and give each a type, by labelling every token and decoding a valid label sequence.›Text normalizationmachine-learning: Rewrite messy text into one canonical form for matchers and models, or into spoken words for speech output (and back), keeping a map to the original offsets.›Unicode text tokenization forsearchsearch-sort: Turn multilingual text into index terms: segment words (UAX 29 style, CJK bigrams), normalize, case-fold and accent-fold, identically at index and query time.›Bit packing and PFORcompression: Store blocks of integers with exactly b bits each and patch rare outliers separately (PFOR); the fastest integer codec family, billions of values per second.›Delta encodingcompression: Store differences between consecutive values instead of the values, turning sorted IDs, timestamps and counters into small numbers that pack into few bits.›Varint (LEB128)compression: Variable-length integers in 7-bit groups with a continuation bit; small numbers take 1 byte, the wire format of Protobuf, WebAssembly and DWARF.›BM25 rankingsearch-sort: The standard lexical relevance score: IDF-weighted term frequency with saturation (k1) and length normalization (b); default in Lucene, Elasticsearch and SQLite FTS5.›Sorted-list intersectionsearch-sort: Intersect sorted id lists by merging, galloping from the short list into the long one, and skipping whole compressed blocks, shortest list first.›+ 15 moreEbpfhardware-os: Verified in-kernel programs on hooks (kprobes, XDP, tc) with mapsPtrace process controlhardware-os: Ptrace: attach, read and write registers and memory, single-step, intercept signalsSeccomp bpfsecurity: Syscall filtering with seccomp BPF programsByte-pair encoding (BPE)tokenizationmachine-learning: Learn a subword vocabulary by repeatedly merging the most frequent adjacent pair; byte-level BPE encodes any input losslessly with 32k-200k tokens.›Transformer blockml-training: The decoder-only pre-norm block (attention plus MLP on a residual stream) behind every modern LLM, with exact shapes, parameter, FLOP and KV-cache counts.›SIMD programminghardware-os: Process 4-16 lanes per instruction with AVX2, AVX-512 or NEON via Rust std::arch, with runtime dispatch and a portable fallback.›Top-k selectionsearch-sort: Keep the k best of n items without sorting all n - bounded min-heap for streams, quickselect for arrays, threshold pruning to skip most items.›+ 7 more+ 1 more

How it is built

Architecture overview

A last-mile delivery system turns a day's parcels into one ordered route per driver and then keeps those routes true while the day goes wrong. It has a planning side that runs in batches (geocode the addresses, group the stops into areas, assign areas to drivers, optimise each route against travel times and delivery windows) and an execution side that runs in real time (the driver's phone reports scans and positions, the dashboard shows progress, the plan is repaired when a stop fails). Everything the optimiser decides rests on two estimates: how long it takes to drive between two points, and how long the driver stands at a stop. The idea that makes it work is to keep those estimates and the search separate: a routing engine answers travel-time queries in microseconds, a learned model supplies service times, and a metaheuristic spends its time on thousands of cheap evaluations of candidate routes rather than on any exact solve. Public data on real routes is rare: the data set of the Amazon Last Mile Routing Research Challenge, 9,184 historical routes from five US metropolitan areas, was described by its authors as the first large public set built from real operational routing data (Merchán et al. 2024).

Parts:

 orders ─► geocode ─► cell index ─► k-means zones ─► Hungarian: zone→driver ─► route optimiser ─► manifests
              │            │                                   ▲                    ▲   │ (LNS, windows)
              ▼            ▼                                   │                    │   ▼
          coordinates   neighbours            travel-time matrix ◄─ routing engine (CH)    phones
                                                               ▲                    ▲        │ scans, GPS
                          GBDT: service time ──────────────────┘   forecast: volume │        ▼
                                                                                      │   event log ─► map-matching ─► live view
                                                 re-plan trigger ◄────────────────────┴───── (event sourcing, WebSocket)

How the parts fit together

Address resolution

An address is text; the optimiser needs a point. address-geocoding (no page yet) resolves the text to a coordinate and a confidence, and the confidence matters downstream: a rooftop match can be driven to, a street centroid cannot. Once a stop is a point, a cell index (H3, S2 or geohash) gives every stop an integer cell id, so "which stops are near this one" is a range scan in an ordinary sorted store and parcels bunch into cells without a spatial database. Geodesic distance (haversine for speed, Karney for accuracy) is the cheap pre-filter: it is a lower bound on road distance, so pairs that are too far apart are never sent to the routing engine at all. Contract: every stop leaves this part with lat, lon, cell id, match quality.

Road network engine

The optimiser asks a travel-time question millions of times per plan, so a fresh Dijkstra per question is far too slow. Contraction hierarchies pay minutes of preprocessing on the road graph once and then answer a query with two small searches that only climb the node ranking. They are the right choice for the big many-to-many matrices a planner needs; OSRM, which implements both CH and Multi-Level Dijkstra, recommends MLD by default and keeps CH for very large distance matrices (OSRM README). The price is that the graph is static: a CH is built for one weighting, so live traffic means rebuilding it, or choosing an algorithm that can re-weight cheaply (which is one reason OSRM's MLD is its default). A* search is the fallback for one-off queries and for cases where the cost changes per query. Contract: matrix(points) -> N×N seconds + metres, with road-snapped points and a flag for unreachable pairs (one-way streets, pedestrian zones).

Zoning and assignment

Optimising one route over every stop in a city is too big and produces routes that cross each other. The common structure is cluster first, route second: k-means on stop coordinates (in a projected metric space, not raw degrees) makes compact zones sized to a driver's day, with cell ids as a fast way to seed and rebalance them. Then the zones must go to drivers: each pairing has a known cost (the driver's depot, vehicle size, how familiar the driver is with the area), and the assignment problem gives the optimal one-to-one matching in O(n³), which is trivial at the size of a depot. Contract: a zone is a list of stop ids with a total demand and an expected duration; a driver gets at most one zone per route.

Route optimiser

Inside a zone the problem is a vehicle routing problem with time windows: order the stops to minimise travel while every window and the vehicle capacity hold. Exact solving with mixed-integer programming works for tens of stops; for real routes a metaheuristic wins. A construction heuristic gives a first route, local-search-2opt-or-opt (no page yet) polishes it with small moves, and large neighbourhood search removes a chunk of stops and re-inserts them, learning which removal and insertion operators pay off; Ropke and Pisinger's adaptive version improved the best known solution on more than half of 350-plus benchmark instances of up to 500 requests (Ropke & Pisinger 2006). Hybrid genetic search is the strong alternative for plain capacitated routing and has an open-source implementation (Vidal 2022).

What makes millions of move evaluations affordable is time-window feasibility: keeping earliest and latest start and slack per route segment turns "does this insertion keep every window?" into an O(1) check instead of a walk down the route. Contract with the engine: the optimiser only ever reads the travel-time matrix and a service-time function; it never calls the road graph during search.

Prediction

Two estimates change the plan more than any algorithm choice. Service time (parking, walking to the door, handing over the parcel) varies by building type, floor, parcel size and time of day, and is a tabular regression problem: gradient-boosted trees over those features are the strong default, trained on the actual dwell times the event log produces. Volume per depot and day is a forecast: a seasonal baseline first, a richer model only if a rolling-origin backtest shows it beats the baseline. The forecast sizes the fleet the night before; the service-time model feeds the optimiser at planning time.

Position cleaning

Driver GPS is noisy and sampled sparsely to save battery. To measure real travel times and show the driver on the right street, map-matching-hmm (no page yet) uses a hidden Markov model: each road segment near a fix is a candidate state, emission probability falls with the distance between the fix and the road, transition probability prefers a route distance close to the straight-line distance between consecutive fixes, and the Viterbi decoder (Convolutional codes and the Viterbi decoder explains the dynamic programme) picks the most likely road sequence (Newson & Krumm 2009). Valhalla ships this as its Meili library. The matched traces both drive the live map and become the ground truth that corrects the routing engine's speeds.

Event backbone and live view

The phone is the system's weakest link: it goes offline in a stairwell and retries when the signal returns. Every scan, arrival and failed attempt is therefore an event appended to a log (Event sourcing), with the parcel's state derived by replay, so a delivery history can be rebuilt and audited. Because a retried upload may arrive twice, every event carries an idempotency key and the server stores the first result for it. The dashboard and the driver app subscribe to changes over WebSockets; position streams are the high-volume part and are sampled for display. Contract: events are immutable, keyed by (parcel, event id), ordered per parcel.

Operations and verification

Process supervision restarts the batch planners, the route optimiser workers and the event consumers under a delay and burst limit, so a planning run that dies on one odd stop restarts without tying up the morning cut-off. Graceful draining lets an optimiser worker finish the route it is improving, and a live-view gateway tell dashboards and driver phones to reconnect elsewhere, so a deploy during the delivery day does not drop scans. Mutual TLS authenticates the calls between the planner, the road engine, the event backbone and the driver-app gateway, so only the planner can publish routes and only the gateway can append scans. Primary-backup replication keeps a standby for the route and event store that the day's execution depends on. Point-in-time recovery rebuilds routes and parcel states to the instant before a bad plan publication or a faulty bulk status update, then replays the scans that came after. hdr-histogram (no page yet) records latency percentiles of the travel-time matrix call and of re-optimisation after a delay, because a mid-day re-plan must finish while the driver is still deciding. Coverage-guided fuzzing mutates manifest files, address strings and scan payloads that arrive from shippers and driver phones, keeping inputs that reach new code. Deterministic simulation testing replays a day of orders, delays and failed deliveries under a seeded clock, so a parcel on two routes or a lost scan replays from its seed.

Data and control flow

Planning run (night before and early morning):

  1. Orders arrive; each address is geocoded and given a cell id (reads address text, writes coordinates and quality).
  2. Orders with a low match quality go to a correction queue; the rest are grouped into zones by k-means, with sizes from the volume forecast and the service-time model.
  3. The Hungarian step assigns zones to the drivers who report for work.
  4. For each route the routing engine returns the travel-time matrix (one many-to-many CH query).
  5. The optimiser runs for a fixed time budget, accepting a move only if the time-window check passes; the best route becomes the driver's manifest. Time goes to the matrix and to the search; the geocoding and assignment steps are minor.

Delivery and re-plan (during the day):

  1. The phone scans a parcel at the door and uploads the event with an idempotency key.
  2. The server appends the event, updates the projection and pushes it to the dashboard and the customer's tracking page.
  3. A failed attempt or a running delay raises a re-plan trigger for that route only.
  4. The optimiser re-runs on the remaining stops with the current position as the start (map-matched) and a short budget; the new order replaces the manifest on the phone.
  5. At the end of the day the stored dwell times and matched traces feed the next training run of the service-time model.

Scaling

In the order they bite:

  1. Matrix computation grows with the square of the stops in a route. Levers: geodesic pre-filtering, smaller zones (more, smaller problems parallelise trivially), CH for large matrices, caching matrices across re-plans.
  2. Search time per route is bounded by the O(1) feasibility check and the move evaluator. Levers: more independent routes in parallel (they share nothing), a time budget per route, warm starts from yesterday's solution on the same zone.
  3. Service-time and travel-time quality limit the real result more than search does. Levers: better features, per-driver effects, correcting speeds from map-matched traces.
  4. Event volume from live positions. Levers: sample for display, partition the log by route, keep the live view on a cache.
  5. Geocoding errors are mostly a data problem: the lever is a correction loop where a driver's confirmed stop location overrides the geocoder next time.

Failure modes

FailureSymptomCauseFix
Wrong geocodedriver at the wrong street, repeated failed attemptsstreet-level match used as a rooftop, ambiguous addresscarry match quality, hold low-quality stops for review, learn the confirmed drop-off point
Stale road weightsplans look short, routes run longCH built once on free-flow speedsre-weight with measured speeds, use a metric that supports updates, rebuild on a schedule
Infeasible by constructionsolver returns no route or drops stopswindows tighter than service times, capacity too small, unreachable paircheck feasibility before search, report dropped stops, relax by priority
Optimistic service timeslate deliveries at the end of every routemodel trained on completed stops only, ignores failed attemptstrain on all attempts, add buffer by quantile not mean
Zones cross or split a streettwo drivers on one street, long deadheadsk-means in raw degrees, ignores the river or the rail lineproject to a metric plane, cluster on travel time, merge by cell adjacency
Duplicate eventsparcel shown delivered twice, double countoffline retries without keysidempotency key per event, dedupe on write
Map-matching jumpstrace teleports between parallel roadssampling gap too long or candidate radius too smallwider candidates, break the trace at long gaps, use the road graph's turn rules

Real implementations

namewhat it isrepolicencenotes
OSRMroad routing engine: CH and Multi-Level Dijkstra, table (matrix) service, map matchingProject-OSRM/osrm-backendBSD-2-ClauseMLD recommended by default; CH for huge matrices
Valhallarouting engine with matrix, isochrones, map matching (Meili) and tour optimisationvalhalla/valhallaMITdynamic costing at query time
GraphHopperrouting engine and route-optimisation librarygraphhopper/graphhopperApache-2.0
VROOMvehicle routing solver in C++ for VRPs with time windows and capacitiesVROOM-Project/vroomBSD-2-Clauseneeds a routing engine such as OSRM, Valhalla or openrouteservice for the matrix
OR-Toolscombinatorial-optimisation toolkit with a routing librarygoogle/or-toolsApache-2.0
PyVRPstate-of-the-art VRP solver (hybrid genetic search)PyVRP/PyVRPMIT
HGS-CVRPreference implementation of hybrid genetic search for the CVRPvidalt/HGS-CVRPMITVidal 2022
H3hexagonal hierarchical cell indexuber/h3Apache-2.0
Peliasmodular geocoderpelias/peliasMIT
NominatimOpenStreetMap geocoderosm-search/NominatimGPL-3.0

No operator publishes its production stack end to end; the open pieces above are the building blocks, and the Amazon challenge data set is the public benchmark for the routing and sequencing part.

Sources

  • D. Merchán et al., 2021 Amazon Last Mile Routing Research Challenge: Data Set, Transportation Science, 2024. DOI
  • S. Ropke and D. Pisinger, An Adaptive Large Neighborhood Search Heuristic for the Pickup and Delivery Problem with Time Windows, Transportation Science, 2006. DOI
  • T. Vidal, Hybrid genetic search for the CVRP: Open-source implementation and SWAP neighborhood*, Computers & Operations Research, 2022. DOI
  • R. Geisberger et al., Contraction Hierarchies: Faster and Simpler Hierarchical Routing in Road Networks, WEA 2008. DOI
  • P. Newson and J. Krumm, Hidden Markov Map Matching Through Noise and Sparseness, ACM SIGSPATIAL 2009. DOI
  • OSRM README (algorithms, MLD default): github.com/Project-OSRM/osrm-backend
  • Valhalla README (matrix, map matching, Meili): github.com/valhalla/valhalla
  • VROOM README: github.com/VROOM-Project/vroom

Similar systems

Share the most building blocks with this one.