Driver Dispatch System: Assigning Work When the Vehicle Is Not the Constraint
Freight dispatch with capacity, hours-of-service, and multi-depot realities — the parts of dispatch that a load board does not solve.

Freight dispatch looks like a proximity problem: find the truck closest to the pickup. Anyone who has watched a real dispatch office for an hour knows this framing is wrong, and the reason is that the vehicle is almost never the binding constraint. The hours-of-service clock, the trailer’s physical position in a queue, and the appointment at the other end usually are.
The scale
a regional carrier
600 trucks
40 depots
8,000 loads per day
- 5,000 pre-booked (yesterday)
- 2,500 same-day
- 500 spot/overflow
300 appointments per depot per day
what dispatch must decide, per load
- which truck
- when it departs
- whether to combine with another load
- where the deadhead miles go
legality, per driver
- hours of service: 11 driving, 14 on-duty,
10 consecutive off (US) or 56h/90min (EU)
- trailer/driver hours
- rest, breaks
- appointment windows
The hours-of-service constraint is what makes this hard, and it is invisible in the naive formulation. A truck 5km from the pickup is not a good match if it is 20 minutes from its mandatory rest, because taking the load means the next load becomes illegal.
The problem, stated properly
given
loads L: origin, destination, size,
time window, commodity,
special requirements
vehicles V: current location, capacity,
equipment type, availability,
HOS state (driven, on-duty,
remaining distance to rest)
trailers T: type, location, reposition cost
decide
assignment of loads to vehicles,
ordering per vehicle, and deadhead
routing
subject to
- capacity at every leg
- time windows and appointments
- HOS: driving and on-duty limits
- equipment match (reefer, hazmat, oversize)
- driver rules, rest, breaks
- trailer availability and repositioning
minimise
empty miles + late risk + detention
+ fixed cost per load + trailer moves
Note what is in that minimise and what is not. Empty miles are a real cost, and they are not the main one. Late deliveries against a customer appointment are usually a contractual and relationship cost well above the fuel.
Why the single global optimiser is the wrong answer
attempt
every load, every vehicle, globally,
in one optimisation, every 15 minutes
why it fails
- the joint problem across 8,000 loads
and 600 trucks is enormous
- most of it is already decided
(5,000 pre-booked loads are 90%
of the plan and do not change)
- a global re-solve ignores that the
business is organised by depot and
driver, and humans hold context
the model does not have
- a global re-solve is unstable: small
changes cause large rearrangements,
which the office cannot execute
The structure that works is hierarchical, and it mirrors how the business actually operates:
LAYER 3: network plan (nightly)
- build load plans by region and time
- decide who carries what, at what level
- identify backhaul candidates and
trailer repositioning
- hours: runs unattended
LAYER 2: assignment (continuous)
- within a region/depot, assign incoming
loads to available trucks
- respect the layer-3 plan as a soft
guide, not a hard constraint
- seconds: runs every few minutes
LAYER 1: re-solve on events (real time)
- a truck broke down
- a customer moved an appointment
- a load was cancelled
- HOS forced a rest earlier
- minutes: triggered, scoped, fast
The soft-vs-hard distinction between layers is the design decision that makes this work. A pre-booked plan should be a strong preference in layer 2, but a new high-priority load or a breakdown must be able to deviate from it. If the plan is hard, the system cannot respond; if it is not present at all, the system has no stability. A soft penalty that the dispatcher can see and override is the right middle.
The constraint that actually binds: hours of service
HOS state per driver
driven_today: 6.5h of 11h
on_duty_since_break: 9.0h of 14h
consecutive_off: 8h required
distance_to_eos: 180 km
a 5km-away truck with 6h driving left
is a fine match today and
unusable for tomorrow's early run
a 40km-away truck with 10.5h left
is worse today but still has
a full day tomorrow
the assignment that maximises today's
utilisation can strand a truck in the
wrong place tomorrow, which shows up
as a much more expensive problem three
days later
Two consequences. First, the HOS state must be part of the vehicle description in every assignment, not a check applied afterward — if it is a post-check, the system will produce assignments it then has to reject, wasting the match. Second, and less obviously, the assignment must consider tomorrow. A load assigned today consumes driving time that constrains tomorrow’s plan, and a system that optimises each day independently will produce a pattern of progressively worse days.
The concrete mechanism: keep a projected end-of-day position for each truck (where it will realistically be at end of shift, given the assigned work) and use that projected position, not the current one, when scoring the next assignment. This single change is the difference between a system that plans a day and one that plans a week badly.
Multi-depot and the load board
LOAD BOARD model
loads are listed, drivers/equipment
claim them (or a dispatcher assigns)
strengths
- simple
- scales with humans, not with code
- good for the long tail of spot loads
weaknesses
- the dispatcher must know where the
good match is; the board rarely
tells them
- no optimisation across loads
- first-come-first-served by default,
which is a policy, not a solution
The load board is not the enemy. It is genuinely good at handling the 500 spot loads a day that do not warrant algorithmic treatment, and it gives the office a manual override on every decision, which matters more than people credit. The right structure is: the load board is the surface, and dispatch logic runs underneath it, with recommendations and reasons attached to each load and the ability to override.
A recommendation with a reason (“3 km away, has 8h of driving left, trailer already positioned, no appointment conflict”) turns the board from a list into a decision aid, and it is what lets a dispatcher accept most of what the system proposes while still catching the cases the model got wrong.
Backhaul, the actual money
the problem
40% of loaded miles have an empty
return leg
why it happens
- the return load is booked later
- it originates in a different system
- the shipper is not your customer
so you do not see their demand
- timing does not line up: your truck
arrives after their freight is ready
a load board with the same loads in
both directions still leaves this empty
-> the problem is visibility and timing,
not allocation
Backhaul is a search problem over time and geography, and the mechanisms that help are unglamorous:
Publish your empty legs. A feed of “returning empty from X to Y on date Z” is a real product for the other direction. Many carriers already have the data; few expose it.
Look one hop out. When matching an empty leg, include inbound loads that originate near your destination and are outbound from there. The two-headed matching problem beats matching one leg at a time.
Timing is a real constraint, not a detail. An empty leg on the same route two hours earlier is worth much less than one that matches the timing. Modelling this is where a good dispatcher beats a good algorithm: humans know that the produce auction clears at 4am and the market is 90 minutes away.
Container and drayage scheduling is a specialised variant where the “vehicle” may not be yours at all, and the constraint set is a different shape again (chassis availability, port windows, free time). It is worth naming because dispatch systems that are designed for owned fleets tend to break on drayage.
Real-time re-solve: the actual product
events that trigger a re-solve
- HOS forces a rest earlier than planned
- a load is cancelled or re-booked
- a customer reschedules
- a vehicle is out of service
- a new high-priority load appears
- traffic makes an appointment infeasible
- equipment is available earlier/later
what the dispatcher wants
not "here is a new plan"
but
"here is the ONE thing that should
change, and here is what it costs,
and here is what happens if you
do nothing"
the good alert
Load 4471 is at risk of missing the
14:00 appointment at Customer X
cause: the 11:20 pickup ran 90 min
late (trailer swap)
options:
A) divert to the 15:30 appointment
(needs customer approval, 2h slack)
B) split: another truck takes the
second half (costs $180, keeps 14:00)
C) accept late, notify customer
(contractual penalty ~$400)
recommended: B
That is a real design pattern: single-change proposals with costed options, not full replans. A dispatcher presented with a new full plan every twenty minutes cannot execute it. A dispatcher presented with “here is the one change that matters” can act in thirty seconds. This is the same stability principle as ride matching, applied to a human in a loop, and it is the difference between a system that is trusted and one that is politely ignored.
Failure stories worth testing
Assign every load to the nearest truck, ignoring HOS
Rejections and illegal plans appear. Confirms HOS must be in the candidate scoring, not a post-check.
Optimise each day independently with no projection of end-of-day position
Watch the 2-3 day pattern of progressively worse assignments. This is the “tomorrow” failure and it is invisible on any single day.
Run a global re-solve every 15 minutes across all depots
Watch the dispatch office’s ability to execute. Unstable plans get ignored, which is worse than no plan.
Remove the soft penalty for the pre-booked plan and let every new load re-optimise freely
Measure how far the actual execution drifts from the published plan. The office cannot follow it.
Take the load board with loads in both directions and no timing model
Measure backhaul rate. It barely moves, which is the point: the board was never the constraint.
Publish empty legs and measure the fill rate
If it does not improve, the constraint is timing and trust, not visibility. That is a useful negative result.
Force a driver’s rest 90 minutes earlier and re-solve
Confirm the system proposes a specific change rather than a full replan, and that the options are costed.
Make a trailer swap take 90 minutes instead of 20
Measure appointment risk propagation. This is the case that separates a model with real ETAs from one with averages.
Add a 400% surge demand load and watch depot behaviour
This is where the hierarchical structure either holds or the depot collapses into manual triage.
Remove the equipment-type constraint from scoring
Oversize and reefer get assigned to dry van trailers. Post-validation will catch some; the rest fail at the dock.
Test with a single-depot operation and a 40-depot operation using the same config
The global settings that work for one depot are wrong for forty. Region-aware configuration is required.
Add a customer appointment that can only be met by a driver who is already near end of shift
Confirm the system flags the infeasibility instead of producing a plan that will be discovered to be impossible at 16:00.
Make the dispatcher override 30% of recommendations and log the reasons
That log is the training data for the cost model, and the reasons are usually about context the system does not have.
A production-ready architecture
NIGHTLY / LAYER 3: network plan
- demand forecast by region and window
- build regional load plans
- identify backhaul and trailer moves
- projected end-of-day positions
|
v
+----------------------------------------------------------+
| LOAD & VEHICLE STATE |
| - load: window, size, equipment, priority, appointment |
| - vehicle: location, capacity, HOS, equipment, trailer |
| - projected EOD position |
| - cost model: empty miles, late risk, detention, fixed |
+----------------------------+-----------------------------+
|
event / poll
|
v
+----------------------------------------------------------+
| LAYER 2: assignment (seconds) |
| - candidate generation: region + equipment + HOS feasible|
| - score: cost model + soft penalty for deviating |
| from the nightly plan |
| - solve: min-cost assignment, auction, or greedy+swap |
| - respect driver-specific constraints and preferences |
+----------------------------+-----------------------------+
|
v
+----------------------------------------------------------+
| LAYER 1: event re-solve (minutes) |
| - single-change proposals with costed options |
| - scoped to the affected vehicle or load |
| - human in the loop with override + reason capture |
+----------------------------+-----------------------------+
|
v
+----------------------------------------------------------+
| COMMUNICATION SURFACE |
| - driver app: next stop, window, HOS warning |
| - dispatcher board: recommendations with reasons |
| - customer: ETA and status with honest ranges |
+----------------------------+-----------------------------+
|
v
+----------------------------------------------------------+
| LEARNING LOOP |
| - override reasons -> cost model calibration |
| - actual vs planned ETAs -> traffic/handling model |
| - empty-leg outcomes -> backhaul value |
+----------------------------------------------------------+
watch: on-time %, empty miles, HOS exceptions per 1000 miles,
override rate, backhaul fill, appointment risk alerts
Delivery checklist:
- Put hours-of-service state in the vehicle description used for scoring, not in a post-assignment check.
- Use projected end-of-day positions, not current positions, when scoring assignments. This is the change that makes multi-day planning work.
- Structure the system as nightly plan / continuous assignment / event re-solve, and keep the layers separate.
- Make the nightly plan a soft constraint in the assignment layer, with a visible penalty and a dispatcher override.
- Generate candidates by region, equipment type, and HOS feasibility. Never score the whole fleet.
- Propose single changes with costed options on events, not full replans. Humans execute changes, not diffs.
- Capture every override with a reason, and treat that log as the most valuable training data you have.
- Publish empty legs and model backhaul timing explicitly. The money is in the return leg.
- Give the dispatcher a reason string on every recommendation. “3 km, 8h driving left, trailer in place” is what makes the board usable.
- Model appointment risk as a cost, not a hard constraint, and surface at-risk loads proactively.
- Make the customer-facing ETA a range from the actual vehicle state, not a network average.
- Version the cost model. When on-time drops, the first question is which cost changed.
- Support one-depot and multi-depot configurations from the same code, with region-aware settings.
- Include equipment type, trailer availability, and special requirements in the candidate filter, not as a warning at the dock.
Common mistakes
| Mistake | What actually happens | Better decision |
|---|---|---|
| Nearest-truck assignment | HOS and equipment ignored, plans rejected | HOS and equipment in scoring |
| HOS as a post-check | Matches are made then thrown away | HOS in the candidate description |
| Today’s position for assignment | Tomorrow’s plan strands trucks | Projected EOD position |
| Global re-solve every 15 min | Plans the office cannot execute | Scoped, single-change proposals |
| Hard pre-booked plan | The system cannot respond to events | Soft penalty with override |
| No pre-booked plan at all | No stability, drift from execution | Nightly plan as a strong guide |
| Load board as the whole system | Optimisation across loads never happens | Logic underneath, board on top |
| Expecting backhaul from allocation | Half the capacity stays empty | Publish empty legs, model timing |
| Full replans on events | Humans cannot execute changes | One change, costed options |
| Recommendations without reasons | Dispatcher cannot sanity-check | Reason string on every proposal |
| Fixed ETA to customers | Late risk discovered at the dock | Range from actual vehicle state |
| Ignoring override reasons | The best training data is discarded | Capture and review overrides |
| Average travel times | Appointment risk is invisible | Vehicle-specific ETAs |
| One global config for all depots | Wrong at every scale but one | Region-aware settings |
| No equipment match in candidates | Dock failures | Filter by equipment type |
| Ignoring detention cost | The real cost of a bad plan is hidden | Detention and late penalty in the model |
The complete story in one minute
Freight dispatch is bin-packing with time, geography, and legality attached, and the vehicle is rarely the binding constraint. The constraint that binds is hours of service — a truck five kilometres away is a poor match if it is twenty minutes from mandatory rest, because taking the load makes the next one illegal. So HOS state has to be part of the vehicle description used for scoring, not a check applied afterwards, and the scoring has to use the truck’s projected end-of-day position rather than where it is right now. That single change is the difference between a system that plans a day and one that plans a week badly.
A single global optimiser does not work at scale and does not match how the business is organised. The structure that works is hierarchical: a nightly network plan by region, a continuous assignment layer that treats the nightly plan as a strong soft preference, and an event-triggered re-solve. Soft rather than hard is the decision that matters — hard and the system cannot respond to a breakdown, absent and it has no stability.
Backhaul is where the money is, and a load board with the same loads in both directions still leaves half your capacity empty. The constraint is visibility and timing, not allocation: publish the empty legs, look one hop out, and model the fact that an empty leg two hours early is worth much less than one that lines up.
The real product is the event re-solve, and it is not a new plan. It is “load 4471 is at risk of missing the 14:00 appointment, here are three costed options, I recommend the split” — a single change a dispatcher can act on in thirty seconds. Capture every override with its reason, because that log is the best training data in the building. And attach a reason string to every recommendation, so a dispatcher who does not trust the model can still check it against what they know.
HOS in the candidate description, projected EOD positions
nightly plan / continuous assignment / event re-solve
soft plan with visible penalty; single-change costed proposals
backhaul via published empty legs and timing models
override reasons as the best training data you have
The hard part was never finding the closest truck. It was noticing that the closest truck is the wrong answer four times out of five, and that someone in the office already knew it.
What this team still owns
The optimiser proposes; dispatch owns acceptance and the driver owns what is physically and legally possible. Persist the rule-set version, travel-time snapshot, equipment state, appointment constraints, and reason for every override with the assignment. A plan is not compliant because the solver returned it: validate it again against the effective hours-of-service rules when it is accepted and when material state changes.


