Skip to content
Geeks Invention

AI Delivery / Legacy modernization

Framework and language migrations, run by an agent fleet

Migration as a repeatable method rather than a heroic project. The codebase gets analysed, the behaviour gets pinned down, the target architecture gets approved by your architect, and then the work runs in increments that each deploy on their own.

Migration is a method, not a rescue

Most failed migrations fail the same way. Somebody estimates the work by counting files, the estimate turns out to be wrong by a factor of three once the undocumented behaviour surfaces, and the project gets cancelled at the point where half the system has moved and neither half is complete.

What prevents that is doing the analysis before the estimate, and being willing to report back that the sequence is different from what anyone expected. The agent fleet is good at the analysis part: reading an entire codebase, mapping what calls what, and finding the behaviour that isn't written down anywhere. That's slow, unglamorous work that people do badly because it's boring, and it's the part that decides whether the rest of the project succeeds.

The pipeline

Codebase analysis

The whole repository gets read: the dependency graph, the call graph, dead code, the modules that everything touches, and the ones nobody has changed in five years. The output is a map of what's actually there rather than what the architecture diagram claims.

Behaviour mapping

Before anything moves, the current behaviour gets pinned down at the boundaries of whatever is being migrated. This is the step that makes equivalence checkable later, and it's the step most migrations skip.

Target architecture

The architecture agent proposes the target design and the sequence to reach it, mapped onto what you already have rather than onto a greenfield ideal.

Human gate: your architect approves the design and the sequence before implementation starts.

Conversion

Developer agents work through the sequence in parallel, following the conventions already present in your code. Each increment goes through independent review, security scanning and real-browser verification before a pull request opens.

Equivalence verification

This is the step that answers the question every technical buyer actually asks, which is how you know it still does the same thing. Each migrated unit is checked against the behaviour captured in step two. Divergence is reported as a failure rather than discovered by a customer.

Incremental cutover

Every increment deploys on its own and reverts on its own. There is no single switch, and production never waits for the migration to finish.

AngularJS and Angular to React

The most common request we get, and the one where coexistence matters most. AngularJS in particular is long past end-of-life, and the hiring market for it has effectively closed. Rather than a rewrite, a React shell runs alongside the existing application and screens move across one at a time. The approach is covered in depth on the coexistence page.

React to Angular

Less common, and usually driven by a platform standardisation decision rather than a technical one. It works the same way in reverse: an Angular shell hosts React fragments while the boundary moves.

PHP to Go

Typically paired with a decomposition, because a PHP monolith rewritten as a Go monolith rarely justifies the effort. The usual sequence extracts bounded contexts into Go services while the PHP application stays live and gradually loses responsibilities. The hard part is almost never the language. It's the shared database and the assumptions built on it.

Node.js to Go

Usually motivated by throughput or by operational cost at scale. Service by service, with traffic shifted behind a proxy so each migrated service proves itself under real load before the old one is retired.

Ruby on Rails to Go or Node

Rails applications tend to carry a great deal of behaviour in framework conventions rather than in explicit code, which makes the behaviour-mapping step unusually important here. Callbacks, validations and implicit ordering are where the surprises live.

jQuery and server-rendered legacy to a modern SPA or SSR

Often the largest change in practice, because it's an architectural shift rather than a syntax one. Server-rendered pages with jQuery behaviour layered on top don't have component boundaries, so the first real task is deciding what the components are.

JavaScript to TypeScript

The lowest-risk item on this list and a good first engagement. It's incremental by nature, every step is independently valuable, and the type errors surfaced along the way routinely find live bugs.

What you get

  • A written analysis of the existing codebase, including the parts nobody documented.
  • A target architecture and a migration sequence, approved by your architect before work starts.
  • Increments that deploy and revert independently.
  • Equivalence evidence per increment, not a single end-of-project sign-off.
  • An audit trail of every change and the reasoning behind it.

In practice

Placeholder — real figures required before launch.

Mid-market SaaS

The situation: An end-of-life front end, no realistic hiring path, and two abandoned rewrite attempts.

What we did: Analysis first, then incremental migration in production with the roadmap running throughout.

Outcome: TO SUPPLY: scope migrated, elapsed time, defects introduced, days of freeze

Questions we get

Our codebase has no tests. Does this still work?

It's the normal case, not the exception, and it changes the sequence rather than ruling the work out. We characterise the existing behaviour first — capture real inputs and outputs at the boundaries of the module being moved, and turn those into a suite describing what the system actually does today, including the parts that are arguably wrong. That becomes the equivalence check. It isn't a substitute for a real test suite and we won't pretend otherwise.

There's undocumented business logic nobody understands.

Every legacy system has some. We preserve the behaviour first and understand it second. Characterisation tests pin current behaviour before anything moves, so a change in output surfaces as a failure rather than a support ticket six weeks later. Logic that looks deliberate but undocumented goes on a list for your team to rule on — some of it turns out to be a bug that's been load-bearing for years, and that call is yours.

We have custom internal libraries.

They're part of the migration surface, not fixed dependencies. Each is assessed on whether it has a maintained equivalent in the target stack, should be ported as-is, or can be dropped because the platform absorbed the functionality. Internal libraries are often where the most valuable domain logic lives, so they get analysed early.

Send us the stack and the constraint

We'll come back with a sequence, the risks, and a timeline we'd stand behind.