A Pick List Is Not a Route
How a barcode scan became a warehouse route: order lookup, slot choice, shelf ergonomics, and the graph hidden under a pick list.
Series
Warehouse Route OptimizationSeries parts
On this page
A warehouse order can be broken down into: product codes, quantities for each item, and a barcode identifier for the parcel order. The items, which may range from small screws to larger hardware components, are scattered across racks, shelf tiers, and corridors. Some sit at ground level, others hang high enough that picking one up requires a ladder or a forklift.
If you’re acquainted enough with the company’s intricate ERP system, you may query it to find item codes, quantities, and stock locations. In a half-decent system, an operator may even get this information by scanning a barcode on the order sheet. However, ERPs do not provide guidance on how an operator should navigate the building to complete their pick-up round in a timely manner, or how to keep the inventory up to date. And operators are not robots: they have to walk, bend, and lift. The order of operations has a direct impact on their speed, fatigue, and safety.
Between 2019 and 2022, I built a warehouse picking system for Vivaldi srl, an audiodomotic manufacturing company in the Venice area. As a sole engineer, I designed and implemented a full-stack solution that integrated with their existing (outsourced) ERP database, optimized picking routes, provided Excel import-export, print-friendly reports, as well as a barcode-first interface for operators. I also built a benchmark suite and a simulation environment to test my optimization engine against real-world scenarios, and benchmarked it against an unfeasible exact mathematical formulation of the problem, as well as a naive greedy algorithm.
I maintained the system and adapted it to new business requirements as I was studying for my Master’s Degree in Computer Science. I’ve later used it as a source of inspiration for my studies in graph theory, operations research, and mathematical optimization, and refined the system I had previously built out of my own curiosity and interest.
The Workflow
The barcode scan resolved one or more orders. The backend loaded every requested item, the quantity requested for each item, and every warehouse slot where that item currently existed. A single item could appear in multiple places: 8 units at A1.1, 50 at B2.1, 12 at E5.4. Choosing a route therefore meant choosing slots, not just sorting a list of coordinates.
For every line item, the system had to answer three questions:
- Can the order be fulfilled with the current stock?
- If the item exists in more than one slot, which slot should this order consume first?
- Once the slots are chosen, in what order should the operator visit them?
The first question is trivial inventory logic. The second and third are more interesting, and led me to model the warehouse as a system with graphs and constraints.
Position Encoding
A warehouse location like E5.2 carries two pieces of information, separated by a dot (.).
E5 is a two-dimensional coordinate on the floor: aisle E, rack 5.
.2 is a vertical shelf tier. The first coordinate influences the walking distance, whereas the latter impacts operator ergonomics and picking effort.
That split mattered because slots on the same rack may have the same walking distance, but different human cost. Ground-level shelves, in this warehouse, were shelves 1, 4, 7, and 10. They were faster and less tiring to pick from than mid or high shelves. A route that saves two meters of walking but sends someone to four high shelves is decidedly worse for the person doing the work.
The optimizer I was tasked with building treated slots as physical places with different costs, rather than interchangeable buckets of stock. It preferred ground-level picks when possible, and it prioritized draining nearly-empty slots. The idea was simple: if an order requested 10 units and one slot contained exactly 8, emptying that slot first reduced future fragmentation.
This is just one of the rules agreed upon with the warehouse stakeholders to shape the route optimizer. Once the engagement with my client was concluded, I later generalized the system to allow a more flexible set of rules, and a swap mechanism to change the algorithm without modifying and re-deploying the codebase.
The Floor Plan
After inspecting it in person, I modeled the physical warehouse as a 17x31 grid: walkable floor, impassable shelf blocks, named rack positions, and the door where the route starts. You can play the example route to see how the same order changes from a list of shelves into a walkable path, then click a rack to inspect its shelf slots.
Route manifest · 8 picks
- 00StartEntrance
- 01A2.5pick
- 02B3.2pick
- 03A5.2pick
- 04C7.3pick
- 05E5.1pick
- 06I5.4pick
- 07H1.3pick
- 08H2.6pick
- 09ReturnEntrance
In this grid structure, named rack positions are vertices, and walking distances between them are weighted edges: we thus have a graph in our hands. Shelf blocks remove direct paths that would be possible in open space. In this variation, the door is the depot point. An order asks for a route through a subset of vertices, but the subset is chosen dynamically from inventory availability.
That last point is why the problem was never a clean textbook TSP. Rather than facing a standard “visit these 30 nodes while minimizing distance” problem, I had to deal with the “fulfill these requested quantities, from these possible slots, under these warehouse policies, then visit the chosen slots in a sensible order” constraints as well.
The Operators’ Point of View
Without routing aid, an ERP order could send an operator east, west, east again, then back toward a rack they had passed minutes earlier. These seemingly innocent inefficiencies accumulate across shifts and months, and become a time tax paid on every other warehouse operation. Facilities Planning estimates walking as roughly 55% of order-picking time in many warehouses1, and that matched what I observed in this particular warehouse.
In terms of UX, the route screen had to stay boring, and go straight to the point.
Operators would scan the barcode on their order sheet, and after a brief loading time, they’d get an already sorted pick list (E5.2: 5 units; I7.1: 10 units; …), walk it, and come back.
There’s one edge case in particular that influenced the design of the system more than I expected: partial fulfillment. Imagine an order requesting 120 units out of only 100 available across all warehouse slots. Would it make sense to reject that order entirely?
What I did, instead, was to return the 100 fulfillable units, list the shortfall and mark this exceptional case prominently in the route report, and keep going as usual. As it turns out, warehouse-related workflows have a lot of these “continue with the useful part” cases. Software that treats them as exceptions quickly becomes annoying both for the operators and the authors.
From Scan to Route
The pipeline looked like this:
- Resolve the barcode to one or more orders.
- Query the ERP-backed SQL Server database through stored procedures.
- Resolve requested items into all available warehouse slots.
- Mark unfulfillable quantities.
- Choose slots using ground-level and depletion preferences.
- Order the selected slots into a route from the door and back.
- Return a pick list that the operator can follow without thinking about the graph.
The product contract stayed stable: barcode in, route out. That boundary forced me to separate slot choice from route ordering, report shortages without killing the whole pick, and return something an operator could follow immediately instead of something mathematically pretty. It also made the optimizer measurable as a separate unit: slot choice, distance lookup, and route ordering could be benchmarked without changing the workflow on the floor.
Aside
Curiously, the database retrieval was at first much slower than the route optimization itself. When I started the project, I was forced to use SQL Server 2008, and wasn’t allowed to change the database schema or add new indexes. For every SQL stored procedure I’d touch, I had to pick up the phone and call the ERP vendor to get my changes in. Why, did you think we’d use Prisma migrations, or Drizzle’s?
Welcome to old school Italian corporate IT, I guess ¯\_(ツ)_/¯.
The next article follows that route core and why lookup tables beat online pathfinding on this warehouse.