React SPA to Next.js Migration
> We need to migrate a legacy React SPA to Next.js with minimal risk and a rollback path at every phase. What\u2019s the architecture?
Opus Default framed the migration as a routing problem: move routes one by one, keep a proxy in front, roll back if something breaks. That framing is reasonable and widely used.
Opus Deepthink reframed the whole thing. The ability to roll back a single route in under 30 seconds isn't a safety feature — it's what determines how fast you can move and how much risk you carry at every step. Everything else in the plan, including state isolation, only matters because it affects whether that rollback works.
Opus Deepthink also applied a pattern from database migrations: instead of replacing the old app route by route, Next.js serves all routes from day one and mounts the old app for anything not yet migrated. That keeps the user experience seamless and avoids the state boundary problems that split-app architectures create.
“Migrate routes independently”
State dependency audit + pre-mortem
Cross-route Redux dependencies make many routes non-independent — migration order constrained by state graph
“Proxy rollback is sufficient”
Pre-mortem on proxy architecture
Client-side routing bypasses proxy — must pair with client-side awareness
“State decoupling is the top concern”
SRE perspective reframing
Rollback granularity is the higher-order concern — state coupling matters because it affects rollback capability
“Replace route by route”
Expand-contract analogy
Reframed to ADDITIVE: add to Next.js, don’t remove from legacy
The safest approach is an incremental, strangler-fig migration where Next.js progressively takes over routes from the legacy SPA while both applications run in parallel behind a routing layer. This eliminates big-bang risk and gives you a rollback path at every phase.
Phase 0 — Preparation (1–2 weeks): Route inventory with traffic volume and complexity. Shared dependency audit — Redux stores, context providers, auth tokens, cookies. Build and deployment audit. Deploy a reverse proxy that routes requests based on path.
Phase 1 — Enable TypeScript, shard Playwright tests, cache dependencies and build artifacts. Convert easy routes first: low-traffic, low-complexity pages with no auth dependencies.
Phase 2 — Migrate routes in order of increasing complexity. Each route gets a canary rollout: 10% → 50% → 100% over 48–72 hours. Rollback is a one-line proxy config change.
Phase 3 — Shared Component Migration: Extract into @company/ui package consumed by both apps. Migrate React Context providers to Next.js layouts or Zustand/Jotai.
Phase 4 — Auth & Mutation Routes: Implement next-auth or custom JWT middleware. Keep legacy auth working in parallel. Single shared auth token store in Redis or cookies.
Phase 5 — Decommission: Only when 100% of traffic routes to Next.js. Archive legacy repo, keep deployment available for 2–4 weeks. Common pitfalls: don’t do a big-bang migration, don’t rush decommissioning.
Done · 22 sec
Migration order is constrained by the state graph, not by route complexity. A state audit must happen before any migration plan is finalized.
Auth needs guardrails: map all consumers, budget for CSRF edge cases, timebox to 2–3 weeks. But it must start before any route work.
Client-side routing bypasses the proxy entirely. Rollback must be paired with client-side awareness — the proxy alone is insufficient.
Reframed from replace-route-by-route to additive: both serve all routes during coexistence, with routes natively replaced one by one using SLO-gated canary ramps.
Every day both systems coexist, maintenance cost accelerates. This means migration speed matters more than migration perfection — fast and imperfect beats slow and thorough.
The Dual System Tax loop is the most dangerous finding: every day both systems coexist, maintenance cost grows superlinearly. This means migration speed matters more than migration perfection. Per-route rollback and state decoupling are the two critical leverage points.
Walking through “the migration failed at month 8” exposed six invisible dependencies: state coupling paralysis (Redux slices spanning routes), auth token split-brain (localStorage vs cookies), analytics double-counting (SPA vs MPA event models), SEO duplicate content penalties, developer velocity collapse from dual codebases, and backend rollback coupling.
Four perspectives — SRE, frontend dev, PM, end user — converged on a surprising reframing: per-route rollback capability is the higher-order concern that subsumes state isolation. The SRE perspective changed the mental model: “the rollback mechanism IS the architecture.”
The strongest analogy came from database schema migrations: expand (add new alongside old), dual-write, migrate reads, contract (drop old). Applied to frontend: Next.js serves ALL routes from day one, unmigrated routes mount the legacy SPA, routes are replaced one by one, legacy decommissioned last.
Key principle: migration must be ADDITIVE, not REPLACIVE. The Kubernetes rolling deployment analogy added automated SLO-based rollback — remove human judgment from the rollback critical path.
The PM perspective: a single visible regression can kill stakeholder confidence, making cross-app navigation UX a political risk, not just a technical one. This means the user-facing seam between the two apps must be invisible from day one.
The exploration started with “migrate route by route” and ended somewhere substantially different. Rollback capability isn’t a safety feature bolted on — it’s the meta-architecture that determines migration pace, risk tolerance, and team confidence.
13 constraints resolved · 5m 40s
Migration order is constrained by the state graph, not by route complexity. A state audit must happen before any migration plan is finalized.
Auth needs guardrails: map all consumers, budget for CSRF edge cases, timebox to 2–3 weeks. But it must start before any route work.
Client-side routing bypasses the proxy entirely. Rollback must be paired with client-side awareness — the proxy alone is insufficient.
Reframed from replace-route-by-route to additive: both serve all routes during coexistence, with routes natively replaced one by one using SLO-gated canary ramps.
Every day both systems coexist, maintenance cost accelerates. This means migration speed matters more than migration perfection — fast and imperfect beats slow and thorough.
The Dual System Tax loop is the most dangerous finding: every day both systems coexist, maintenance cost grows superlinearly. This means migration speed matters more than migration perfection. Per-route rollback and state decoupling are the two critical leverage points.
Walking through “the migration failed at month 8” exposed six invisible dependencies: state coupling paralysis (Redux slices spanning routes), auth token split-brain (localStorage vs cookies), analytics double-counting (SPA vs MPA event models), SEO duplicate content penalties, developer velocity collapse from dual codebases, and backend rollback coupling.
Four perspectives — SRE, frontend dev, PM, end user — converged on a surprising reframing: per-route rollback capability is the higher-order concern that subsumes state isolation. The SRE perspective changed the mental model: “the rollback mechanism IS the architecture.”
The strongest analogy came from database schema migrations: expand (add new alongside old), dual-write, migrate reads, contract (drop old). Applied to frontend: Next.js serves ALL routes from day one, unmigrated routes mount the legacy SPA, routes are replaced one by one, legacy decommissioned last.
Key principle: migration must be ADDITIVE, not REPLACIVE. The Kubernetes rolling deployment analogy added automated SLO-based rollback — remove human judgment from the rollback critical path.
The PM perspective: a single visible regression can kill stakeholder confidence, making cross-app navigation UX a political risk, not just a technical one. This means the user-facing seam between the two apps must be invisible from day one.
The exploration started with “migrate route by route” and ended somewhere substantially different. Rollback capability isn’t a safety feature bolted on — it’s the meta-architecture that determines migration pace, risk tolerance, and team confidence.
13 constraints resolved · 5m 40s
Unlock your AI’s deep reasoning potential
Simple installation for Claude Code, Claude Desktop, Codex, Gemini, Anti-Gravity, Cursor, and Windsurf.
Works with any client that supports MCPs.