CASE STUDY

Multi-Carrier Parcel Shipping System

3 min read·503 words·Intermediate

Asked at

1 candidate report in Mar 2026

How to use this case study

SDE-2 / Mid

Design shipment creation, choosing a carrier (UPS, USPS, FedEx), buying a label through the carrier's API, and tracking status.

SDE-3 / Senior

Go deeper on the carrier adapter abstraction, rate shopping, idempotent label purchase, webhook + polling tracking, and the shipment state machine.

Staff / Principal

Discuss carrier outages and fallback, rate limits, SLA-based routing, returns, and reconciliation of carrier invoices.


0) Problem Restatement

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.


1) Architecture

Architecture Diagram

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"]

2) Carrier Adapter Abstraction

Each carrier has a different API, auth and data format. Define one interface and write an adapter per carrier (the adapter pattern):

interface CarrierAdapter {
  getRates(shipmentRequest)      -> [ {service, price, estimatedDeliveryDate} ]
  createLabel(shipmentRequest, service, idempotencyKey) -> {trackingNumber, labelPdfUrl, cost}
  voidLabel(trackingNumber)
  getTracking(trackingNumber)    -> [ normalized events ]
}

Adding a new carrier = one new adapter, and the rest of the system stays unchanged. Adapters also normalize tracking statuses into our own set: LABEL_CREATED, PICKED_UP, IN_TRANSIT, OUT_FOR_DELIVERY, DELIVERED, EXCEPTION, RETURNED.


3) Key Flows

3.1 Create shipment and buy label

  1. Receive a shipment request with an idempotency key (the order + package ID).
  2. 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).
  3. 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.
  4. 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 EXCEPTION for problems (address issue, damaged).
  • Events → customer notifications ("Out for delivery today").


4) 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.


5) Wrap-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.

More Case Studies

Practice with a Mock Interview

Apply what you learned in a live system design mock interview with our AI interviewer.

Start System Design Interview →