Skip to content

system rewrites

Legacy application modernization: VB6, Delphi, PHP and ColdFusion into Go

PHP 5.6, VB6, Delphi, ColdFusion, Classic ASP, Perl, Oracle Forms, a Java monolith on a server nobody dares reboot. We rewrite them module by module into Go, Rust and TypeScript while the old system keeps serving production, and a parity harness compares old against new on your own recorded traffic. Thirty days for a system of around a million lines, in phases you can stop after.

Get a quoteFour screens about the stack, the pain and the deadline. An engineer answers, usually the same day.

30 days for a system of around a million lines, split into phases


The plaintext drop is struck through because part of every legacy system gets deleted rather than ported, and that is usually the cheapest work in the project.

Today

Four runtimes, two of them out of support, and a file drop nobody wants to look at.

  • app.phpPHP 5.6
  • legacy.dllVB6
  • reports.cfmColdFusion
  • crystal reportsunsupported
  • ftp-dropplaintext

After cutover

Go for the core and the reporting path, TypeScript on the front, transfer that is signed.

  • coreGo
  • reportingGo · read replica
  • webTypeScript
  • transferSFTP · signed

30 days for a system of around a million lines, split into phases

The plaintext drop is struck through because part of every legacy system gets deleted rather than ported, and that is usually the cheapest work in the project.

How the work goes

Nothing is switched over until the new module has produced the same output as the old one on your own traffic.
  1. 01days 1–4

    Map it

    Call graph, scheduled jobs, external interfaces, and a measured list of what is dead. Estimates go wrong because someone counted lines instead of finding out which lines still execute.

  2. 02days 3–9

    Put the seam in

    A boundary that traffic passes through, so old and new implementations can run side by side and be compared. Nothing user-visible changes in this step.

  3. 03the bulk of the project

    Port module by module

    Each module ships behind the seam, gets replayed against recorded production traffic, and takes real traffic when the outputs match. Bugs included, because which of them to fix is a separate conversation with the business.

  4. 043–7 days per subsystem

    Decommission

    Old code deleted, licences cancelled, servers switched off. A migration that leaves the old system running has doubled what you maintain instead of finishing.

What the platform does, what a person does

Translation is machine work and we do it at machine speed. What the code is supposed to mean is where our engineers earn their keep.
Machine translation without review produces code nobody will own. Every module goes through a person before it takes traffic.
What the platform does, what a person doesWho does itNotes
Reading the whole codebase and building the call graphPlatformIncluding the dialect nobody supports and the file that has been generated since 2009.
Finding dead codePlatformStatic reachability, plus production traces when you can give us a period of them.
Translating a modulePlatformFirst pass is machine output, reviewed as a pull request and rewritten by hand where a reviewer cannot follow it.
Deciding which behaviour is a bug and which is a ruleEngineerThe system has rounded the same way for twelve years and somebody downstream has built on it.
The parity harnessBothCapture and replay are ours. Which differences are acceptable is a decision your side signs.
Data migration and reconciliationBothThe rows that never balanced in the old database do not balance in the new one either.
Cutover and rollbackEngineerDates, on-call, and the point at which we agree to go back.

What we have moved before

Dozens of migrations and more than ten years inside legacy code. VB6 and Delphi clients into the browser, Classic ASP and ColdFusion into Go, PHP 4 and 5 monoliths into typed services, Perl cron scripts and PL/SQL packages into services with tests on them, Oracle Forms onto the web, Java that predates Maven into something a new hire can build on their first morning. Our engineers have met your dialect before, including the parts of it nobody wrote down.


The first phase is small enough to be a test of us: one module through the seam, replayed against your own traffic, with the diff report in front of you. You see how we work for the price of a phase rather than a year.

Questions we get from engineers

Can we keep shipping features during the rewrite?
Yes, and it is the main reason this takes longer than a clean-room rewrite. Feature work goes into the old modules until each one is ported, and the parity harness catches the ones that changed under us.
Why not refactor what we have?
Sometimes that is the right answer and you will hear it on the first call. The rewrite wins when the runtime is out of support, the toolchain only exists on one machine, or you cannot hire anyone who will touch it. We do both kinds of work, so the answer is not a sales position.
What if we stop halfway?
You are left with a system that is part Go, part what you had, with a documented boundary between them. That is a real state to live in, and some clients sit there for a year while budget frees up.
Does machine translation produce code we would accept?
Line-for-line translation does not, which is why we do not ship it. Each module is regenerated in idiomatic Go, reviewed like any pull request, and rewritten by hand when the reviewer cannot follow it. You are welcome to review the first module yourselves before we continue.
How do you estimate this?
From measured module sizes, coupling and how much of the code is reachable. The assessment gives you a range, a phase plan and prices against it in three to five days.

Start with one module

A rough description is enough to start: what the system is, how big, and what is forcing the date. You get a phase plan back, and the first phase is small enough to approve without a committee.

Get a quote

Ten minutes if you have the answers to hand. You can send it before you have decided anything.