guides

Brownfield upgrade

Migrating an existing system without breaking it. Pipemason's brownfield-upgrade mode adds two pre-flight phases on top of the standard program machine: audit and migration_plan.

When this mode fits

  • Auth swaps (Auth0 → Clerk, Cognito → Auth0, etc).
  • Framework upgrades that cross multiple PRs (React 18 → 19, Next 14 → 15).
  • Schema redesigns with data migrations.
  • Monolith decomposition / extracting a service.
  • Anything where the order of PRs matters for safety.

For smaller adds that don't need ordered migrations (e.g. add SSO to existing auth), use brownfield-extension instead.

The two extra phases

audit

The audit phase reads the current system and freezes AUDIT.md: a snapshot of architecture, dependencies, tests, CI, deploy surface, and known issues. Every downstream phase reads AUDIT.md as the source of truth for "what we have today".

Note

Read the audit before kicking off the program. If the audit missed something material, the migration plan will too.

migration_plan

The migration plan phase writes MIGRATIONS.md — an ordered list of migration steps with rollback procedures. Each step becomes one or more stories that the iterate phase dispatches in order. A migration step usually has:

  • A title + scope.
  • A forward procedure (what the PR does).
  • A rollback procedure (how to undo it without losing data).
  • A gate the verify phase checks before allowing the next step (often: "both old and new paths work in parallel for N runs").

Walkthrough: auth swap

pipemason program start "migrate auth from Auth0 to Clerk, preserve sessions, deprecate Auth0 after 30 days" \
  --mode brownfield-upgrade --dry-run

Review AUDIT.md + MIGRATIONS.md. A typical auth-swap migration plan looks like:

migrations:
  - id: M-001
    title: "Add Clerk alongside Auth0 (dual-write)"
    forward: "Provision Clerk app; install SDK; wrap every protected route in <SignedIn> matching the Auth0 guard."
    rollback: "Remove Clerk SDK + provider; routes fall back to Auth0-only."
    gate: "Both auth surfaces accept tokens for 7 calendar days"

  - id: M-002
    title: "Migrate users (bulk Auth0 → Clerk import)"
    forward: "Run import script in Clerk dashboard with backfilled metadata."
    rollback: "Delete imported users; Auth0 remains authoritative."
    gate: "100% of active Auth0 users have a Clerk shadow account"

  - id: M-003
    title: "Switch primary auth to Clerk"
    forward: "Flip feature flag; new sessions issued by Clerk; old Auth0 sessions still accepted."
    rollback: "Flip flag back; Auth0 resumes issuance."
    gate: "0 errors on /api/me for 24 hours under Clerk-primary"

  - id: M-004
    title: "Decommission Auth0"
    forward: "Remove Auth0 SDK + provider + env vars. Cancel Auth0 plan."
    rollback: "Hard — restore from backup tag; reprovision Auth0 SaaS."
    gate: "30 days under Clerk-primary; no rollback fired"

Iterate

When you remove --dry-run the iterate phase dispatches one migration step at a time. Each step is a story / PR. The verify gate holds the next step until the previous step's gate passes.

Brownfield upgrade · Pipemason Docs