← Back to workCase study 03 / 03

Migrating an admin panel without a big-bang release

Moving an ALPR platform’s admin panel from Filament to React and Inertia in batches, then making the one production cutover a planned, reversible day.

  • 22

    admin resources migrated to React / Inertia

  • 4

    batches, ordered by complexity

  • 636

    tests in the CI gate in front of every deploy

  • 18

    database migrations planned into one cutover

01 —Context

Spyglass Portal is the web side of an automatic licence plate recognition platform. Operators manage cameras, lists, zones, integrations and users in it, and handsets in the field sync lists and upload reads through its API. Part of the portal already ran on React and Inertia, but the admin resources still lived in a Filament panel: two navigation systems, two themes and two ways of building a screen.

02 —Problem

Rewriting the panel in one go would have meant months without a usable middle state. And the deploy itself was fragile: the production script pulled code, built and migrated in one pass with no rollback step, so a failure halfway left new code on a half-migrated database.

03 —What I did

  • Ran both panels side by side. The Inertia sidebar mirrored the Filament navigation and the theme was shared between them through a cookie, so screens could move one at a time without users losing their way.
  • Migrated in four batches ordered by complexity: simple integrations and settings first, then API keys, organisations and licences, then presets, cameras and list integrations, and last zones, lists and camera presets. The activity log and role management, skipped at first, followed: 22 resources in total. Then I closed the gaps (CRUD, stats, imports) and removed Filament and its permissions plugin.
  • Put a CI gate in front of every deploy. Pull requests ran no tests, build or type check, and deploys checked code style alone. One verification workflow now runs on every pull request and inside both deploy workflows: code style, dependency audits, the test suite (636 tests), type checks and the build. The audits turn “no open advisories” from a snapshot into an invariant.
  • Wrote the cutover plan as a document in the repository, reviewed like code. Production was 18 migrations behind, six of them on the largest table. I rehearsed it on a restored copy of production, timing each migration (a dry-run plan is not a duration) and the restore itself, since the restore time decides the abort point. The database snapshot is the rollback, because nothing else can undo a forward-only migration.
  • Planned the parts a deploy does not do: maintenance mode before the merge, the permission seeder that removes grants as well as adding them, restarting the long-running workers so they drop cached permissions, and telling the mobile client owners about API payload changes beforehand.
  • Ended the plan with a checklist of things a person does, not things the deploy claims: sign in as a customer administrator, sync lists and upload a read from a handset.

04 —Result

All 22 resources run on React and Inertia, and Filament is gone from the codebase. Production moved to the new portal in September 2026 through that plan. Every deploy now passes the same gate as a pull request, and the known security advisories were closed.

05 —What I’d do differently

I would put the CI gate in first. Every batch was migrated while no tests ran in CI, and the gate came months later.

I would also promote to production at batch boundaries. The panel moved in small steps, but production stayed on an older build, so the final cutover carried months of change in one window. The plan made that day safe; releasing more often would have made it small.