Strangler Fig Migration skill for Codex

Replace legacy one slice at a time, reversibly. Codex inventories every legacy entry point including cron jobs and direct database readers, ships a no-op routing facade first, then moves one slice at a time behind a flag. Parity is measured with shadow traffic or parallel runs, data ownership moves one entity at a time without dual writes, and nothing legacy is deleted until traffic evidence and your approval are in hand.

  • Architecture
  • $strangler-fig-migration
  • 159 lines
  • 2 files
  • ~1,850 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- strangler-fig-migration

Installs to ~/.agents/skills/strangler-fig-migration/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $strangler-fig-migration, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Plans and executes incremental replacement of a legacy system or module with the strangler fig pattern - a routing facade (reverse proxy, API gateway or in-code seam), branch by abstraction, slice-by-slice cutover behind flags, shadow traffic and parallel-run comparison, data migration via CDC or backfill, and final decommissioning. Use when asked to rewrite a monolith or legacy service, migrate endpoints to a new stack, replace a module without a big-bang cutover, or plan how to retire old code safely.

Use it when

  • A legacy system or module must be replaced while it keeps serving users
  • Someone proposes a big-bang rewrite and you want a safer path
  • Endpoints or jobs are moving to a new stack one by one
  • Old code paths need to be retired with evidence they are unused

Use something else when

How to check the result

For each slice, parity mismatches stay under the agreed threshold, error rate and p99 match the legacy baseline at every ramp step, and a staging rollback drill succeeds.

The mistake it prevents

Shadowing write traffic or dual-writing two stores from application code, which duplicates side effects and leaves data silently inconsistent.

~/.agents/skills/strangler-fig-migration/SKILL.md

--- name: strangler-fig-migration description: Plans and executes incremental replacement of a legacy system or module with the strangler fig pattern - a routing facade (reverse proxy, API gateway… license: MIT ---

Done well means the legacy system keeps serving users while the new one takes over one slice at a time, every slice can be routed back to legacy in minutes, behaviour parity is measured rather than assumed, and the old code is actually deleted at the end. A rewrite that runs both systems forever has doubled the maintenance cost.

Before planning

  1. Read AGENTS.md, the legacy system’s entry points (routes, message consumers, cron jobs, batch scripts) and its data stores. Hidden entry points (nightly jobs, admin screens, direct database readers from other teams) are what sink migrations.
  2. Build an inventory from evidence: access logs or gateway metrics give traffic per route; rg over routes and consumers gives the code list. Record for each entry point: traffic, business criticality, data it reads and writes, known quirks.
  3. Find who else touches the legacy database directly (reporting, ETL, other services). Ask the user; query database grants or connection logs if available.
  4. Agree on the target and the reason (cost, velocity, end of support). Without a reason the migration stalls at 60%.

Choose the interception point

Legacy shapeFacadeNotes
HTTP applicationReverse proxy or API gateway (nginx, Envoy, cloud LB rules) routing by path and methodMost common; route-level slices
Library or module inside one codebaseBranch by abstraction: interface in front of old code, new implementation behind a flagWorks without network changes
Message-driven systemNew consumer on a separate subscription; route by message type or tenantWatch ordering and duplicate processing
Batch jobsRun new job in parallel writing to a shadow table, compare outputsCut over one job at a time
UIRoute by URL prefix to the new frontend; share session and navigationMicro-frontend shell only if teams are separate

Slice selection

Pick the first slice to prove the path, not to deliver the most value:

  • Read-only, moderate traffic, well-understood behaviour, owned data.
  • Then reads with complex logic, then writes, then the core write paths last.
  • Slice by route, by tenant or customer cohort, or by percentage of traffic. Tenant slicing keeps a customer’s experience consistent; percentage slicing finds performance problems early.

Workflow

- [ ] Inventory entry points, traffic, data ownership, direct DB consumers
- [ ] Put the facade in front of legacy with zero behaviour change; deploy; verify
- [ ] Build slice 1 in the new system behind a flag (default: legacy)
- [ ] Shadow or parallel-run slice 1; compare responses; fix diffs
- [ ] Ramp slice 1: internal users, 1%, 10%, 50%, 100%, watching error and latency SLOs
- [ ] Remove the legacy code path for slice 1; repeat for the next slice
- [ ] Migrate data ownership; stop legacy writes; archive and decommission

Step 1: facade with no behaviour change

Route everything to legacy through the new facade first. Ship that alone and confirm latency and error rates are unchanged. Every later step is then a routing change.

upstream legacy  { server legacy.internal:8080; }
upstream modern  { server orders-v2.internal:8080; }

map $request_method$uri $orders_backend {
    default                     legacy;
    "~^GET/api/orders/[0-9]+$"  modern;   # slice 1: read single order
}

server {
    location /api/orders/ { proxy_pass http://$orders_backend; }
    location /            { proxy_pass http://legacy; }
}

For percentage or tenant ramps, route in a gateway or in the application using the feature flag system, so rollback is a flag flip rather than a config deploy.

Step 2: branch by abstraction (in-process)

class InvoiceCalculator(Protocol):
    def total(self, order: Order) -> Money: ...

def calculator_for(order: Order) -> InvoiceCalculator:
    if flags.enabled("invoice-v2", tenant=order.tenant_id):
        return NewInvoiceCalculator()
    return LegacyInvoiceCalculator()

Step 3: prove parity

  • Shadow (dark) traffic: send a copy of read requests to the new system, discard its response, log differences. Never shadow writes unless the new side writes to an isolated store; duplicated side effects (emails, charges) are real.
  • Parallel run in-process: call both implementations, return legacy’s result, record mismatches with enough context to reproduce (GitHub’s Scientist library and its ports implement this pattern).
  • Normalise before comparing: field order, timestamps, float formatting, legacy bugs you intend to fix. Keep an explicit list of accepted differences.
  • Exit criterion: mismatch rate below an agreed threshold (for example under 0.1% over a week of traffic), every remaining mismatch explained.

Step 4: data

SituationApproach
New system can read the legacy schema for nowShare the database temporarily; new code owns no tables yet
New system needs its own storeBackfill, then keep in sync with change data capture from legacy until cutover
Writes must moveOne writer at a time per entity; flip ownership per slice; sync back to legacy via CDC while legacy readers remain

Avoid application-level dual writes (writing both stores in one request without a transaction); partial failures leave them inconsistent. If dual writes are unavoidable, add a reconciliation job that detects and repairs drift.

Step 5: decommission

Deleting legacy routes, tables or services is destructive. Before each removal, show the user evidence that traffic is zero (access logs, metrics over at least one full business cycle including month-end jobs), take a backup or snapshot, and get explicit approval. Remove in order: routing, code, scheduled jobs, then data after a retention period.

Gotchas

  • Hidden callers: other teams’ scripts hitting legacy URLs or tables directly. Log unknown clients at the facade (user agent, source IP, API key) before cutover.
  • Session and auth must work across both systems for the whole migration; plan shared sessions or token validation up front.
  • Rewriting with “improvements” breaks parity checks. Port behaviour first, fix bugs in a separate, flagged change.
  • Long-lived feature branches for the new system defeat the pattern; merge to main behind flags.
  • The last 10% (admin tools, reports, edge cases) takes as long as the first 90%. Put those slices in the plan explicitly.

Verification

  • Per slice: parity mismatch rate, error rate and p99 latency for the slice compared with legacy baseline, at each ramp step.
  • Rollback drill: flip the slice back to legacy in staging and confirm it works, including data written by the new system during the ramp.
  • At the end: zero requests to legacy for one full business cycle before removal.

Output template

## Strangler plan: <legacy> -> <target>
**Facade:** <proxy/gateway/abstraction>, deployed <date>, no behaviour change confirmed
| # | Slice | Traffic | Data | Parity method | Status | Rollback |
|---|---|---|---|---|---|---|
| 1 | GET /api/orders/{id} | 120 rps | read legacy DB | shadow, 0.04% diff | 100% modern | flag `orders-v2-read` |

**Hidden callers found:** <list>
**Data ownership plan:** <per entity>
**Decommission prerequisites:** <evidence needed, approvals>

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Strangler Fig Migration"
  short_description: "Replace legacy one slice at a time, reversibly"
  default_prompt: "$strangler-fig-migration Plan how to move our PHP monolith's order endpoints to the new Go service without a big bang"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
New

Introducing Codex Skills Pro

79 professional skills that take Codex through complete engagements, each with its own scripts, references and a report that verifies the result.

All-AccessOne-time payment, no subscription
$49
Get All-AccessOr browse individual skills from $2.99 →

Secure checkout by Creem