What is Order Fulfillment?
Order fulfillment covers the steps from a confirmed order to the item delivered to the customer: pick → pack → ship → track → deliver. The fulfillment system coordinates warehouse management, carrier integration, shipping label generation, and real-time tracking updates. A well-designed fulfillment system directly impacts customer experience (delivery speed, accuracy) and operational cost.
Requirements
- Route orders to the optimal warehouse (closest to destination, has stock)
- Generate pick lists for warehouse workers
- Print shipping labels and submit to carrier APIs (UPS, FedEx, USPS)
- Track shipments in real time; push updates to customers
- Handle returns and re-shipments
- 100K orders/day, SLA: ship within 24h of order placement
Order Lifecycle
PLACED → PAYMENT_CONFIRMED → ROUTED → PICKING → PACKED → SHIPPED → IN_TRANSIT
→ OUT_FOR_DELIVERY → DELIVERED
↓
EXCEPTION (delay, lost, damage)
↓
RETURN_REQUESTED → RETURN_RECEIVED → REFUNDED
Warehouse Routing
When an order is confirmed, route to the best warehouse. Routing algorithm:
- Find warehouses with all order items in stock (JOIN Inventory WHERE quantity_available >= quantity)
- Calculate estimated shipping time from each candidate warehouse to the delivery address (carrier API or shipping zone table)
- Calculate shipping cost for each candidate (carrier rate table: weight × zone × service)
- Score = alpha * delivery_time + beta * shipping_cost (configurable weights)
- Assign to the warehouse with the best score
- Reserve inventory at that warehouse
Split shipments: if no single warehouse has all items, split across two warehouses. Minimize splits (customer receives everything in one delivery when possible).
Pick List Generation
A pick list tells warehouse workers which items to retrieve. Generated when the order is assigned to a warehouse:
PickList(pick_list_id, order_id, warehouse_id, status ENUM(PENDING,IN_PROGRESS,COMPLETE),
created_at, assigned_to, completed_at)
PickListItem(pick_list_id, sku, location_code, quantity, picked_quantity, scanned_at)
-- location_code = aisle-rack-bin, e.g., "B3-12-04"
Batch picking: group multiple orders’ items by warehouse zone to minimize travel distance. A picker walks once through the warehouse collecting items for 10-20 orders simultaneously. Zone picking: divide warehouse into zones; each picker is responsible for one zone. Items are merged at the packing station.
Carrier Integration and Label Generation
def create_shipment(order_id, warehouse_id):
order = get_order(order_id)
package = calculate_package(order.items) # weight, dimensions
# Call carrier API (EasyPost, ShipStation, or direct UPS/FedEx API)
response = carrier_api.create_shipment(
from_address=warehouse.address,
to_address=order.shipping_address,
parcel={'weight_oz': package.weight_oz,
'length': package.length, ...},
service='UPS_GROUND'
)
label_url = response.postage_label.label_url
tracking_number = response.tracking_code
rate = response.rate.rate # cost in dollars
UPDATE Order SET tracking_number=tracking_number, status='SHIPPED',
carrier='UPS', label_url=label_url, shipped_at=NOW()
Shipment Tracking
Carriers provide tracking webhooks (or polling APIs) with status updates: picked up, in transit, out for delivery, delivered, exception. Consume tracking events via webhook → Kafka → update Order status and notify customer. Push notification when out for delivery and when delivered. For tracking page: query order.tracking_number → carrier tracking API (cached in Redis, TTL=5min). Exception handling: if a package is stuck in transit for > 2× expected delivery time, alert customer service team and initiate a trace with the carrier.
Returns
Return flow: customer requests return → generate return shipping label (carrier API) → email label to customer → customer ships back → warehouse receives and inspects → if condition OK: restock inventory; if damaged: write off → issue refund. Track return: ReturnRequest(order_id, reason, status ENUM(REQUESTED,LABEL_SENT,IN_TRANSIT,RECEIVED,INSPECTED,REFUNDED)). Restock only if item is resellable (no damage, original packaging). Disposition code on inspection: RESTOCK, DISPOSE, VENDOR_RETURN.
Key Design Decisions
- Warehouse routing algorithm — minimize shipping time and cost with configurable weights
- Batch picking — reduces warehouse travel time, increases throughput per shift
- EasyPost/ShipStation for carrier abstraction — single API for all carriers, automatic rate shopping
- Carrier webhook for tracking — real-time updates without polling
- State machine for order lifecycle — prevents invalid state transitions
Amazon system design is the canonical order fulfillment interview topic. See common questions for Amazon interview: order fulfillment and warehouse system design.
Shopify system design covers order fulfillment and shipping. Review patterns for Shopify interview: order fulfillment system design.
Uber Eats system design covers order fulfillment and delivery logistics. See design patterns for Uber interview: order fulfillment and logistics design.