Skip to main content
A plan’s objectives list isn’t a set of weights. It’s a ranking: the engine compares two candidate solutions one objective at a time, in the order you give, and the first objective that separates them decides which one wins. This page explains that comparison, what each objective measures, and how to choose an order for your own operation.

How two solutions are compared

The engine looks at the first objective in the list. If one solution is better on it, that solution wins — whatever happens on every later objective. Only when the two solutions are tied on the first objective does the engine look at the second, and so on down the list. No amount of improvement on a later objective can make up for a loss on an earlier one. There are no weights to tune and no exchange rate between objectives: the position in the list is the only lever. Take three candidate solutions for the same plan: With the default order (maximizeMandatoryStops, then minimizeDelay, then … minimizeResources):
  • C loses straight away: it plans one mandatory stop fewer, even though it’s better on every other line.
  • A and B tie on mandatory stops, so delay decides: B wins, even though it needs an extra vehicle.
Move minimizeResources ahead of minimizeDelay and A beats B instead: you’ve told the engine that fleet size matters more than on-time delivery, so it lets stops run late to avoid dispatching an extra vehicle. C still loses under both orders, because maximizeMandatoryStops stays first.

The default list

If a plan doesn’t set objectives, the engine uses this order:
In plain terms: first plan as many mandatory stops as possible; among solutions that do, prefer less customer delay; then lower cost; then fewer vehicles; then fewer breaches of overlappingCapacitiesByStopTag limits; then more optional stops; then more stops matched to a resource’s preferredStopTags; then less total working time; then less distance. Treat it as a starting point to check against your plan, not a template to copy. Several entries do nothing unless your plan declares the data they measure (see each objective below), and an objective your operation cares about may be missing from it.

Built-in objectives

Each built-in objective is a plain string in the list. The ones marked needs data measure something your plan has to declare: without it, the objective has nothing to optimize, doesn’t raise an error, and the comparison simply falls through to the next entry. Drop those from the list rather than keeping a guaranteed no-op.

maximizeMandatoryStops

Maximizes the number of mandatory (non-optional) stops planned. First in the default list, so nothing ranked after it can justify leaving a mandatory stop unplanned. A mandatory order with a very low priority still counts here: a low priority doesn’t let an order be dropped from this count, only "optional": true does (see maximizeOptionalStops).

minimizeDelay

Minimizes the sum of delays against stops’ preferredTimeWindows. Only lateness counts: the engine never plans a stop before its preferred window opens, so arriving early is never traded against it. Needs data: at least one preferredTimeWindows. A plan whose windows are all hard authorizedTimeWindows has no delay to minimize — see Hard vs soft constraints.

minimizeCosts

Minimizes the plan’s total cost, as defined by each resource’s cost object (a flat using cost per resource used, per-kilometre km, per-hour workedHours, per-capacity costs, and so on). Needs data: at least one resource with a cost. See Cost modeling.

minimizeResources

Minimizes the number of resources used. When some resources have to stay unused, Resource.priority decides which ones go first (lower numbers matter more). It counts resources directly: no using cost is needed for it to work.

minimizeOverOverlappingCapacitiesOnStops

Minimizes how far the limits set in overlappingCapacitiesByStopTag are exceeded — the number of resources simultaneously present at stops sharing a tag, beyond the limit set for that tag. The limit is a soft target, not a hard cap: whether the engine accepts an overrun to do better elsewhere depends on where this objective sits in the list. Needs data: plan-level overlappingCapacitiesByStopTag. See Limiting simultaneous depot visits.

maximizeOptionalStops

Maximizes the number of optional stops planned, in addition to the mandatory ones. Marking an order "optional": true takes it out of maximizeMandatoryStops, so this objective is the only reason the engine plans it: without it in the list, an optional stop is unlikely to be planned at all, since it adds distance and time without serving any other objective. In the default order it comes after minimizeResources, so the engine never adds a vehicle just to plan an optional stop. Needs data: at least one order marked "optional": true.

maximizePreferredStops

Maximizes the stops served by a resource whose preferredStopTags match the stop’s tags — a soft preference, where another resource can still take the stop when that does better on a higher-ranked objective. Needs data: at least one resource with preferredStopTags matching some stops. See Driver skills and qualifications.

minimizeLargestTourDuration

Minimizes the working duration of the single longest tour in the fleet — travel, service, and waiting time included — which narrows the gap between that tour and the others. Not in the default list. It’s an objective the engine improves as far as it can, not a limit it enforces: use a resource’s maxWorkingDuration for a hard bound.

minimizeWorkingDuration

Minimizes the total working duration summed across every tour, travel, service, and waiting time included.

minimizeDistance

Minimizes the total distance travelled, summed across every tour.

minimizeEarlyLoadings

Minimizes early loadings, as declared per resource in avoidEarlyLoadings or per resource tag in the plan-level avoidEarlyLoadingsByResourceTag: each declaration names a stop tag, and optionally the capacities to consider. Not in the default list: add it explicitly, or the declarations have no effect. Needs data: at least one such declaration.

Object entries

Two kinds of entries are objects rather than names.

maximizePrecedences

Rewards the engine for respecting “this stop tag before that stop tag” rules. It takes a precedences array of pairs, each with a previous and a next stop tag, and the engine tries to satisfy as many pairs as possible. As an objective, it’s a preference, not a hard rule: a stop’s position inside an order’s stops array is the only precedence the engine always enforces.

Custom objectives

A custom entry optimizes a cost expression of your own, built from costsByResourceTag, in the direction you choose (minimize or maximize), under a name that must not collide with a built-in objective. When a custom objective replaces minimizeCosts, put it at the exact position minimizeCosts held: anywhere else, it’s traded off against different objectives and can return a different plan. See Cost modeling.

Choosing an order

  • Put first what you’d never trade away. For most operations that’s maximizeMandatoryStops: nothing ranked after it can then justify leaving a mandatory stop unplanned.
  • Check every adjacent pair, not just the first two. Swapping any two neighbours changes which solutions win. minimizeCosts before minimizeResources prioritizes total cost even if reaching it takes an extra vehicle; the reverse caps fleet size first and lets cost settle wherever that leaves it.
  • Drop the objectives your plan gives no data to, and add the ones it needs: minimizeLargestTourDuration to balance tours, minimizeEarlyLoadings for declared early loading rules.
  • Keep preferences in objectives, rules in constraints. A hard constraint is never traded off, whatever the order; only soft constraints are arbitrated by the list. See Hard vs soft constraints.

Reading objectives in a solution

A solution reports the achieved value of each objective in objectives, with its name, direction (minimize or maximize), and value. Values are in each objective’s natural unit: for example a count of planned stops for maximizeMandatoryStops, a count of resources for minimizeResources, kilometres for minimizeDistance, and hours for minimizeWorkingDuration. Comparing these values between two versions of a plan shows which objective an update actually moved.

See also