Multi-tenant fleet SaaS codebase migration
A fleet-management and dispatch SaaS for construction-haulage operators, the firms moving aggregates, muck-away, and waste between sites. I came in as the senior engineer to lead the codebase migration, white-label through a partner agency: four years of steady client work had proven the product. What it needed was a platform that could keep up.
Anonymized under NDA: the product appears here as “Atlas Fleet”, and the tenant shown is fictional.
The problem
The system I was asked to replace served twenty client companies: real regulated work, with Sage exports, weighbridge capture, EWC coding for regulated waste, and PDF dockets with proof-of-delivery. It was not a prototype.
What held it back was structural. Signing a twenty-first client meant another deployment, and every shared fix had to be merged twenty times over. The design that got the product to twenty customers made the twenty-first expensive. Each client ran on its own database, its own git branch, and its own folder of compiled assets.
kestrel.atlasfleet.app · /login
What I built
The codebase migration onto Laravel 13 and PHP 8.4 landed in under four months: an office side for planning and compliance, and a driver app in the field; the workflows the product had built up over four years carried over intact.
The core change is one shared database: every record carries an account, each tenant stays inside its own data, and the subdomain decides who a request belongs to.
It stayed a modular monolith on purpose: one multi-tenant schema, queue workers under Supervisor, and role-scoped access down to a PIN-token driver.
kestrel.atlasfleet.app · /dashboard
Planning a week, vehicle by vehicle
The operational heart of the office side is a week-by-vehicle planning grid.
Own trucks appear by plate, subcontractors by their code, and jobs with no vehicle yet sit in an amber row, next to the daily safety checks, whose defects surface on the dashboard above.
kestrel.atlasfleet.app · /operations/planning
What drivers use
A React Native app, with subcontractor drivers joining on a PIN token instead of an account. A driver starts a transport by picking how full the truck is, closes it with a docket number, a photo, and a signature, and sees the tonnes still to move on the project.
kestrel.atlasfleet.app · driver-app
What changed
- One database instead of one per client: account scoping in a single schema, instead of twenty named connections.
- One branch instead of one per client: no more merging every shared fix across twenty branches.
- Onboarding: from a deployment to a row of data: one server, one certificate, one database, one branch.
Have a codebase to migrate?
Tell me what’s straining and how big it has grown. Sometimes the fix is a full migration; often it is one bottleneck you can pull out without one.