Service area

How do you modernize a legacy codebase with AI without breaking what works?

Write the tests first, from the system's real behavior, then move one framework or runtime at a time behind them. AI makes the parts that used to make this unaffordable cheap: reading code nobody understands, generating the tests that were never written, drafting the migration for each module. It does not make judgment cheap, and judgment is where rewrites fail.

What the research saysEngineering teams holding a system nobody wants to touch
Chart titled "Almost right is the expensive failure". 66% name "AI solutions that are almost right, but not quite" as their biggest frustration. 46% distrust the accuracy of AI tools, against 33% who trust it. 45% say debugging AI-generated code takes longer.
The migration order
  1. Map the systemtwo weeksA risk map: what it does, what talks to what, what has not executed in years, where the risk concentrates.
  2. Characterize before movingTests written from observed behaviour, drafted in bulk, pruned by someone who knows the domain.
  3. One boundary at a timeRuntime, then framework, then the modules with the fewest dependents. Old and new run side by side until the new one has earned it.
  4. Remove with evidenceDead code and abandoned dependencies come out, each with a reason recorded.
  5. Leave a harness behindReview gates and rules for what AI may touch, so the codebase does not rot again by 2028.

You can stop after any step and be better off than you started, which is not true of a rewrite at the halfway point.

Legacy Code Modernization

Why the system nobody touches is now urgent

Most legacy codebases are not a problem until a support date passes. SQL Server 2016 left extended support in July 2026 (Microsoft lifecycle). .NET 8 reaches end of support in November 2026 (.NET support policy), which surprises teams that migrated to it recently and thought they were done. An application nobody has changed in five years is fine right up until the platform under it stops receiving security patches, and then it is the most urgent thing in the company.

The reason it was never modernized is not mystery. The people who wrote it left, nothing is documented, there are no tests, and every estimate for a rewrite came back at a number the business would not pay. Those four facts are exactly the ones AI changes.

What AI changed, and what it did not

Reading two hundred thousand lines of code nobody understands used to be a quarter of a senior engineer’s year. A model reads all of it in an afternoon and produces a map that is mostly right. Writing tests for behavior that was never specified used to be the reason tests were never written. A model drafts hundreds of them from observed inputs and outputs, and the job becomes choosing which ones matter. Drafting the migration of a module from one framework to another used to be a week per module. It is now a day, most of which is review.

What did not change: knowing which behaviors are load-bearing, which bugs customers depend on, and which module to move first so that a failure is survivable. That is judgment, and it is the part we sell. AI on its own produces a migration that compiles and is wrong in ways nobody finds until a customer does.

What we actually do

Assess, two weeks. We read the codebase with the tools and produce a map: what it does, what talks to what, what has not been executed in years, and where the risk concentrates. You get a written risk map and a module order before anything is touched.

Characterize before moving. Tests written from what the system does today, generated in bulk and then pruned by someone who knows the domain. This is the step every failed migration skipped. It is also the step that makes the rest of the work safe to do quickly.

One boundary at a time. Runtime first, then framework, then the modules that depend on the fewest others. The old and new run side by side until the new one has earned the traffic. You can stop after any module and be better off than you started, which is not true of a rewrite at the halfway point.

Remove with evidence. Dead code and abandoned dependencies come out, each with a reason recorded. A codebase that shrinks during modernization is a codebase the team is willing to change afterward.

Leave a harness behind. The migration sets the pattern for how changes get made: AI drafts, review gates check, a named person approves. We install that practice in the repo so the codebase does not rot again by 2028. It is the Engineering Enablement service applied to your code, and usually part of the same engagement.

The migrations we see most

  • .NET Framework 4.x to .NET 8 or .NET 10. The most common request and the most tractable, except for the UI layer when it is Web Forms. Web Forms never made it to modern .NET (Microsoft), so that part is a rewrite, and we say so on the first call.
  • SQL Server 2012 through 2016 to a supported version, on-premises or to Azure SQL. Thirty years of combined database administration means the upgrade is the easy part; the schema and query debt underneath it is where the work is, and it is also the Data & Platform service.
  • Classic ASP, VB6, and the things nobody admits to running. Less common, more urgent when they appear, and usually the systems the business depends on most.

What you get

  • Codebase assessment and risk map: what the system actually does, who depends on which part, and where the bodies are
  • Characterization tests written from observed behavior before any code moves, so the migration has something to be checked against
  • An incremental migration plan by module, with the order chosen by risk and dependency rather than by convenience
  • Framework and runtime upgrades, one boundary at a time, with the old and new running side by side until the new one has earned it
  • Dead-code and dependency removal, with evidence for each removal rather than a guess
  • Documentation generated from the system as it is, then corrected by the people who know it
  • A coding harness left behind so the codebase stays current, which is the Engineering Enablement service applied to your repo

Questions we get

Can AI really understand a codebase nobody on the team understands?
It can read all of it, which no person on the team has done in years, and produce a map that is mostly right. The mostly is the job. We use it to find the parts worth a human's attention and to draft explanations that your longest-tenured engineer then corrects in an afternoon instead of a month.
Why tests first? We have never had tests.
Because without them the only way to know the migration worked is to ship it and wait. Characterization tests record what the system does today, right or wrong, so the new version can be held to it. AI writes most of them; we choose which behaviors matter.
Our app is ASP.NET Web Forms. Can it move to modern .NET?
Not directly. Web Forms was never brought to .NET Core or later, so the UI layer is a rewrite whatever anyone tells you. The business logic and data access underneath usually are not, and that is where the migration starts. We say this on the first call rather than the fourth month.
How is this different from the big rewrite we were quoted?
A rewrite replaces everything at once and is judged at the end. This replaces one boundary at a time and is judged at each one. You can stop after any module and still be better off, which is not true of a rewrite at the halfway point.
What happens after you leave?
The codebase rots again unless the team's practice changes with it. We leave a coding harness behind, with review gates and rules for what AI may touch, so the next change is made the same way the migration was. That is a separate service, and it is usually part of the same engagement.

How it starts

Two-week codebase assessment, then migration sprints of four to eight weeks per module group. You keep everything produced, whether or not there is a next part.

Talk to us