Every codebase has one: the sprawling, undertested, load-bearing subsystem everyone routes around because touching it is terrifying. The classic move is to rewrite it from scratch — and that's how you burn a quarter and ship a fresh batch of regressions, because a from-scratch rewrite throws away years of hard-won edge cases nobody remembers writing. The Claude way is the opposite of heroic: scope it in plan mode, map every call site with a subagent, pin the current behaviour with tests first, then migrate incrementally behind a green suite — each step reviewed, each commit small. The subsystem is never broken for more than one reviewable diff at a time. This is the Stage-3 orchestration move: it pulls in the map (the map-the-codebase playbook), the CLAUDE.md (project-context), the green suite (debug-from-trace and backfill-tests), the safe refactor (safe-refactor), the wide migration (mechanical-migration), and small reviewed commits (git-workflow) — one motion instead of seven.
- The repo open in Claude Desktop with its
CLAUDE.md, so Claude reads your actual subsystem and follows your house rules — not a generic version of how code like this usually looks. - A clear target: what modern means here, concretely. The new library, the new pattern, the new boundary — "move the payments adapter off the deprecated SDK and behind a clean interface," not just "clean up payments."
- The subsystem's current test coverage — and an honest read on the gaps. Thin coverage isn't a blocker; it's the first thing you fix (you'll backfill before you migrate). Bring the test, type-check, and lint commands so Claude can run your real loop.
- Buy-in that this is staged, not big-bang: the team agrees the old and new paths will live side by side for a while, and that the suite stays green the whole time.
-
Scope it in plan mode before any code
Open the folder in Claude Desktop and switch to plan mode — a replatform is exactly what plan mode is for. The plan is the cheapest place in the whole project to be wrong, so spend your judgment here. Force the shape out: current state, target state, the seam to cut along, and the risks — before a single line changes. See
/features/plan-mode/.you askWe're modernizing the payments subsystem off the deprecated SDK and behind a clean interface. In plan mode: read the subsystem, describe the current state, the target state, the exact seam we'd cut along, the order of operations, and the top risks. Don't write any code — give me a staged plan I can push back on.what you get back A staged plan in prose — current vs. target, the boundary to introduce, an ordered sequence of incremental steps, and a ranked risk list — that you read hard and argue with before approving. No edits yet.
Read this like it's load-bearing, because it is. Correcting a wrong approach costs a paragraph here; correcting it after the migration starts costs days. If the plan proposes a big-bang cut-over, that's your cue to push back and stage it.
-
Map the blast radius with a subagent
A modernization fails on the call site nobody remembered. Send a subagent to do the wide read — every caller, every implicit contract, what's load-bearing and what's dead — so the deep dig doesn't crowd out your working context. The subagent reports back; you don't lose your place. See
/features/subagents/.you askUse a subagent to map the full blast radius of the payments subsystem: every call site, every place that depends on its current behaviour or data shape, any implicit contracts (error formats, side effects, ordering), and anything that looks dead. Give me the inventory grouped by risk — straightforward, needs-judgment, and weird.what you get back A grouped inventory — "41 straightforward callers, 7 that depend on the exact error shape, 3 doing something undocumented with retries" — naming real files, so you know the true size and shape of the job before committing to it.
Don't let the subagent's wide read become gospel — spot-check the surprising entries against the real files yourself. A confident map of a place it skimmed is still a map you have to verify.
-
Pin the current behaviour with tests first
You can only prove "nothing broke" against a green baseline — so before you change anything, characterize what the subsystem does today, bugs and all. If coverage is thin, this is where you fix it. Route through the backfill-tests playbook for the gaps; the suite you build here is the seatbelt for every step that follows.
you askBefore we migrate anything, write characterization tests that pin the payments subsystem's current behaviour — the happy paths, the error shapes, and the weird edge cases the map surfaced. I want a green suite that will fail loudly if behaviour changes. Flag any behaviour that looks like a latent bug, but pin it as-is for now.what you get back A green characterization suite covering the subsystem's real current behaviour, with any suspicious-looking behaviour flagged but pinned — your safety net, in place before the first migration step.
Pin behaviour as it is, not as it should be. Mixing a bugfix into the baseline means a later red test could be the migration or the fix — and you've lost the ability to tell them apart. Fix the latent bug in its own commit, after.
-
Build the new path behind a boundary, old and new side by side
Don't migrate in place. Stand up the modern implementation behind a flag or a clean interface, with the old path still running, and keep the suite green. This is the strangler-fig shape: the new thing grows alongside the old, and nothing is irreversible yet. Each step lands as a visual diff you read and accept in the file pane.
you askBuild the new payments path behind the interface from the plan, behind a feature flag, with the old path still in place and the suite still green. Don't migrate any call sites yet — I want both implementations live and tested so we can switch over incrementally. Show me the diff.what you get back A new implementation living next to the old one behind a flag/boundary, the characterization suite still green against the old path, and a reviewable diff — a reversible step, not a cut-over.
"Old path still in place" is the load-bearing phrase. As long as both run, every step is reversible and the subsystem is never broken — which is the whole point of staging over big-bang.
-
Migrate the call sites incrementally, in small reviewed commits
Now move callers onto the new path a slice at a time — the mechanical-migration move, but each slice committed and reviewed before the next. After each slice, the suite stays green. This is where a refactor of each call site stays honest: read the diff, accept it, commit it with a message that says why, then move on.
you askMigrate the straightforward call sites onto the new path in small batches. After each batch, run the suite and show me the diff for just that batch so I can review and commit it before the next. Leave the needs-judgment and weird cases for us to do together — don't force them.what you get back A sequence of small, green, individually-reviewable diffs — one batch of call sites each — that you accept and commit one at a time, with the hard cases left flagged for a human call instead of guessed.
Resist the urge to migrate everything in one giant diff. Small batches mean a red suite points at one batch, not the whole subsystem — and your
git blamestays honest for the next person. Keep the commits small and logical; bilingual teams keep the code English but write the commit messages and the ADR in the reviewer's language. -
Cut over, delete the old path, and prove it's identical
Once every call site is migrated and green, flip the flag, delete the old implementation, and prove the whole thing: suite green, types green, lint green, behaviour identical to the baseline you pinned in step three. Running the loop is the Power Track half (a terminal-enabled session); on Desktop, run it yourself and paste any red back. A human owns this cut-over — read the final diff.
you askAll call sites are on the new path and green. Remove the feature flag and delete the old payments implementation entirely. Then run the full suite, the type-checker, and the linter, and show me that behaviour matches the characterization baseline exactly. Give me the final diff and the deletion to review — I own the cut-over.what you get back The old path gone, the flag removed, and a green loop (tests + types + lint) proving the modern subsystem behaves identically to the baseline — plus a final diff you read line by line before you own the merge.
The deletion is the satisfying part, but it's also the irreversible one — read it. And capture why you made the key calls in an ADR while it's fresh; the next engineer will thank you for the reasoning, not just the diff.
- Strangler-fig at the traffic layer: instead of a code flag, route a percentage of real traffic to the new path and ramp it up as confidence grows — the new subsystem strangles the old one gradually in production, not just in the test suite. Same staged discipline, applied to rollout.
- Split it across an agent team: for a large subsystem, run the wide work in parallel — one subagent maps the blast radius, one backfills the characterization tests, one does the mechanical migration of the easy call sites — while you stay the orchestrator who reads every diff. See
/features/subagents/. - Keep an ADR of the decisions: write an Architecture Decision Record as you go — why this boundary, why this library, what you considered and rejected. For a bilingual team, the code stays English but the ADR is worth writing in the reviewer's language so the why actually gets read.
- A big-bang rewrite without the green suite is a guess at scale. The whole risk of modernization is the edge case nobody remembers — staging behind a characterization suite is how you catch it one diff at a time instead of all at once on launch day. If you can't keep it shippable the whole way through, you've chosen the risky path.
- The plan is the cheapest place to be wrong — so read it. A replatform that starts coding before the seam is agreed is a replatform that discovers the seam was wrong halfway in. Spend your judgment in plan mode, where a mistake costs a paragraph.
- Don't let the subagent's wide read become gospel. A map of a large subsystem is exactly where a confident-but-skimmed answer hides. Spot-check the surprising call sites against the real files — the map orients you, it doesn't absolve you.
- A human owns each merge and the cut-over. Every reviewed diff and the final deletion are yours to sign off on — treat the agent's output like a stream of PRs from a fast, eager junior, never merged unread. And keep proprietary code, secrets, and customer data in-boundary: on Desktop the open folder is the boundary and you approve each read.
you'll end up with The subsystem everyone feared, modernized incrementally — every step scoped in plan mode, mapped by a subagent, pinned by a characterization suite, migrated in small reviewed commits, and proven identical at cut-over — instead of a quarter-long rewrite and a regression scramble. The old path is deleted, the new one is clean, and an ADR records why. You stayed the architect; Claude was the tireless reader, tester, and migrator.
Questions people ask
- Why not just rewrite the subsystem from scratch — wouldn't that be cleaner?
- A from-scratch rewrite throws away years of accumulated edge cases nobody remembers writing, and it's broken the entire time you're building it — which is how rewrites burn a quarter and ship regressions. Staging it behind a green characterization suite gives you the clean result *and* a working system at every step: the new path grows alongside the old one, you migrate incrementally, and the subsystem is never down for more than a single reviewable diff. You get the modern code without betting the quarter on a big-bang cut-over.
- How do subagents and agent teams actually help on a modernization like this?
- The expensive failure in a replatform is the call site nobody remembered, so the wide read is the highest-leverage work. A subagent does that wide read — mapping every caller and implicit contract — in its own context, so the deep inventory doesn't crowd out the working context you need for the migration itself. On a large subsystem you can run an agent team in parallel: one maps the blast radius, one backfills the characterization tests, one migrates the easy call sites, while you stay the orchestrator who reads and approves every diff. See `/features/subagents/`.
- How do I keep the subsystem shippable the whole way through?
- Two rules. First, pin the current behaviour with a characterization suite *before* you change anything, and keep it green after every step — a red suite means you broke behaviour, full stop. Second, build the new path behind a flag or boundary with the old path still running, so old and new live side by side and every step is reversible until the final cut-over. You only delete the old implementation once every call site is migrated and the suite, types, and lint are all green.
- How long should this take, and how do I scope it?
- Anywhere from half a day for a contained subsystem to a few days for a large one — most of the time goes into the plan, the blast-radius map, and backfilling the characterization tests, not the migration itself. Scope it in plan mode first: get Claude to lay out the current state, the target, the seam to cut along, and the risks, and let the size of *that* tell you the budget. If the plan is vague, the estimate is a guess — sharpen the plan until the steps are concrete, then size them.