Migrated a live govtech SaaS while fire departments kept using it.
FeatherShark was a two-person product used by US municipal fire departments and building-inspection offices. I owned the frontend while moving the app from CRA/JavaScript to Vite/TypeScript, replacing ad-hoc fetching with RTK Query, and shipping new modules for maps, tenant config, 2FA, guided inspection drafting, and contractor workflows.
Multi-tenant
Maps
State machines
Inspection drafting
What changed
Moved one layer at a time: runtime, language, data fetching, forms, maps, auth, and tenant configuration.
Measured outcomes
- JS → TS
- 7
- 0
Highlights
- Moved the whole frontend off JavaScript on CRA onto TypeScript on Vite in roughly three months without blocking a release. Leaned on the allowJs flag to convert module by module, starting with forms and data hooks where types kept catching real bugs.
- Replaced hand-rolled fetch hooks with RTK Query. Tag-based cache invalidation removed a long tail of stale-data bugs and loading states that disagreed with each other.
- Made Zod schemas the shared contract for forms and API edges. The same schema drove validation, component state, and DTO typing through react-hook-form.
- Built autocomplete across server records, ArcGIS suggestions, and tenant-defined locations. Each source could fail without killing the typeahead.
- Shipped ArcGIS occupancy maps with drag-and-drop safety markers, persistent zoom and pan, and staged marker updates that stopped flicker during edits.
- Modeled 2FA as an explicit state machine: credentials submitted, awaiting code, hydrating session, recovery code, and refresh mid-flow all had named transitions.
- Moved tenant config from build-time .env to a server-driven settings endpoint for theme tokens, default map coordinates, and admin UI shape. New municipalities no longer required a redeploy.
- Added guided inspection text with backoff, deterministic fallback, and human review before save. Drafted language stayed editable and never blocked the inspection flow.
- Built the checklist-template admin: drag-and-drop sections and items, fee-type fields, and cascading deletes for the authoring surface the product depends on.
- Built the contractor portal as its own routing tree with role-scoped permit lists, activities, invoices, and document uploads.
Outcomes
- For 20 months I was the sole frontend engineer on a two-engineer team. Six migrations shipped without creating a deploy gap.
- Frontend stopped being the scary part of the roadmap. Feature work and migration work moved together instead of competing.
- New municipalities could be configured by settings instead of redeploying.
- The platform was live with US municipal fire departments and building-inspection offices before my engagement ended.
Stack
- React 18
- TypeScript
- Vite
- Redux Toolkit
- RTK Query
- react-hook-form
- Zod
- ArcGIS Maps SDK
- MUI
- MUI DataGrid Pro
- Mobiscroll
- Model API
- Biome
- Yarn 4 PnP