Skip to content

app-modernization

app-modernization

Modernizes legacy applications incrementally — strangler-fig slices, expand-contract data migrations, parity-verified cutovers, and clean decommissioning. Use when migrating a monolith or legacy stack, upgrading frameworks in place, extracting services, replatforming, or when the user mentions modernization, migration, or a legacy rewrite.

Category
Category: architecture
  • legacy
  • strangler-fig
  • migration
  • replatform
  • cutover
  • parity
  • expand-contract
  • decommission
  • monolith
  • modernization

Install this skill

npx . --tool claude-code --only app-modernization

Run from a git checkout of this repo — see the README for details.

Modernization is incremental replacement behind a stable facade, not a rewrite. The legacy system encodes years of fixed bugs and business rules nobody documented; the goal is to move off it without losing either. Every slice ends in production — a modernization that lives on a branch is a science project.

When to apply

Use when migrating a legacy or monolithic application to a modern stack, upgrading a framework in place, extracting services from a monolith, replatforming (cloud, runtime, database), or whenever someone proposes "just rewrite it".

Workflow

  1. Assess — inventory modules, dependencies, data stores, and integration points. Score each on business value × change risk. Output: a ranked list of migration slices. Modernization is a portfolio decision before it is a code exercise.
  2. Pick the seams — find (or carve) module boundaries where old and new can coexist behind a facade, interface, or route table. No seam, no slice — do the modularization work first.
  3. Slice — order slices by value ÷ risk. The first slice is a tracer bullet: small, high-visibility, low blast radius. Each slice ships to production before the next starts.
  4. Choose the per-slice pattern — from the pattern table below; one pattern per slice, written down before code.
  5. Verify parity — the new path proves it behaves like the old one on real cases (fixture-driven round trips, golden-master diffs) including the weird legacy edge cases, before any traffic moves. Document deliberate deltas; don't silently "fix" behavior.
  6. Cutover — route a fraction of traffic (or one tenant/flow) to the new path, watch the signals, widen, flip. Every cutover has a rehearsed rollback — if you can't roll back, you're not cutting over, you're betting.
  7. Decommission — delete the old code path, its tables, its feature flags, its deploy config, its runbook. Done means the old thing is gone, not that the new thing works.

Migration patterns

  • Strangler fig — grow the new system around the old; route at a facade; delete old code per slice. The default choice.
  • Branch by abstraction — for in-place framework upgrades: old and new implementations behind one interface, flip callers one at a time.
  • Parallel run (shadow) — run new alongside old on live traffic and diff outputs. The cure for "we don't actually know what this code does".
  • Expand–contract — for schema and API changes: add the new shape, migrate writers/readers, contract (remove) the old. Never a breaking in-place change.
  • Dual-write → backfill → verify → read-switch — for data: write both stores, backfill history, verify counts and checksums, switch reads, stop writing old.

Rules

  • No big-bang rewrites — a rewrite re-learns every bug the legacy code already fixed and freezes feature delivery for its duration.
  • Behavior first, structure second — changing what the system does and how it's built at the same time makes every regression ambiguous. Sequence them.
  • Every cutover has a tested rollback — rehearsed, not theoretical.
  • Decommissioning is part of done — a leftover half-old system rots, confuses on-call, and doubles the maintenance bill.
  • Parity over perfection — match current behavior before improving it; improvements get their own slices.

Related skills