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
{“@context”:”https://schema.org”,”@type”:”FAQPage”,”mainEntity”:[{“@type”:”Question”,”name”:”How does warehouse routing work for order fulfillment?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”When an order is placed, the fulfillment system must decide which warehouse to ship from. Routing factors: (1) Stock availability: the warehouse must have all ordered items in stock (or can be split across two warehouses). (2) Shipping time: distance to the delivery address, carrier transit days by zone. (3) Shipping cost: carrier rates depend on weight, dimensions, and shipping zone. (4) Warehouse capacity/throughput: avoid overloading a single warehouse during peak periods. Algorithm: fetch all warehouses with stock. For each, calculate estimated_delivery_date = current_time + pick_pack_time (4-8h) + carrier_transit_days. Calculate shipping_cost from carrier rate table. Score = weighted combination of delivery_days and shipping_cost. Assign to the lowest-score warehouse. Tie-break: prefer fewer split shipments over slightly longer delivery time. Cache carrier rate tables in memory (refresh daily) — avoid API calls for every order routing decision.”}},{“@type”:”Question”,”name”:”What is batch picking and why does it improve warehouse efficiency?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”In single-order picking, a warehouse worker retrieves all items for one order, then moves to the next order — travels the entire warehouse for each order. With 100 orders, the worker walks 100 × warehouse_traversal_distance. Batch picking: group multiple orders' items together. The worker collects items for 10-20 orders in a single warehouse walk, placing items in labeled slots or totes. Efficiency gain: the worker might retrieve 50 units of laptop chargers for 10 different orders in one trip instead of 10 separate trips. Zone picking: divide the warehouse into zones (electronics, clothing, home goods). Each picker handles only their zone. Items from multiple zones are consolidated at a packing station. "Pick and pass": items are picked in zone A, passed to zone B worker who adds their items, passed to zone C, etc. Technology: barcode scanners or RF devices guide pickers to locations and confirm correct items. Pick-to-light: LED indicators on bins tell pickers where to go next.”}},{“@type”:”Question”,”name”:”How do carrier APIs like EasyPost work for label generation?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Carrier APIs abstract the complexity of integrating with individual carriers (UPS, FedEx, USPS, DHL). EasyPost, ShipStation, and ShipBob provide a unified API. Request: POST /shipments with from_address, to_address, parcel (weight, dimensions), carrier preferences. Response: a list of rates (carrier, service, price, delivery_days) + label URL and tracking number for the selected service. Rate shopping: fetch rates from multiple carriers simultaneously. Select the cheapest option that meets the delivery commitment. Label printing: the label_url is a PDF or ZPL (thermal printer) label. The warehouse printer generates the physical label affixed to the package. EasyPost handles: carrier account management, rate caching, address validation, and tracking aggregation. For high volume (100K labels/day): use EasyPost's bulk endpoint and maintain carrier accounts at volume discount tiers (negotiated rates with UPS/FedEx based on annual volume).”}},{“@type”:”Question”,”name”:”How do you implement real-time shipment tracking?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Carriers provide tracking events via webhooks (FedEx, UPS, USPS all support webhooks or polling). EasyPost aggregates these into a single webhook stream: POST /tracking_webhook with {tracking_code, status, details, timestamp}. Architecture: carrier webhook → EasyPost webhook → your webhook endpoint → Kafka → tracking event processor → update Order.tracking_status → notify customer. Customer notifications: push notification when status changes to "Out for Delivery" and "Delivered." SMS and email for "Delivered." Exception alerts: if status = "Exception" (package lost, returned, address problem) → alert customer service within 15 minutes. Tracking page: client queries GET /orders/{id}/tracking → returns cached tracking events from Redis (TTL=5min). If cache miss: query EasyPost tracking API (rate limited) or internal DB. Do not query the carrier API directly on every page load — cache aggressively.”}},{“@type”:”Question”,”name”:”How do you handle returns in an order fulfillment system?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Return flow: (1) Customer requests return via UI → create ReturnRequest record. (2) Validate eligibility: within return window? Item category eligible? (3) Generate return label: call carrier API for a pre-paid return label, email to customer. (4) Customer drops off package. (5) Warehouse receives: scan return label → mark return as RECEIVED. (6) Inspection: warehouse worker inspects item. Decision: RESTOCK (undamaged, resellable) or DISPOSE (damaged) or VENDOR_RETURN (defective). (7) If RESTOCK: increment inventory quantity_on_hand. (8) Trigger refund: publish RefundRequested event. Payment service processes refund. Automation: most return steps (label generation, inventory update) should be automated. Human inspection is required to determine disposition. Monitor: return rate by SKU (high return rate = product description mismatch or quality issue). Return rate by carrier (high damage rate for specific carrier → switch routing).”}}]}
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.