editkit / docs

Framework migration with unified diffs

Multi-hunk changes per file — Next 13 → 15, React class → hooks, Express 4 → 5. Unified diff stops the model from emitting placeholders.

For migrations that touch many places in one file (API surface changes, breaking config changes, idiom shifts), unified diff outperforms SEARCH/REPLACE. The diff structure stops the model from emitting // ...rest unchanged placeholders that lose context.

const page = await readFile("app/products/[id]/page.tsx", "utf8");
const { text } = await generateText({
  model: openai("gpt-4o"),
  system: UNIFIED_DIFF_PROMPT,
  prompt: `Migrate this Next 13 page to Next 15: async params, new metadata API, new caching defaults.\n\n\`\`\`tsx\n${page}\n\`\`\``,
});
const [r] = await applyEdits(
  text,
  { "app/products/[id]/page.tsx": page },
  { formats: ["unified-diff"] },
);
if (r?.ok) await writeFile(r.path, r.after);

Why unified diff wins here

SEARCH/REPLACE works one block at a time. For a migration with 8 changes per file, that's 8 separate SEARCH blocks — and the model often loses track of context between them. A unified diff is one continuous structure: 8 hunks in one block, with explicit line numbers and context lines anchoring each one.

When to use SEARCH/REPLACE instead

If the migration is mostly replacements (rename API X to Y), SEARCH/REPLACE is fine and cheaper. Unified diff shines when you need additions and removals interleaved with unchanged context.

On this page