XAN TORRES
Back to work
FeatherSharkGovtech · Multi-tenant SaaS2023 – 2024feathershark.com

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

Whole frontend migrated, weekly releases preserved
JS → TS
domain modules shipped on the new stack
7
release gaps during the migration
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