>
Software

AI helps legacy migrations when analysis comes before transformation

Six months after a team I worked with rewrote their legacy application, the codebase looked nothing like the old one. The deployment pain was the same. The unclear business rules were the same. The integration bugs were the same. What had changed was the language, and the language was the easy part. AI coding tools make that exact failure mode faster to reach. They translate code competently, generate tests in seconds, and explain unfamiliar classes in plain English. They do not know which parts of the system matter and which parts exist because no one ever cleaned them up. If you point an AI tool at a legacy codebase and ask it to migrate, the most likely outcome is a modern stack that preserves the original mess.

The right way to use AI in a legacy migration is closer to research than to translation. The first job is to figure out what the existing system does, why it does it that way, and which behaviours are load-bearing. The migration itself comes later, and even then the AI is most useful for the mechanical parts. The thinking still belongs to the engineers who understand the business.

Why a straight rewrite through AI usually loses

A translation-focused approach assumes the legacy code is the problem. It rarely is. The code is the part that compiles and runs in production. The harder problems are the ones that do not show up in the source: business rules that live in stored procedures, edge cases that survive in tests nobody runs, side effects that come from a cron job that runs at 3 AM. A clean translation through an AI tool can quietly drop all of that, because the AI does not see those things as code to translate. It sees them as something else.

I have watched this pattern repeat across a few different teams. The first attempt is the optimistic one: a junior engineer runs the codebase through an AI migration tool, the tests pass on the new stack, and the team ships it. The regression reports start arriving in the first month. Pricing logic that was never expressed in code is now wrong. Scheduled jobs that depended on a quirk in the old framework no longer fire. The new system is faster, but it is not the same system.

The translation-first approach assumes the legacy code is well understood. In most legacy systems, it is not. The code is what was written; the system is what was written plus what was never written but somehow ended up working. A migration that does not surface the second part is a migration that ships a regression.

The analysis-first workflow

The alternative is to spend the first half of the migration on analysis, before any AI tool is asked to write code. The analysis has three parts: mapping, behavior-capture, and boundary identification.

Mapping is the inventory stage. The goal is to understand the shape of the codebase: where the entry points are, what talks to what, which databases and external services are involved, which jobs run on which schedule, which configuration is read at boot, and which shared state is mutated by multiple components. A team that has done this mapping well can answer “what does this system touch?” in a single conversation. A team that has not cannot answer it at all. AI tools help here, but only as research assistants. The right prompt is “list every external dependency in this module and what it does,” not “rewrite this module in TypeScript.”

Behavior-capture is the second stage, and it is the one most teams skip. The point is to record what the system actually does today, before the migration changes anything. The test suite, if it exists, is part of this. The integration tests, the production logs, the database constraints, and the cron job outputs are also part of this. A behavior-capture pass produces a record that says: when the user does X, the system does Y. That record is what lets the migration happen with confidence. If a behaviour is captured and the migration breaks it, the test fails and the team knows. If a behaviour is not captured, the migration breaks it and the team does not know until a customer complains.

Boundary identification is the third stage. The goal is to find the places where the old system and the new system can run side by side for a while. These places matter because they let the migration happen in pieces instead of as a single cutover. A good boundary is a place where the old system’s behaviour can be reproduced by a new component that does not know the rest of the system exists. Pricing rules, customer validation, and notification formatting are common boundary candidates. Database access, payment processing, and email delivery are common infrastructure candidates that should sit behind interfaces so the new system can swap implementations without rewriting the workflow.

A surprising amount of the value here comes from the analysis, not from the AI. Once the team has a clean map, a behavior-capture record, and a list of boundaries, the migration is mostly mechanical work that AI tools are well-suited for.

What AI is good for, and what it is not

The honest split is this: AI is good at the parts of a migration that are repetitive, well-bounded, and have a clear target. It is not good at the parts that require understanding what the business actually needs.

Generating adapters (small glue components that translate between two interfaces) is the canonical AI win. The old code talks to a payment gateway in one shape; the new code wants to talk to it in another. The adapter is straightforward to specify and tedious to write by hand. AI tools do this well. Translating a configuration file from one framework’s format to another is the same shape: clear target, clear input, no judgment required. Updating type definitions across a module, generating database access code from a schema, and producing integration test stubs are all in the same category.

What AI is not good for is the design decisions that shape the migration. Should the new system use the same pricing model or a different one? Should the order workflow be synchronous or event-driven? Should the customer validation logic move to the gateway or stay in the application layer? These questions require understanding the business, not just the code. An AI tool can list options for each, but it cannot pick the right one for the company’s actual situation.

A short list of where AI helps during the mechanical phase, and where it does not:

  • Adapter generation. Translating a payment gateway call from one shape to another; the input and output are both clear.
  • Configuration migration. Converting a config file from one framework’s format to another; no judgment involved.
  • Type and stub generation. Producing TypeScript or schema definitions from a sample of the legacy code.
  • Repetitive integration code. Re-writing boilerplate that the new framework expects but the old code did not bother with.
  • Design decisions. Pricing model, workflow shape, validation placement, boundary placement: these need human judgment, not pattern matching.

The last category is the longest and the most expensive to get wrong.

This is where the analysis-first workflow pays off. The mapping stage surfaces the design decisions before the AI gets involved. The behavior-capture stage records what the current system does so the team knows what they are trying to preserve. The boundary identification stage narrows the scope of each design decision to a place where a human can think about it. After that, the AI is a multiplier on the mechanical work, not a substitute for the thinking.

Trade-offs

The analysis-first workflow is slower to start. A team that follows it spends the first two weeks of the migration reading code, writing behavior-capture tests, and arguing about boundaries instead of producing visible progress. That delay is real, and it is the main reason teams skip it. The trade-off is that the migration is more likely to ship without regressions and more likely to be maintainable by the team that runs it afterwards.

Behavior-capture tests are not free either. They duplicate work that the existing test suite, if it exists, already does. They are also a record of the current behavior, including behaviors the team might decide are bugs. When the migration changes those behaviors, the team has to decide intentionally. That intentional decision is the point. The cost is the conversation, not the tests.

The boundary-based approach assumes the legacy system has boundaries that map cleanly to the new system. Sometimes it does not. When the legacy system is tightly coupled, the analysis surfaces that fact, and the team has to decide whether to invest in decoupling first or accept a higher-risk cutover. AI does not help with that decision.

Bottom line

If the migration is small enough that one engineer can hold the whole system in their head, a translation-first approach through an AI tool is probably fine. If the migration touches a system with more than a few years of accumulated business logic, the analysis-first workflow is the difference between a migration that ships and a migration that ships and then slowly breaks. The AI is a multiplier. The thinking is still yours.

Leave a comment