Public transport
Hard constraints wanted a solver, and the platform wanted deleting
A bus operator asked for machine learning to improve driver rostering. The algorithm question had a settled answer, because hard legal limits want a constraint solver. The finding worth the engagement was the platform the previous team had left behind, costing more each month than the problem was worth.
Two decisions kept deliberately apart. One settled by the literature, one nobody had asked about.
Problem
Rosters were built by hand in each depot, taking days and producing uneven work. Overtime was high and drivers complained about fairness. The incoming operations director asked for an AI rostering system.
Context
Several depots, a unionised workforce with a local agreement on rest and rotation, and a service contract with punctuality penalties.
Current architecture
A scheduling package of record, a set of microservices built by a previous team to ‘modernise’ around it, a managed Kubernetes cluster, two databases, and a message bus carrying traffic between four services that are always deployed together.
Constraints
- Legal: driver hours are governed by hard limits, not preferences.
- Industrial: the local agreement adds constraints beyond the law, and cannot be traded away by software.
- Operational: a roster must be publishable weeks ahead and stable once published.
- Skills: one platform engineer supports everything, and is the single point of failure for the cluster.
Evidence
Each statement placed on the ladder before it was used.
Regulation (EC) No 561/2006 applies to vehicles constructed or permanently adapted to carry more than nine persons including the driver, and sets daily driving of 9 hours (extendable to 10 hours twice a week), a 56-hour weekly limit, and a regular weekly rest of at least 45 hours in any two consecutive weeks. Article 3(a) then excludes regular passenger services whose route does not exceed 50 km, which covers much of this operator’s network and leaves those drivers under the applicable domestic rules instead.
Four of the six services are deployed together every time, share one database and have never been scaled independently. Established from deployment history and the cluster manifests.
A roster that breaches a hard limit is not a lower-scoring roster. It is an unlawful one.
Overtime is driven by poor rostering, not by absence. Believed in the depots; absence data suggested both matter.
Fairness and cost pull against each other, and the trade has to be decided by people, not inferred from history.
Questions that changed the answer
- Which constraints are legal, which are contractual, and which are habit?
- Which regime applies to each service, 561/2006 or the domestic rules covering regular routes under 50 km? That question splits the network in two, and it was asked first.
- Is the objective minimum cost, maximum fairness, or stability once published? These give different rosters.
- What does the current platform cost to run and to keep secure, against what it is used for?
- If a model produced an unlawful roster, how would anyone notice before publication?
Options
Including the one nobody wanted to discuss.
Machine learning over historical rosters
Learn from past rosters to propose new ones.
What it costs: Learns historical practice including its unfairness, and cannot guarantee a hard constraint, the one property this problem requires.
Constraint solver with explicit rules
Encode legal and agreement constraints, choose an objective, and let a solver produce rosters that are correct by construction.
What it costs: Requires the operator to state the objective out loud and agree it with the union; the hardest part of this is not technical.
Buy a rostering product
Adopt a specialist package.
What it costs: Strong fit for the legal rules, weaker for the local agreement; kept as a serious alternative and priced.
Leave the inherited platform as it is
Build rostering on top of the existing cluster, message bus and six services.
What it costs: Keeps a monthly bill larger than the problem is worth, and makes the new code inherit the estate’s deployment and on-call story.
Collapse the co-deployed services and retire the cluster
Merge the four services that always ship together into one, drop the message bus, retire the managed cluster.
What it costs: Weeks of work with no user-visible feature at the end of it, and it has to be sequenced before the rostering build to be worth doing.
Economics
Four horizons, not one estimate.
- Build
- A solver model plus a constraint library. Small, and mostly analysis rather than code.
- Run
- The decisive finding. The existing cluster cost more each month than the rostering problem was estimated to be worth, for four services that deploy as one.
- Change
- Agreement changes are a constraint edit in the solver; in the current estate the same change touches several services and a bus.
- Exit
- Low for the solver. The constraints are written down and portable. High for the platform, which is why removing it was treated as its own decision.
Decision
Build rostering as a constraint-solver problem with explicit legal and agreement rules and a stated objective. Separately, collapse the four co-deployed services into one and retire the cluster and the message bus.
Why
The first half is not an original conclusion and was not presented as one: bus driver rostering under hard legal limits is a well-established constraint-programming problem, and a solver guarantees what a learned model can only score. The second half is what the engagement was actually worth. A cluster, a message bus, two databases and six services were supporting one team, one deployment cadence and no independent scaling. The monthly bill exceeded the estimated value of the rostering problem itself. The two were gated separately, so the platform decision had to stand on its own evidence rather than arrive in the slipstream of a project everyone already wanted.
Why not the alternatives
- Machine learning over historical rosters: Cannot guarantee a legal limit, and would reproduce the historical unfairness that prompted the request.
- Buy a rostering product: Priced and taken seriously; rejected because the local agreement is the awkward part and would have become permanent customisation.
- Leave the inherited platform as it is: The monthly run cost exceeded the estimated value of the rostering problem itself, for four services that have never deployed or scaled independently. Nothing in the estate pointed at a requirement.
Trade-offs accepted
- Someone has to state the objective and defend it. Software cannot avoid that conversation, only delay it.
- Collapsing the services is work with no visible feature at the end of it.
Reversibility
Constraints written down in one place are the most portable asset the operator could own. They outlive whichever solver is used. The platform removal was staged so each step could be halted.
Complexity budget
Kubernetes, a message bus, two databases and six services supported one team, one deployment cadence and no independent scaling. None of it pointed at a requirement. The rostering work was sequenced behind the removal so the new code did not inherit the estate.
Non-functional requirements
- No roster may be published that breaches a hard constraint, enforced rather than scored
- A roster must be explainable to the driver it affects
- Rostering must run without the platform engineer being available
Decision gate
GO on the solver, and a separate explicit GO on removing the platform, deliberately not bundled so the second could be argued on its own evidence.
Implementation
Constraints written down and agreed with union representatives before any code. A solver producing rosters correct by construction. Services merged one at a time, cluster retired last.
What the decision was expected to achieve
Rosters produced in minutes rather than days, no unlawful roster reaching publication, and a materially smaller monthly platform bill. Overtime hours and constraint violations at publication are the measures.
No outcome is claimed. This is an illustrative example, so there is nothing measured to report, and a real engagement would state what happened and how it was verified.
Lessons
- If the rules are hard limits, you want a solver. That is settled practice in this field, not an insight. Knowing which of your conclusions are settled is part of the job.
- The expensive problem is often the estate you inherit, not the one you were asked about. Nobody had priced the platform because nobody had been asked to.
- Gate unrelated decisions separately. Bundling a contested decision with an obvious one buys the contested one a free pass.
- Write the constraints down. They are the durable asset. The algorithm is replaceable.

