[{"data":1,"prerenderedAt":670},["ShallowReactive",2],{"blog-coding-agents-legacy-codebase-migration-en":3},{"slug":4,"published":5,"minutes":6,"category":7,"tags":8,"keywords":14,"about":27,"sources":37,"cover":73,"og":74,"expertise":75,"locales":76,"lang":77,"title":80,"description":81,"coverAlt":82,"metaTitle":83,"takeaways":84,"faq":90,"toc":109,"blocks":146,"others":426},"coding-agents-legacy-codebase-migration","2026-10-17",11,"agents",[9,10,11,12,13],"Legacy migration","Coding agents","Strangler fig","Testing","Code review",[15,16,17,18,19,20,21,22,23,24,25,26],"legacy code migration ai","coding agent legacy codebase","strangler fig pattern ai","characterization tests legacy code","ai code migration review","agent assisted refactoring","legacy modernization coding agent","differential testing migration","shadow traffic migration","ai code review gate","test harness before migration","llm code migration",[28,31,34],{"name":29,"url":30},"Legacy code","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FLegacy_code",{"name":32,"url":33},"Characterization test","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCharacterization_test",{"name":35,"url":36},"Strangler fig pattern","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FStrangler_fig_pattern",[38,41,43,46,49,52,55,58,61,64,67,70],{"title":39,"url":40},"Martin Fowler: Strangler Fig (2024)","https:\u002F\u002Fmartinfowler.com\u002Fbliki\u002FStranglerFigApplication.html",{"title":42,"url":33},"Wikipedia: Characterization test",{"title":44,"url":45},"Google Research: Accelerating code migrations with AI (2024)","https:\u002F\u002Fresearch.google\u002Fblog\u002Faccelerating-code-migrations-with-ai\u002F",{"title":47,"url":48},"Ziftci et al.: Migrating Code At Scale With LLMs At Google (arXiv 2504.09691)","https:\u002F\u002Farxiv.org\u002Fabs\u002F2504.09691",{"title":50,"url":51},"Anthropic: Effective harnesses for long-running agents (2025)","https:\u002F\u002Fwww.anthropic.com\u002Fengineering\u002Feffective-harnesses-for-long-running-agents",{"title":53,"url":54},"Anthropic: Redesigning source control at Anthropic (2026)","https:\u002F\u002Fwww.anthropic.com\u002Fengineering\u002Fredesigning-source-control",{"title":56,"url":57},"METR: Measuring the Impact of Early-2025 AI on Experienced Open-Source Developer Productivity","https:\u002F\u002Fmetr.org\u002Fblog\u002F2025-07-10-early-2025-ai-experienced-os-dev-study\u002F",{"title":59,"url":60},"Chand et al.: Why Industry Migrates and When It Trusts LLMs (ESEM 2026)","https:\u002F\u002Fdrops.dagstuhl.de\u002Fentities\u002Fdocument\u002F10.4230\u002FLIPIcs.ESEM.2026.85",{"title":62,"url":63},"Li et al.: Environment-in-the-Loop: Rethinking Code Migration with LLM-based Agents (arXiv 2602.09944)","https:\u002F\u002Farxiv.org\u002Fabs\u002F2602.09944",{"title":65,"url":66},"AWS: Amazon Q Developer just reached a $260 million dollar milestone (2024)","https:\u002F\u002Faws.amazon.com\u002Fblogs\u002Fdevops\u002Famazon-q-developer-just-reached-a-260-million-dollar-milestone\u002F",{"title":68,"url":69},"OpenAI: Why SWE-bench Verified no longer measures frontier coding capabilities (2026)","https:\u002F\u002Fopenai.com\u002Findex\u002Fwhy-we-no-longer-evaluate-swe-bench-verified\u002F",{"title":71,"url":72},"GitHub Docs: About GitHub Copilot cloud agent","https:\u002F\u002Fdocs.github.com\u002Fen\u002Fcopilot\u002Fconcepts\u002Fagents\u002Fcoding-agent\u002Fabout-coding-agent","\u002Fimages\u002Fblog\u002Fcoding-agents-legacy-codebase-migration\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fcoding-agents-legacy-codebase-migration\u002Fog.jpg","ai-engineer",[77,78,79],"en","de","hu","Migrating a legacy codebase with coding agents: a strangler fig playbook","How to use coding agents on a legacy migration: characterization tests first, one seam per task, review gates and differential verification.","A stack of slabs with the top two replaced by fresh ones beside a seam and a check.","Legacy migration with coding agents · Balázs Csorba",[85,86,87,88,89],"Agents collapse the repetitive middle of a migration, not the work of discovering what the old system does.","Build a runnable environment and characterization tests before the first agent task; without an oracle you are generating plausible code.","Scope each task to one seam or file group; practitioners accept single-file agent migration far more than whole-project migration.","Gate review with a diff budget, a no-test-edits rule and explicit evidence; an agent never approves its own migration.","Verify with replay, shadow traffic and invariants, and delete the old path only after parity holds.",[91,94,97,100,103,106],{"q":92,"a":93},"Can a coding agent migrate a whole legacy codebase on its own?","Not reliably. In a 2026 survey of 61 practitioners, only 4.9% considered LLM assistance acceptable for entire-project migration most or all of the time, against 62.3% for single-file migration. Use agents slice by slice, with a harness and a reviewer.",{"q":95,"a":96},"What should I build before the first agent task?","A runnable environment and an oracle: build in CI, start with real dependencies, capture representative requests and responses, and add characterization tests around the first seam. Without that, the agent generates plausible code with no way to tell whether behavior changed.",{"q":98,"a":99},"How large should one agent task be?","One seam or file group, one pull request, one deployable outcome. GitHub's cloud agent works on one branch and one pull request per task with a 59-minute session limit; that is a product constraint, not a target, but it is a useful reminder that long tasks are where scope creeps.",{"q":101,"a":102},"How do I prove that behavior did not change?","Replay recorded inputs against old and new code and diff normalized outputs, run shadow traffic before raising the percentage, and assert invariants that must hold regardless of implementation. Green unit tests are necessary but not sufficient.",{"q":104,"a":105},"Are coding benchmarks a good estimate for migration work?","No. OpenAI stopped reporting SWE-bench Verified in February 2026 after finding flawed tests in a large share of audited cases and evidence of training-data contamination. Benchmark scores describe small, well-specified tasks; your migration is a context problem.",{"q":107,"a":108},"When is a rewrite the better choice?","When the old system has little behavior worth preserving, when the boundary is small enough to replace in one step, or when the harness would cost more than the rewrite. That is a judgement about risk and value, not about how fast an agent can type.",[110,113,116,119,122,125,128,131,134,137,140,143],{"id":111,"title":112},"the-real-bottleneck","The bottleneck moved, but it did not disappear",{"id":114,"title":115},"why-rewrites-fail","Why the big-bang rewrite still fails",{"id":117,"title":118},"harness-first","Build the harness before the first agent task",{"id":120,"title":121},"scope-one-seam","Scope every task to one seam",{"id":123,"title":124},"strangler-loop","The loop: route, extract, verify, retire",{"id":126,"title":127},"what-agents-do-well","What agents are genuinely good at",{"id":129,"title":130},"what-agents-get-wrong","What agents get wrong on legacy code",{"id":132,"title":133},"review-gates","Review gates that catch migration bugs",{"id":135,"title":136},"verification","Verification beyond green tests",{"id":138,"title":139},"when-not-to","When not to use an agent",{"id":141,"title":142},"playbook","A migration playbook in nine steps",{"id":144,"title":145},"sources","Sources",[147,151,154,157,160,163,166,167,170,173,176,177,180,183,186,228,237,244,245,248,251,254,257,260,261,270,273,276,279,280,283,286,289,290,293,296,299,302,305,306,309,324,332,333,336,339,342,345,347,350,351,354,357,364,365,386,387],{"type":148,"content":149},"paragraph",[150],"Legacy migrations are the least glamorous work in software: a framework upgrade that touches 4,000 files, a language port nobody wants to own, an API change postponed for three release cycles. In 2025 and 2026, coding agents became genuinely useful for exactly this kind of work. Google reported that across 39 migrations in twelve months, 74.45% of submitted code changes and 69.46% of edits were generated by its LLM tooling, with engineers estimating a 50% reduction in total migration time. Amazon says it moved tens of thousands of production Java applications to Java 17 with its code transformation agent, saving more than 4,500 developer-years.",{"type":148,"content":152},[153],"The catch is that a migration is not a typing problem. An agent can produce a plausible diff for code it does not understand, and legacy code is precisely the code nobody understands completely. What makes agent-assisted migration work is not a better prompt; it is the scaffolding around the agent: a harness that can tell whether behavior changed, seams that keep each change small, and review gates that catch what the tests cannot. That is the process I would use, and where I would not use an agent at all.",{"type":155,"level":156,"id":111,"text":112},"heading",2,{"type":148,"content":158},[159],"Google's numbers are often quoted as proof that migrations are solved. Read the paper and the picture is more specific. The tooling pointed at locations found by static analysis and engineers, expanded the file set through symbol references, generated diffs per file group, compiled and ran unit tests, and used a repair loop when those failed. The model did the editing. Engineers still chose the migration, found the files, reviewed the changes and rolled them out. The reported 50% is a reduction in total time, not a reduction in the work of understanding the system.",{"type":148,"content":161},[162],"Amazon's Java upgrade story has the same shape. The agent automates dependency migration and the build-and-test loop for an application whose boundaries are already known: a Java application moving from Java 8 or 11 to 17. That is a large, repetitive, well-specified transformation. It is no business-process rewrite.",{"type":148,"content":164},[165],"So the useful claim is narrow: agents collapse the cost of the repetitive middle of a migration. They do not collapse the cost of discovering what the old system does, deciding what the new one should do, or proving that the two match. Design the process around those three jobs.",{"type":155,"level":156,"id":114,"text":115},{"type":148,"content":168},[169],"Martin Fowler's strangler fig pattern is two decades old and still the right default. His description of the failure mode is blunt: the simple replacement plan \"go[es] down in flames most of the time\", because replacing a serious system takes a long time, users cannot wait for new features, and the details of existing behavior are hard to specify. Some of that behavior is not even wanted, so rebuilding it is waste.",{"type":148,"content":171},[172],"Coding agents make the replacement look cheaper than it is. A demo that reimplements a screen or an endpoint takes an afternoon; the remaining 80% is the part where invoices round differently on leap days, a report depends on an archived table, and a background job writes a field that three other services read. None of that is in the demo.",{"type":148,"content":174},[175],"The strangler fig answer is to grow the new system around the old one. Put a seam in front of a piece of behavior, move that behavior into new code, route traffic gradually, and keep the old path until the new one has earned trust. Fowler calls the glue \"transitional architecture\" and notes that it looks like waste; it is the price of not betting the business on a cutover weekend.",{"type":155,"level":156,"id":117,"text":118},{"type":148,"content":178},[179],"Michael Feathers' characterization test is the tool that makes legacy work possible: a test that describes the actual behavior of the existing code, not the behavior the documentation claims. It is a change detector. It does not tell you the old behavior is correct; it tells you when it changed. Golden-master tests are the same idea at the level of a whole response: record the output for a set of inputs, normalize the volatile parts, and compare.",{"type":148,"content":181},[182],"The first agent task on a migration should not be a code change. It should be making the system runnable and observable: build it in CI, start it with its real dependencies, capture representative requests, and record what the old system returns. A 2026 paper on environment-in-the-loop migration argues that code and environment move together; static analysis of the target is not enough, and a slow loop causes rework. An agent that cannot run the old system is guessing.",{"type":148,"content":184},[185],"Then build the oracle in layers. The table below is the order I would use; each layer costs less than the incident it prevents.",{"type":187,"head":188,"rows":192},"table",[189,190,191],[77],[78],[79],[193,200,207,214,221],[194,196,198],[195],"Characterization test \u002F golden master",[197],"Unintended output changes in one unit or endpoint",[199],"Before touching that unit",[201,203,205],[202],"Contract test at the seam",[204],"Shape and semantics of the interface between old and new",[206],"Before routing any traffic",[208,210,212],[209],"Replay of recorded requests",[211],"Real payloads, edge cases, encoding and null handling",[213],"Once you can capture production traffic",[215,217,219],[216],"Shadow traffic diff",[218],"Divergence under live volume and ordering",[220],"Before raising traffic above a canary",[222,224,226],[223],"Invariant checks",[225],"Properties that must hold whatever the implementation",[227],"Before retiring the old path",{"type":148,"content":229},[230,231,236],"None of this is new: it is the same harness argument as in",{"tag":232,"to":233,"children":234},"link","\u002Fblog\u002Fharness-engineering-coding-agents",[235],"harness engineering",", applied to a codebase never designed for it. But an agent will happily generate a thousand lines against a weak oracle, so the harness stops being a nice-to-have and becomes what keeps the migration honest.",{"type":238,"variant":239,"title":240,"body":241},"callout","warn","A green suite is not a specification",[242],[243],"Characterization tests record what the old system does, including its bugs. Before you freeze that behavior, decide which parts are contract and which are accidents. An agent will faithfully preserve both, and a reviewer who only sees a passing suite will not notice the difference.",{"type":155,"level":156,"id":120,"text":121},{"type":148,"content":246},[247],"The strongest evidence for small scope comes from a 2026 practitioner survey: of 61 industry respondents, 62.3% considered LLM assistance acceptable most of the time or almost always for single-file migration, but only 4.9% said the same for entire-project migration. Trust depended on correctness, context, security, verification evidence and provenance. Practitioners are not refusing agents; they are bounding them.",{"type":148,"content":249},[250]," GitHub's Copilot cloud agent works on one branch at a time, opens one pull request per task, and has a 59-minute session limit. Anthropic's long-running harness gives the agent a feature list, asks it to work on one feature at a time, and requires it to leave a clean state with a commit and a progress note. The common rule: one task, one verifiable outcome, one reviewable diff.",{"type":148,"content":252},[253],"A task card is worth more than a long prompt:",{"type":255,"code":256},"code","# migrate-invoice-total\nscope: src\u002Fbilling\u002Finvoice-total.ts + its tests only\ngoal: replace the legacy rounding helper with the new money library\ninvariants:\n  - totals for existing fixtures are byte-identical\n  - currency is never coerced to float\n  - the public function signature does not change\nout of scope: invoice PDF rendering, tax rules, database schema\ndone when: golden tests pass, contract test passes, no new lint warnings\nevidence: test output, diff summary, list of intentionally changed fixtures",{"type":148,"content":258},[259],"Two lines do the most work: the file list and the out-of-scope list. They give the agent a boundary and give the reviewer a way to reject a diff that wandered. Anthropic's harness goes further and tells the agent not to edit the feature list or the tests; if a test is wrong, a human changes it in a separate commit. That one rule prevents the most common failure I have seen in agent migrations: the agent adjusts the oracle until it agrees with the new code.",{"type":155,"level":156,"id":123,"text":124},{"type":262,"attrs":263,"inner":267,"caption":268},"diagram",{"viewBox":264,"role":265,"aria-labelledby":266},"0 0 720 300","img","clm1-t clm1-d","\u003Ctitle id=\"clm1-t\">Strangler fig routing\u003C\u002Ftitle>\u003Cdesc id=\"clm1-d\">A legacy monolith on the left, a router in the middle sending an increasing share of traffic to one extracted slice on the right, and a comparison harness underneath that records old and new outputs.\u003C\u002Fdesc>\u003Cdefs>\u003Cmarker id=\"clm1-ah\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\">\u003Cpath d=\"M0 0L10 5L0 10z\" class=\"d-head\" \u002F>\u003C\u002Fmarker>\u003C\u002Fdefs>\u003Crect x=\"20\" y=\"60\" width=\"230\" height=\"200\" rx=\"12\" class=\"d-box\" \u002F>\u003Ctext x=\"135\" y=\"90\" text-anchor=\"middle\" class=\"d-title\">Legacy system\u003C\u002Ftext>\u003Crect x=\"38\" y=\"106\" width=\"92\" height=\"40\" rx=\"6\" class=\"d-fill-muted\" \u002F>\u003Ctext x=\"84\" y=\"131\" text-anchor=\"middle\" class=\"d-small\">billing\u003C\u002Ftext>\u003Crect x=\"140\" y=\"106\" width=\"92\" height=\"40\" rx=\"6\" class=\"d-fill-muted\" \u002F>\u003Ctext x=\"186\" y=\"131\" text-anchor=\"middle\" class=\"d-small\">reports\u003C\u002Ftext>\u003Crect x=\"38\" y=\"158\" width=\"194\" height=\"40\" rx=\"6\" class=\"d-fill-muted\" \u002F>\u003Ctext x=\"135\" y=\"183\" text-anchor=\"middle\" class=\"d-small\">undocumented rules\u003C\u002Ftext>\u003Ctext x=\"135\" y=\"222\" text-anchor=\"middle\" class=\"d-small\">still serving\u003C\u002Ftext>\u003Ctext x=\"135\" y=\"240\" text-anchor=\"middle\" class=\"d-small\">most traffic\u003C\u002Ftext>\u003Ctext x=\"360\" y=\"26\" text-anchor=\"middle\" class=\"d-label\">Requests\u003C\u002Ftext>\u003Cpath d=\"M360 36V58\" class=\"d-line\" marker-end=\"url(#clm1-ah)\" \u002F>\u003Crect x=\"300\" y=\"60\" width=\"120\" height=\"64\" rx=\"10\" class=\"d-gold\" \u002F>\u003Ctext x=\"360\" y=\"88\" text-anchor=\"middle\" class=\"d-text\">Router\u003C\u002Ftext>\u003Ctext x=\"360\" y=\"108\" text-anchor=\"middle\" class=\"d-small\">10% to 100%\u003C\u002Ftext>\u003Crect x=\"470\" y=\"60\" width=\"230\" height=\"110\" rx=\"12\" class=\"d-mint\" \u002F>\u003Ctext x=\"585\" y=\"93\" text-anchor=\"middle\" class=\"d-text\">Extracted slice\u003C\u002Ftext>\u003Ctext x=\"585\" y=\"116\" text-anchor=\"middle\" class=\"d-small\">new code, old contract\u003C\u002Ftext>\u003Ctext x=\"585\" y=\"138\" text-anchor=\"middle\" class=\"d-small\">one seam per PR\u003C\u002Ftext>\u003Cpath d=\"M250 92H298\" class=\"d-line\" marker-end=\"url(#clm1-ah)\" \u002F>\u003Cpath d=\"M420 92H468\" class=\"d-line\" marker-end=\"url(#clm1-ah)\" \u002F>\u003Crect x=\"250\" y=\"240\" width=\"340\" height=\"48\" rx=\"10\" class=\"d-sky\" \u002F>\u003Ctext x=\"420\" y=\"268\" text-anchor=\"middle\" class=\"d-small\">Golden tests + output diff\u003C\u002Ftext>\u003Cpath d=\"M585 170V238\" class=\"d-dash\" marker-end=\"url(#clm1-ah)\" \u002F>\u003Cpath d=\"M250 264H135V262\" class=\"d-dash\" marker-end=\"url(#clm1-ah)\" \u002F>",[269],"The old system keeps serving while one slice moves. The dashed path is the comparison harness, not a second production route.",{"type":148,"content":271},[272],"The mechanics fit on one page. Put a router or facade in front of the legacy path. Pick a vertical slice with a clear contract. Extract it behind the same interface, so callers do not change. Route a small share of traffic to the new implementation and compare results. Raise the share as confidence grows. When the new path has carried the slice through a full business cycle, delete the old code.",{"type":148,"content":274},[275],"The comparison is the important step, and it has to compare more than status codes. Log the old and new outputs for the same input, normalize timestamps and identifiers, and diff the rest. If they differ, decide whether that is a bug in the new code or an old bug you are fixing on purpose. Write that decision down; it becomes the migration's specification.",{"type":148,"content":277},[278],"Fowler's warning about transitional architecture applies: the router and the dual-write code are temporary, and they need an owner and a removal date. An agent is good at producing that glue; it is also good at producing more of it than you need. Keep the glue boring and delete it on schedule.",{"type":155,"level":156,"id":126,"text":127},{"type":148,"content":281},[282],"Agents are strongest where the work is wide, repetitive and locally checkable. Renaming an API across hundreds of call sites, updating imports and type annotations, porting a test suite, adding the same error handling to twenty handlers, or reading an unfamiliar module and summarising its contracts: these are tasks with a fast oracle and a small blast radius per edit.",{"type":148,"content":284},[285],"Google's workflow is the clearest example: generate diffs per file group, compile, run tests, and feed failures back for repair. The agent does not need to be right on the first attempt if the loop can tell it that it is wrong. The same principle explains a striking internal result at Anthropic, where a rewritten ingestion pipeline consumed 20x less memory and ran 4x faster while preserving the implicit invariants around ordering and data structures. It is a vendor's account, not a controlled study, but the mechanism is plausible: a tight correctness loop plus a clear invariant is exactly where agents shine.",{"type":148,"content":287},[288],"The practical test: can you give the agent a command that fails when it is wrong? If yes, delegate aggressively. If the only feedback is a human reading the diff, keep the task small.",{"type":155,"level":156,"id":129,"text":130},{"type":148,"content":291},[292],"Three failure modes matter more than benchmarks.",{"type":148,"content":294},[295],"First, the agent does not know which behavior is load-bearing. METR's 2025 randomized trial found experienced open-source developers were 19% slower with AI tools on their own mature repositories, despite expecting a 24% speedup and believing afterwards that they had been faster. The tasks had implicit requirements: style, tests, documentation, and conventions that were never written down. Legacy migrations are made of exactly those requirements.",{"type":148,"content":297},[298],"Second, agents optimise the local pattern and violate the system constraint. Anthropic's account of its source-control migration is unusually candid: agents sometimes tried to satisfy a pattern in a way that broke a core invariant, and engineers had to tell them no. Adversarial review could make this worse, producing over-engineered defences against hypothetical problems. The team still reviewed every change request, but reviewed intent rather than every line.",{"type":148,"content":300},[301],"Third, the benchmark is not your codebase. In February 2026 OpenAI stopped reporting SWE-bench Verified because an audit found that at least 59.4% of a 27.6% sample had flawed tests, and because models could reproduce gold patches from training data. A high score means the model has seen many small, well-specified Python issues. It does not mean it knows what your invoice rounding rule was in 2013.",{"type":148,"content":303},[304],"Legacy-specific hazards: dead code that is still scheduled, implicit contracts between services, data migrations that are not reversible, concurrency and ordering assumptions, and performance that depends on a specific query plan. An agent can change all of these confidently. Only a harness and a reviewer can tell whether the change was safe.",{"type":155,"level":156,"id":132,"text":133},{"type":148,"content":307},[308],"Review is the throughput limit of agent-assisted migration, and the wrong review process turns a speedup into a queue. The gates I use:",{"type":310,"items":311},"list",[312,314,316,318,320,322],[313],"One seam per pull request, with a diff budget. If the diff exceeds the budget, split the task, not the review.",[315],"No test edits in the migration commit. Test changes go in a separate commit with a written reason.",[317],"Two keys for anything that touches money, identity or deletion. An agent may propose; a human approves.",[319],"Evidence in the pull request: the command that ran, the golden diff, the shadow comparison, the list of intentionally changed behavior.",[321],"Review intent, not formatting. Ask what the change is supposed to preserve, and whether the diff proves it.",[323],"A rollback path that is exercised before the traffic shift, not after the incident.",{"type":148,"content":325},[326,327,331],"GitHub's own documentation is explicit that agent session logs do not replace your review and testing. The review bottleneck is real, and it gets worse when agents produce more diffs than reviewers can absorb; the fix is smaller batches and better evidence, not faster rubber-stamping. I wrote about the queue problem separately in",{"tag":232,"to":328,"children":329},"\u002Fblog\u002Fai-generated-pr-review-bottleneck",[330],"the review bottleneck",".",{"type":155,"level":156,"id":135,"text":136},{"type":148,"content":334},[335],"A green suite proves that the tests you have still pass. It does not prove that the new path behaves like the old one on inputs you did not think of. Three techniques close the gap.",{"type":148,"content":337},[338],"Replay: capture real requests and their old responses, then run them against the new implementation and diff the normalized output. This is the cheapest high-value check, and it catches encoding, null handling and rounding differences that unit tests miss.",{"type":148,"content":340},[341],"Shadow: send a copy of live traffic to the new path without serving its response. Compare asynchronously. This catches ordering, concurrency and volume effects that replay cannot.",{"type":148,"content":343},[344],"Invariants: assert the properties that must hold no matter what, such as \"the sum of line items equals the total\" or \"no invoice is emitted twice\". Invariants survive implementation changes; golden files do not.",{"type":255,"code":346},"const cases = await loadRecordedRequests(\"invoices\u002F2026-q2\");\nfor (const c of cases) {\n  const before = normalize(await legacyTotal(c));\n  const after = normalize(await nextTotal(c));\n  expect(after, `case ${c.id}`).toEqual(before);\n}",{"type":148,"content":348},[349],"Normalize aggressively: timestamps, ids, ordering, whitespace, locale formatting. Every field you normalize is a field you have decided not to verify, so keep the list short and review it. When a diff is intentional, record it in the migration's behavior changelog and add a test that asserts the new behavior.",{"type":155,"level":156,"id":138,"text":139},{"type":148,"content":352},[353],"Agents are a bad fit when the oracle is missing and cannot be built cheaply. A 15-year-old system with no tests, no traffic capture, no staging environment and a frozen regulatory boundary is not a migration; it is an archaeology project, and the first deliverable is the harness, not the diff. They are also a poor fit for a one-off 200-line change, where scoping and review cost more than the work.",{"type":148,"content":355},[356],"They are a poor fit when the real task is a decision: which system owns the data, whether to keep the vendor, whether the process should change. An agent implements whichever answer you put in the prompt, without warning you that the question was wrong.",{"type":148,"content":358},[359,360,331],"Finally, be sceptical of vendor arithmetic. The 4,500 developer-years and the 50% reductions come from specific programs with specific boundaries. They are not a forecast for your repository. Measure cycle time per slice, escaped defects, review hours and rollback count. If the agent doubles diff volume and review time stays flat, you have not sped up the migration; you have moved the queue. The wider economics of this trade-off are in",{"tag":232,"to":361,"children":362},"\u002Fblog\u002Fai-assisted-development-economics",[363],"AI-assisted development economics",{"type":155,"level":156,"id":141,"text":142},{"type":310,"ordered":366,"items":367},true,[368,370,372,374,376,378,380,382,384],[369],"Write down the outcome and the boundary: which behavior moves, which does not, and how you will know.",[371],"Make the old system runnable in CI with its real dependencies.",[373],"Capture representative inputs and outputs; mask secrets and volatile fields.",[375],"Add characterization tests around the first seam; accept that they describe, not approve, current behavior.",[377],"Put a router or facade in front of the seam.",[379],"Write a task card per slice with files, invariants, out-of-scope and evidence.",[381],"Let the agent work in a branch; require a failing test first where possible.",[383],"Review with a diff budget, a no-test-edits rule and an explicit rollback.",[385],"Raise traffic gradually, compare, then delete the old path and the glue.",{"type":155,"level":156,"id":144,"text":145},{"type":310,"ordered":366,"items":388},[389,393,396,399,402,405,408,411,414,417,420,423],[390],{"tag":391,"href":40,"children":392},"a",[39],[394],{"tag":391,"href":33,"children":395},[42],[397],{"tag":391,"href":45,"children":398},[44],[400],{"tag":391,"href":48,"children":401},[47],[403],{"tag":391,"href":51,"children":404},[50],[406],{"tag":391,"href":54,"children":407},[53],[409],{"tag":391,"href":57,"children":410},[56],[412],{"tag":391,"href":60,"children":413},[59],[415],{"tag":391,"href":63,"children":416},[62],[418],{"tag":391,"href":66,"children":419},[65],[421],{"tag":391,"href":69,"children":422},[68],[424],{"tag":391,"href":72,"children":425},[71],[427,484,554,623],{"slug":428,"published":5,"minutes":6,"category":7,"tags":429,"keywords":435,"about":446,"sources":456,"cover":478,"og":479,"expertise":75,"locales":480,"lang":77,"title":481,"description":482,"coverAlt":483},"agent-tool-design-descriptions-errors",[430,431,432,433,434],"AI agents","Tool use","Function calling","MCP","Agent design",[436,437,438,439,440,441,442,443,444,445],"agent tool design","writing tool descriptions for llm","function calling best practices","tool use error handling","mcp tool design","tool calling is_error","how many tools should an agent have","tool search defer_loading","agent computer interface","llm tool schema design",[447,450,453],{"name":448,"url":449},"Intelligent agent","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FIntelligent_agent",{"name":451,"url":452},"Model Context Protocol","https:\u002F\u002Fmodelcontextprotocol.io\u002F",{"name":454,"url":455},"Poka-yoke","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPoka-yoke",[457,460,463,466,469,472,475],{"title":458,"url":459},"Anthropic Engineering: Writing effective tools for agents, with agents (Sep 2025)","https:\u002F\u002Fwww.anthropic.com\u002Fengineering\u002Fwriting-tools-for-agents",{"title":461,"url":462},"Anthropic Engineering: Building effective agents (Dec 2024)","https:\u002F\u002Fwww.anthropic.com\u002Fengineering\u002Fbuilding-effective-agents",{"title":464,"url":465},"Claude Platform Docs: Define tools","https:\u002F\u002Fplatform.claude.com\u002Fdocs\u002Fen\u002Fagents-and-tools\u002Ftool-use\u002Fdefine-tools",{"title":467,"url":468},"Claude Platform Docs: Handle tool calls","https:\u002F\u002Fplatform.claude.com\u002Fdocs\u002Fen\u002Fagents-and-tools\u002Ftool-use\u002Fhandle-tool-calls",{"title":470,"url":471},"Claude Platform Docs: Tool search tool","https:\u002F\u002Fplatform.claude.com\u002Fdocs\u002Fen\u002Fagents-and-tools\u002Ftool-use\u002Ftool-search-tool",{"title":473,"url":474},"Model Context Protocol specification 2025-11-25: Tools","https:\u002F\u002Fmodelcontextprotocol.io\u002Fspecification\u002F2025-11-25\u002Fserver\u002Ftools",{"title":476,"url":477},"OpenAI API Docs: Function calling","https:\u002F\u002Fdevelopers.openai.com\u002Fapi\u002Fdocs\u002Fguides\u002Ffunction-calling","\u002Fimages\u002Fblog\u002Fagent-tool-design-descriptions-errors\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fagent-tool-design-descriptions-errors\u002Fog.jpg",[77,78,79],"Designing tools for AI agents: names, descriptions, schemas and error messages","How to design agent tools that get picked and called correctly: task-shaped tools, descriptions, schemas, compact responses and errors a model can act on.","Three rectangular slots in a base, with a block of matching width lined up over the middle slot and two wrong-width blocks beside it.",{"slug":485,"published":5,"minutes":486,"category":7,"tags":487,"keywords":491,"about":503,"sources":513,"cover":548,"og":549,"expertise":75,"locales":550,"lang":77,"title":551,"description":552,"coverAlt":553},"background-agents-async-pr-workflow",12,[10,488,489,13,490],"Pull requests","CI","Automation",[492,493,494,495,496,497,498,499,500,501,502],"background coding agent","async coding agent","cloud coding agent pull request","copilot coding agent limits","codex cloud tasks","claude code cloud sessions","jules coding agent limits","agent pull request review","coding agent sandbox","agent CI gating","cost per merged pull request",[504,507,510],{"name":505,"url":506},"Software agent","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSoftware_agent",{"name":508,"url":509},"Pull request","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FDistributed_version_control",{"name":511,"url":512},"Continuous integration","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FContinuous_integration",[514,515,518,521,524,527,530,533,536,539,542,545],{"title":71,"url":72},{"title":516,"url":517},"GitHub Docs: Risks and mitigations for Copilot cloud agent","https:\u002F\u002Fdocs.github.com\u002Fen\u002Fcopilot\u002Fconcepts\u002Fagents\u002Fcloud-agent\u002Frisks-and-mitigations",{"title":519,"url":520},"OpenAI Developers: Codex cloud environments","https:\u002F\u002Fdevelopers.openai.com\u002Fcodex\u002Fcloud\u002Fenvironments",{"title":522,"url":523},"Anthropic: Use Claude Code in the cloud","https:\u002F\u002Fcode.claude.com\u002Fdocs\u002Fen\u002Fclaude-code-on-the-web",{"title":525,"url":526},"Google Jules: Limits and plans","https:\u002F\u002Fjules.google\u002Fdocs\u002Fusage-limits\u002F",{"title":528,"url":529},"Google Jules: Getting started","https:\u002F\u002Fjules.google\u002Fdocs\u002F",{"title":531,"url":532},"GitHub Docs: About protected branches","https:\u002F\u002Fdocs.github.com\u002Fen\u002Frepositories\u002Fconfiguring-branches-and-merges-in-your-repository\u002Fmanaging-protected-branches\u002Fabout-protected-branches",{"title":534,"url":535},"GitHub Docs: Models and pricing for GitHub Copilot","https:\u002F\u002Fdocs.github.com\u002Fen\u002Fcopilot\u002Freference\u002Fcopilot-billing\u002Fmodels-and-pricing",{"title":537,"url":538},"OpenAI Developers: Codex pricing","https:\u002F\u002Fdevelopers.openai.com\u002Fcodex\u002Fpricing",{"title":540,"url":541},"Ehsani et al.: Where Do AI Coding Agents Fail? An Empirical Study of Failed Agentic Pull Requests in GitHub (MSR 2026, arXiv 2601.15195)","https:\u002F\u002Farxiv.org\u002Fabs\u002F2601.15195",{"title":543,"url":544},"Peralta et al.: Why Are Agentic Pull Requests Merged or Rejected? An Empirical Study (MSR 2026, arXiv 2605.22534)","https:\u002F\u002Farxiv.org\u002Fabs\u002F2605.22534",{"title":546,"url":547},"DORA: State of AI-assisted Software Development 2025","https:\u002F\u002Fdora.dev\u002Fdora-report-2025","\u002Fimages\u002Fblog\u002Fbackground-agents-async-pr-workflow\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fbackground-agents-async-pr-workflow\u002Fog.jpg",[77,78,79],"Background coding agents that open PRs: a practical async workflow","How async coding agents queue tasks, run in sandboxes, open draft PRs and pass CI. Provider limits, cost controls, review design and failure modes.","One card in a tray fanning out to five pads, all arrows leading to one page behind a gate.",{"slug":555,"published":556,"minutes":557,"category":7,"tags":558,"keywords":564,"about":576,"sources":584,"cover":617,"og":618,"expertise":75,"locales":619,"lang":77,"title":620,"description":621,"coverAlt":622},"context-rot-compaction-long-running-agents","2026-10-16",10,[559,560,561,562,563],"Context rot","Compaction","Context engineering","Long-running agents","Agent memory",[565,566,567,568,569,570,571,572,573,574,575],"context rot","context rot LLM","compaction AI agents","context engineering","long-running agents context window","Claude compaction API","context editing clear tool results","OpenAI responses compaction","lost in the middle LLM","agent context management","sub-agents context isolation",[577,580,583],{"name":578,"url":579},"Context window","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FLarge_language_model#Context_window",{"name":581,"url":582},"Attention (machine learning)","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FAttention_(machine_learning)",{"name":448,"url":449},[585,588,591,594,597,599,602,605,608,611,614],{"title":586,"url":587},"Chroma: Context Rot: How Increasing Input Tokens Impacts LLM Performance (Jul 2025)","https:\u002F\u002Fresearch.trychroma.com\u002Fcontext-rot",{"title":589,"url":590},"Liu et al.: Lost in the Middle: How Language Models Use Long Contexts (2023)","https:\u002F\u002Farxiv.org\u002Fabs\u002F2307.03172",{"title":592,"url":593},"Modarressi et al.: NoLiMa: Long-Context Evaluation Beyond Literal Matching (2025)","https:\u002F\u002Farxiv.org\u002Fabs\u002F2502.05167",{"title":595,"url":596},"Anthropic: Effective context engineering for AI agents (Sep 2025)","https:\u002F\u002Fwww.anthropic.com\u002Fengineering\u002Feffective-context-engineering-for-ai-agents",{"title":598,"url":51},"Anthropic: Effective harnesses for long-running agents (Nov 2025)",{"title":600,"url":601},"Claude API docs: Compaction at a token threshold","https:\u002F\u002Fplatform.claude.com\u002Fdocs\u002Fen\u002Fbuild-with-claude\u002Fcompaction-threshold",{"title":603,"url":604},"Claude API docs: Compaction on demand","https:\u002F\u002Fplatform.claude.com\u002Fdocs\u002Fen\u002Fbuild-with-claude\u002Fcompaction-on-demand",{"title":606,"url":607},"Claude API docs: Context editing","https:\u002F\u002Fplatform.claude.com\u002Fdocs\u002Fen\u002Fbuild-with-claude\u002Fcontext-editing",{"title":609,"url":610},"Claude API docs: Memory tool","https:\u002F\u002Fplatform.claude.com\u002Fdocs\u002Fen\u002Fagents-and-tools\u002Ftool-use\u002Fmemory-tool",{"title":612,"url":613},"OpenAI API docs: Compaction","https:\u002F\u002Fplatform.openai.com\u002Fdocs\u002Fguides\u002Fcompaction",{"title":615,"url":616},"Cognition: Don't Build Multi-Agents","https:\u002F\u002Fcognition.ai\u002Fblog\u002Fdont-build-multi-agents","\u002Fimages\u002Fblog\u002Fcontext-rot-compaction-long-running-agents\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fcontext-rot-compaction-long-running-agents\u002Fog.jpg",[77,78,79],"Context rot in long-running agents: clearing, compaction, memory and sub-agents","Why LLM accuracy drops as context grows, and how clearing tool results, compaction, notes and sub-agents keep long-running agents on track.","A small square press squeezing a tall stack of sheets into a short one, two notes branching off.",{"slug":624,"published":556,"minutes":625,"category":7,"tags":626,"keywords":630,"about":641,"sources":648,"cover":664,"og":665,"expertise":75,"locales":666,"lang":77,"title":667,"description":668,"coverAlt":669},"browser-agents-e2e-testing-playwright",7,[627,628,10,433,629],"Playwright","E2E testing","Test automation",[631,632,633,634,635,636,637,638,639,640],"playwright test agents","playwright planner generator healer","ai e2e testing","playwright mcp","llm generated playwright tests","self-healing tests","browser agent testing","playwright init-agents","ai test automation 2026","playwright cli skills",[642,644,647],{"name":627,"url":643},"https:\u002F\u002Fplaywright.dev\u002F",{"name":645,"url":646},"End-to-end testing","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSystem_testing",{"name":451,"url":452},[649,652,655,658,661],{"title":650,"url":651},"Playwright docs: Test agents (planner, generator, healer)","https:\u002F\u002Fplaywright.dev\u002Fdocs\u002Ftest-agents",{"title":653,"url":654},"Microsoft: Playwright MCP README (MCP vs CLI, accessibility snapshots)","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fplaywright-mcp",{"title":656,"url":657},"Playwright docs: Best practices","https:\u002F\u002Fplaywright.dev\u002Fdocs\u002Fbest-practices",{"title":659,"url":660},"Playwright docs: Locators","https:\u002F\u002Fplaywright.dev\u002Fdocs\u002Flocators",{"title":662,"url":663},"Browserbase: Stagehand README (act, extract, observe)","https:\u002F\u002Fgithub.com\u002Fbrowserbase\u002Fstagehand","\u002Fimages\u002Fblog\u002Fbrowser-agents-e2e-testing-playwright\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fbrowser-agents-e2e-testing-playwright\u002Fog.jpg",[77,78,79],"Browser agents for E2E testing: let agents write Playwright tests, not run them","Where AI agents help with end-to-end tests: Playwright planner, generator and healer, MCP or CLI, and the guardrails that keep healed tests honest.","Small square modules on the left handing a plain rectangle to a closed rectangular loop on the right.",1792245345462]