Skip to main content

Migrating legacy code

Legacy migrations work well when you treat them like a sequence of small, repeatable steps. Start by discovering patterns, then migrate in slices.

Identify migration patterns first

Run deterministic scans to find candidates and group them.

  - id: list_candidates
run: rg "oldApiCall" src -l

- id: group_patterns
structured_prompt:
prompt: |
Group these files by migration pattern:
{{ outputs.list_candidates }}
schema:
type: object
required: [groups]
properties:
groups:
type: array
items:
type: object
required: [pattern, files]
properties:
pattern: { type: string }
files:
type: array
items: { type: string }

Generate a migration guide artifact

Use a prompt or agent to write a guide, then save it as an artifact so the rest of the workflow can reuse it.

Guides are useful for:

  • codifying the new API or pattern
  • listing constraints and exceptions
  • defining test or verification steps

Migrate in slices

Use for_each to apply one pattern or file at a time. Keep instructions narrow and add constraints that prevent unrelated changes.

  - name: Migrate one file
for_each:
from: '{{ outputs.group_patterns.groups[0].files | json_encode() }}'
agent:
extends: Coding
instructions: "Migrate {{ for_each.value }} to the new API."
constraints:
- "Do not change other files."
- "Keep public behavior the same."

Handle long runs

  • Use continue_on_error: true on the migration step so you can collect failures.
  • Use task graphs if you want to branch to a recovery path.