Problem RestatementProblem
Walmart asked: design a system for shipping customer parcels that supports multiple carriers (UPS, USPS, FedEx...). For each order, it should:
- create a shipment (addresses, package size and weight, service level),
- choose a carrier and service (cheapest that meets the delivery promise),
- buy a shipping label through that carrier's API,
- track the parcel until delivery and notify the customer,
- handle carrier failures, returns and exceptions.
ArchitectureArchitecture
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
OMS["Order system / warehouse"] --> SS["Shipment Service"]
SS --> DB[("Shipments DB")]
SS --> RATE["Rate shopping"]
RATE --> AD["Carrier adapters"]
AD --> UPS["UPS API"]
AD --> USPS["USPS API"]
AD --> FDX["FedEx API"]
SS -->|"buy label"| AD
UPS -->|"tracking webhooks"| TRK["Tracking ingestion"]
FDX --> TRK
POLL["Tracking poller"] --> AD
TRK --> SS
SS --> K[("Shipment events")]
K --> NOTIF["Customer notifications"]Deep Dive — Supporting four carriers without four codebasesDeep dive
UPS, USPS and FedEx each have their own API, authentication, address format and status vocabulary. How that difference is absorbed decides what adding the fifth carrier costs.
Branch on the carrier in the shipping service
if carrier == 'ups': ... elif carrier == 'fedex': ... wherever a carrier is involved.
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
SVC["Shipping service"] --> R["Rates: if/elif per carrier"]
SVC --> L["Labels: if/elif per carrier"]
SVC --> T["Tracking: if/elif per carrier"]
SVC --> V["Voids: if/elif per carrier"]
NEW["Add a carrier"] --> EDIT["Edit every branch - and miss one"]
EDIT --> BUG["Carrier works for labels, silently unsupported for voids"]The carrier's shape leaks into every part of the system, so each new carrier is a change in many places and a partial integration is easy to ship without noticing.
A client class per carrier
Give each carrier its own class wrapping its API. Vendor details are at least contained.
Better, and the classes still have different method names and return different shapes, so the calling code branches on which client it holds. Nothing forces a new carrier to implement everything, and the shipping service still knows that FedEx returns deliveryEstimate while UPS returns estimatedDelivery.
One interface, plus a normalised vocabulary
Define the contract the system needs and write an adapter per carrier against it:
interface CarrierAdapter {
getRates(shipmentRequest) -> [{service, price, estimatedDeliveryDate}]
createLabel(shipmentRequest, service, idempotencyKey) -> {trackingNumber, labelPdfUrl, cost}
voidLabel(trackingNumber)
getTracking(trackingNumber) -> [normalised events]
}%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
SVC["Shipping service - knows only the interface"] --> A1["UPS adapter"]
SVC --> A2["USPS adapter"]
SVC --> A3["FedEx adapter"]
A1 --> N["Normalise statuses"]
A2 --> N
A3 --> N
N --> V["LABEL_CREATED, PICKED_UP, IN_TRANSIT, OUT_FOR_DELIVERY, DELIVERED, EXCEPTION"]
ADD["New carrier"] --> ONE["One new adapter - nothing else changes"]- Adding a carrier is one file. The interface also makes an incomplete integration a compile-time or startup failure rather than a runtime surprise.
- Normalise the status vocabulary at the edge. Carriers report dozens of idiosyncratic codes; mapping them into one small set means the order page, notifications and analytics are written once. Keep the raw code alongside for support, but never let it into business logic.
- The idempotency key belongs in the interface. Label creation costs money, so every adapter must accept one and pass it through — making it part of the contract is what stops a carrier being integrated without it.
Rate shopping falls out for free: ask every adapter for rates in parallel with a deadline, and pick by price, speed or a business rule. Because the return shape is uniform, that comparison is a sort rather than a special case per carrier.
Key FlowsFlows
3.1 Create shipment and buy label
- Receive a shipment request with an idempotency key (the order + package ID).
- Rate shopping: ask the eligible carriers for rates in parallel (with timeouts), filter the options that meet the promised delivery date, and pick by cost, reliability score and business rules (carrier allocation contracts).
- Buy the label with an idempotency key, so a retry after a timeout doesn't buy two labels. If the carrier's API doesn't support idempotency keys, first check whether a label for this reference already exists before retrying.
- Save the tracking number and label, and set the state to
LABEL_CREATED. The warehouse prints the label.
3.2 Tracking
- Webhooks from carriers that support them (verified signatures) → normalize → update the shipment.
- Polling for the others (and as a safety net): poll active shipments, more often when "out for delivery" and less when "in transit" for days, within each carrier's rate limits.
- The state machine only moves forward (ignore older or out-of-order events by event time), with
EXCEPTIONfor problems (address issue, damaged). - Events → customer notifications ("Out for delivery today").
Failures and Operations
- Carrier API down: circuit breaker on that adapter, and route new shipments to the next-best carrier. Queue label purchases with retries if all are down.
- Rate limits: per-carrier token buckets for rates, labels and tracking calls.
- Stuck shipments: no update for N days → an alert and a claim workflow.
- Returns: create a return label (reversed addresses), and track it the same way.
- Invoice reconciliation: compare carrier invoices (actual weight and dimensions surcharges) with quoted costs, and flag differences.
Wrap-UpWrap-up
Hide each carrier behind an adapter implementing get rates, create or void label and get tracking, with normalized statuses. Shop rates in parallel and pick the cheapest option that meets the promise, buy labels idempotently, and track parcels through verified webhooks plus rate-limited adaptive polling into a forward-only shipment state machine that drives notifications. Protect against carrier outages with circuit breakers and fallback carriers, and reconcile carrier invoices afterwards.