Skip to content

02 / Confidential client · US · 2019-present

Seven years on call for one platform

A compliance platform serving 3,406 fleet companies, built and run by one person for seven years.

Role
Solo Developer & Architect
Timeline
7+ years
Type
DOT compliance SaaS
Stack
Laravel 10 · Vue 3 · Inertia.js · Tailwind CSS · MySQL · Redis · Stripe · Pusher

At a glance

  • Multi-tenant DOT compliance SaaS serving 3,406 fleet companies and 8,859 active employees. I have built and run it alone since April 2019.
  • Five authentication guards separate super admins, staff teams, company admins, company teams, and multi-company agents across a ~75-table schema.
  • Stripe at three levels: subscriptions, per-order charges, and one-time fees, all reconciled through webhooks.
  • Currently migrating Blade + Vue 2 to Inertia + Vue 3 page by page, on a live system, with both stacks in production.

01 / Context

Compliance is a product that can't be wrong

US trucking companies are legally required to run DOT drug-and-alcohol testing programs: pre-employment tests, random pools, post-accident protocols, FMCSA Clearinghouse queries, motor-vehicle-record checks. The platform turns that regulatory maze into software. Fleet companies enroll drivers, order tests, collect e-signatures, and stay auditable.

The client hired me off Upwork in April 2019 for what could have been a small job. It became the longest engagement of my career. I architected the platform, built it, deployed it. I have been its entire engineering department ever since. For seven years, I have handled features, fixes, and late-night server calls. Same client, same codebase, same person answering.

02 / Constraints

One engineer, thousands of companies, no room for lost records

  • Regulated recordkeeping. Test results, consent forms, and status changes are compliance artifacts. Losing history isn't a bug; it's a liability for the customer.
  • Five kinds of users with real power differences. A super admin and a company team member must never share an inch of accidental surface area.
  • Agents work across companies. Some users legitimately operate many fleets. Scoping mistakes here would leak one company's drivers into another's dashboard.
  • Sole maintainer. Every clever trick I add, I maintain alone, potentially years later. Cleverness has a carrying cost.

03 / Architecture

Five guards, one schema, no accidental neighbors

Laravel's auth system usually ships with one guard. the platform runs five, and the split is about session models as much as roles: company and super-admin sessions on the web guard, their staff teams on a dedicated team guard, token clients on the API guard. Custom middleware (platform-owner check, subscription check, billing exception check, account status check) layers policy on top.

actorsauth guardsmiddleware chainapplicationSuper Adminplatform ownerSuper Admin Teamstaff accountsCompany Adminfleet company ownerCompany Teamcompany staffAgentmulti-companyagents switch companies through session context: one login, many fleetsweb guardsession · company + superadminteam guardsession · team membersapi guardtoken clients1. platform-owner check2. team membership check3. subscription check4. billing exception check5. account status checkordersrecordsbillingreports~75 tables
Fig. 01: who gets in, through which guard, and past which checks. The API guard is ghosted because it serves token clients only.

Agents are the awkward case. Service providers manage testing for many fleets at once, so they authenticate once and switch companies through session-scoped context. From there, queries stay tied to the selected company instead of relying on a developer to remember where company_id = in four hundred places.

04 / State & audit

Status columns grow up into state machines

Employees move through a real lifecycle: pending, approved, active, suspended, declined, restored. Orders move through their own. Early on these were status columns; as compliance requirements hardened, they became explicit state machines with history tables recording every transition: who, when, from what, to what.

employee lifecycleorder lifecyclePendingApprovedActiveSuspendedDeclineddeclinedsuspend / restorePlacedProcessedCompletedRefundedresults uploadedrefund every transition appends to a history table: who, when, from, and to. Nothing is overwritten.
Fig. 02: the two lifecycles that matter. History tables make every transition auditable.

Retrofitting explicit states onto years of historical data was one of the platform's more difficult migrations, backfilling history for records that predated the concept of history. It's also why my answer to "when should lifecycles become state machines" is now: before the first production record exists.

05 / Billing

Stripe at three altitudes

The platform charges companies in three ways: recurring subscriptions for platform access, per-order charges for tests and records as they're consumed, and one-time fees like driver enrollment. Setup intents handle card capture; webhooks reconcile all three streams back into a coherent billing state, including the awkward cases where a subscription lapses mid-order.

I built each charge type where it was needed, and the logic spread across controllers. It works, verified by seven years of production billing. A unified billing abstraction from the start would have made every Stripe feature since cheaper. That abstraction is on the migration roadmap.

06 / The living migration

Two frontend stacks, one product, no pause button

The platform started as server-rendered Blade with Vue 2 sprinkled in. That was the right choice in 2019. It is becoming an Inertia and Vue 3 SPA now. The migration is happening one page at a time, with both stacks running in production and sharing auth, sessions, and layout.

Doing this live taught me more about migration strategy than any greenfield project could. Keep the boundary explicit. Migrate whole user journeys rather than random pages. Resist "while I'm here" rewrites. The migration is the work; improvements ride the next train.

fleet companies served
active employees managed
years maintained solo
authentication guards

07 / Results

The quiet kind of success

3,406 fleet companies. 8,859 actively managed employees. FMCSA Clearinghouse queries, MVR consent flows with e-signatures, state-specific pricing, a built-in support ticket system, shipped and maintained by one person without a major incident across seven years.

This kind of platform does not need a dramatic launch story. The useful measure is simpler: after seven years, the client still sends the next feature request to the same developer because the last three hundred went fine.

08 / Lessons

What seven years of ownership teaches

  • Solo longevity is an architecture requirement. Every decision gets judged by "will I understand this in year five?"
  • State machines should be explicit from the first record. Retrofitting history is an order of magnitude harder than recording it.
  • Scattered billing logic is technical debt with compound interest. Unify the abstraction before the third charge type, not after.
  • Long-running systems deserve deliberate documentation. Seven years of context in one head is a bus-factor problem I actively write my way out of.