Most ecommerce developers focus on the front end product pages, checkout flows, conversion optimisation. The back end gets less attention. And within the back end, order routing gets almost none.
That's a mistake.
Here's why order routing matters more than most developers realise and what the correct architecture looks like.
The problem with manual and default routing
When a seller starts out, order routing is simple. One warehouse. One carrier. Every order goes to the same place the same way.
As they scale multiple warehouses, FBA, 3PLs, multiple carriers, multiple channels — the single-location default stops making sense. But most ecommerce platforms don't route intelligently by default. They route to whatever location is configured first or leave the decision to a human.
The result:
javascript// What manual routing looks like at scale
orders.forEach(order => {
// Someone manually decides:
// - Which warehouse has stock?
// - Which carrier is cheapest for this weight/destination?
// - Which location can fulfil fastest?
// At 200 orders/day this is unsustainable
manuallyRouteOrder(order); // inconsistent, slow, error-prone
});
At 20 orders a day this is manageable. At 200 orders across five channels it produces delays, inconsistencies, and wrong decisions made repeatedly before anyone notices.
What intelligent routing actually requires
A proper order routing engine evaluates multiple variables simultaneously for every order the moment it arrives:
javascriptasync function routeOrder(order) {
const [
availableLocations,
carrierRates,
fulfillmentCapacity,
deliveryEstimates
] = await Promise.all([
getLocationsWithStock(order.lineItems),
getCarrierRates(order.weight, order.destination),
getFulfillmentCapacity(order.priority),
getDeliveryEstimates(order.destination, order.promisedDate)
]);
const optimalRoute = evaluateRouting({
locations: availableLocations,
carriers: carrierRates,
capacity: fulfillmentCapacity,
delivery: deliveryEstimates,
rules: order.seller.routingRules // proximity, cost, speed, value
});
return assignToFulfillmentLocation(order, optimalRoute);
}
Every order evaluated against every available option simultaneously. The routing decision made in milliseconds. No human required.
The routing variables that matter
Proximity — route to the warehouse closest to the delivery address. Reduces carrier cost and delivery time simultaneously.
Stock availability only route to locations that actually have every line item in the order. Splitting orders across locations increases cost and reduces customer experience.
Carrier cost the cheapest carrier for that specific weight, dimension, and destination combination. Not the default carrier. The optimal one for that order.
Delivery speed - if the customer was promised next-day delivery, route to the location and carrier that can actually fulfil that promise. Not the cheapest option — the right option for the commitment made.
Order value — high-value orders routed to preferred locations with better handling, insurance, or carrier reliability. Low-value orders optimised for cost.
Fulfillment type — FBA, 3PL, and own warehouse all have different lead times, costs, and reliability profiles. The routing engine needs to know the performance characteristics of each and factor them into the decision.
The architecture at scale
javascript// Event-driven routing — fires the moment an order is confirmed
orderEventBus.on('order.confirmed', async (order) => {
const route = await routingEngine.evaluate(order, {
strategy: order.seller.routingStrategy, // 'cost' | 'speed' | 'proximity'
fallback: order.seller.fallbackLocation,
constraints: {
maxSplits: 1, // don't split unless necessary
carrierBlacklist: order.seller.excludedCarriers,
locationPriority: order.seller.locationHierarchy
}
});
await fulfillmentQueue.assign(order, route);
await notifyFulfillmentLocation(route.location, order);
await updateOrderStatus(order.id, 'routed', route);
});
Routing fires immediately when the order confirms. The fulfillment location receives it instantly. The seller's custom rules — cost optimisation, speed priority, location hierarchy — applied automatically every time.
What this looks like in production
This is the architecture Nventory's order routing engine is built on intelligent routing across FBA, 3PLs, and owned warehouse locations, firing the moment an order arrives from any connected channel.
Every routing decision evaluated against proximity, cost, speed, stock availability, and order value simultaneously. Every seller's custom rules applied consistently at any volume.
Worth exploring if you're building multichannel fulfilment infrastructure: nventory.io/us/order-routing
The developer checklist for intelligent order routing
Before your client's fulfilment breaks at scale:
→ Routing decisions must be event-driven fire on order confirmation, not on a schedule
→ Every available fulfilment location evaluated simultaneously not sequentially
→ Carrier rates fetched in real time not from a static rate card
→ Stock availability checked at routing time not assumed from a cached count
→ Custom rules engine — sellers need to define their own routing logic without code changes
→ Fallback logic — what happens when the optimal location is out of stock or at capacity
→ Full audit trail — every routing decision logged with the reason it was made
The broader point
Order routing is invisible when it works and catastrophically visible when it doesn't. A customer who receives their order two days late because it was routed to the wrong warehouse doesn't know about the routing decision — they just don't come back.
Getting routing right is one of the highest-leverage engineering investments in ecommerce infrastructure. The decisions happen hundreds of times a day. Every suboptimal decision has a cost. Multiply that across volume and the margin impact is significant.
Build the routing engine properly. Your client's P&L will reflect it before their customers do.
Top comments (0)