← All writing
articleJun 21, 202519 min read

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.

OptimizationLogisticsAlgorithmsArchitecture
Driver Dispatch System: Assigning Work When the Vehicle Is Not the Constraint cover illustration

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:

  1. Put hours-of-service state in the vehicle description used for scoring, not in a post-assignment check.
  2. Use projected end-of-day positions, not current positions, when scoring assignments. This is the change that makes multi-day planning work.
  3. Structure the system as nightly plan / continuous assignment / event re-solve, and keep the layers separate.
  4. Make the nightly plan a soft constraint in the assignment layer, with a visible penalty and a dispatcher override.
  5. Generate candidates by region, equipment type, and HOS feasibility. Never score the whole fleet.
  6. Propose single changes with costed options on events, not full replans. Humans execute changes, not diffs.
  7. Capture every override with a reason, and treat that log as the most valuable training data you have.
  8. Publish empty legs and model backhaul timing explicitly. The money is in the return leg.
  9. Give the dispatcher a reason string on every recommendation. “3 km, 8h driving left, trailer in place” is what makes the board usable.
  10. Model appointment risk as a cost, not a hard constraint, and surface at-risk loads proactively.
  11. Make the customer-facing ETA a range from the actual vehicle state, not a network average.
  12. Version the cost model. When on-time drops, the first question is which cost changed.
  13. Support one-depot and multi-depot configurations from the same code, with region-aware settings.
  14. 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.

Technical references

Keep reading
Browse everything