Ten Agents Read a Dead Codebase So My Rewrite Didn't Have to Guess
// 2026-09-04 · Frederic Haddad · 9 min read
I spent months rebuilding a platform whose source code I had never seen.
That sounds like malpractice. It wasn't a choice. One of my businesses runs on a legacy codebase I inherited, and what I inherited did not include the source that actually produced the output. So the rewrite got built the only way it could be: from scraped pages, from samples of what the old system produced, from watching it behave and inferring the rule behind it. Months of careful guessing, on a modern stack, with tests.
Then the original production source turned up.
Ground truth arrives after you've already built the wrong thing
This is the worst moment in a migration, and it is far more common than anyone admits. You hold two things at once: a rewrite that mostly works and is founded on inference, and a pile of real code that is authoritative and completely unreadable at any speed a human can sustain. Dozens of distinct output paths. Business rules smeared across years of small fixes, every one of them presumably made because something broke for somebody.
The obvious wrong move is to start reading it yourself, top to bottom. You'll be there for weeks and still miss the clause that fires on one edge case.
The second wrong move is to hand the whole pile to an AI agent and say "port this." That is how you get an enormous, plausible, unverifiable diff that nobody will ever audit.
What I did instead ran inside a single working session of under three hours. It ended with a test suite that went from 202 passing tests to 1,137, and one critical security hole found and closed before it ever shipped. The shape of the process is the part worth stealing.
Scout before you build
Nothing was written in the first phase. I ran ten agents in parallel, all read-only, each on a slice of the legacy code, each with one job: compare this against what we already built and tell me precisely what is missing or wrong.
Not "summarize the codebase." Not "suggest improvements." A ranked, concrete gap map with file-level specifics.
It came back with 112 gaps. Some were trivial. Several were entire behaviors the rewrite had invented from scratch and gotten subtly wrong — the kind of defect that produces output which looks correct and is wrong in a way somebody discovers three weeks later.
That's the whole output of the phase. No feature, no commit, no demo. Surveyors before builders. Skip it and every builder invents its own assumptions, and you find out where they disagree at integration time, which in practice means after the thing is already in front of people.
The ranking mattered as much as the list. Ten agents can produce a hundred true observations of wildly different importance, and an unranked pile of true observations is just a longer form of not knowing.
Four waves, committed in order
With the map in hand I ran four sequential waves of agents. Sequential is the important word.
- Foundations — six agents. The output pipeline, the data model, the core transaction path. The things everything else stands on.
- Output paths — twenty-one agents. The generated outputs themselves, ported one at a time against the real source rather than against a scraped copy of the result.
- Remaining product flows — five agents. The paths through the app that tie the rest together.
- Adversarial review — seven area reviewers, each with three refuters. More on this below, because it is the wave that earned its keep.
Every wave was committed to version control before the next one started. That single rule is what turns a large agent run from a gamble into a project. A wave that goes badly costs you a wave. Not the week.
Inside a wave, agents run in parallel and inevitably collide — two of them edit the same shared file, each correctly for its own purpose, and the result is a mess that belongs to nobody. I didn't let last-writer-wins decide that. Cross-file collisions went to dedicated integration agents whose entire job was reconciling other agents' output. It's the same reason a building site has someone who owns where the pipes and the wiring cross. Neither trade is wrong. Somebody still has to decide.
Interruption is a design constraint, not an accident
Here is the unglamorous bit that nobody puts in the demo video.
I hit the account's spend limit five separate times, mid-wave, during this work.
If your workflow treats that as an exception, you lose hours of parallel agent output every time it happens — and it will happen, along with dropped connections, expired sessions, and machines that reboot for updates. Long-running AI work is not a clean-room activity. It is a physical process that gets interrupted, and the interruptions are certainties, not risks.
So every stop was handled the same way: checkpoint the partial work to disk, then resume from cache rather than restart the wave. Five interruptions, zero lost work, nine commits from the first foundation wave to the final fix.
Design for the interruption and it becomes a pause. Ignore it and it becomes a rollback. It is the same lesson I keep relearning about automation that survives contact with reality generally — the systems that survive are the ones that assume something will go wrong at the worst possible moment and write down where they were before it does.
The wave that paid for everything
Thirty-two builder agents produced a large, coherent, well-tested body of work. The test suite grew more than fivefold. Every agent could account for its own slice.
I still ran an adversarial wave, because competent is not the same as verified.
That wave was hostile by construction. Seven reviewers, each owning an area, each hunting for defects rather than confirming success. And each finding was handed to three refuters whose job was to attack it — to argue the finding was wrong, misread, or already handled. That structure produced 59 findings, of which 49 survived scrutiny and were fixed. Ten were knocked down. Without refuters those ten become ten pointless changes to working code, and the review loses credibility, and next time you skip it.
Then the thing that justified the entire exercise.
The reviewers found an unvalidated field flowing into a server-side rendering path — a value that arrived from outside, unchecked, and reached a component capable of executing code. In plain terms: an unauthenticated request could get code running inside my rendering process. It was live in the port. It had passed thirty-two building agents and a growing test suite without a murmur.
Why thirty-two agents walked past it
Because every one of them was right.
The agent that carried the field through was correct: it faithfully preserved a legacy behavior. The agent that built the renderer was correct: it rendered what it was given. The agent that wrote the tests was correct: the feature worked. Nobody was sloppy. The defect lived in the seam between three correct pieces, and no agent optimizing its own slice has any reason to look there.
Every room passed inspection. The wiring between the rooms was the problem. I've hit the same law in a completely different project — a conversion audit that surfaced an unauthenticated endpoint in a flow where every individual change had been reviewed — and it's why 8,600 green tests were still hiding 31 real bugs. Individually reviewed pieces, individually passing tests, defects in the seams.
That's the general law, and it applies to human teams just as hard. Parallel workers optimize locally. Cross-cutting defects — security holes especially — live between the slices, so someone whose only assignment is to attack the finished whole has to go looking. Budget that pass. It is not a formality at the end; it is a deliverable of its own.
It is also why the ranked gap map at the start and the adversarial wave at the end are the same instrument pointed in opposite directions. One of them tells you what the honest local reports will miss before anyone writes code; the other tells you what they missed after. Both exist because the middle of the process — thirty-two capable agents each doing correct work inside its own boundary — has no mechanism for noticing a problem that belongs to no boundary. You cannot get that from more agents, or better prompts, or a larger model. You get it from putting something in the run whose only assignment is the space between the slices.
The template
Strip out my specifics and this is the operating procedure for any AI-driven migration worth attempting:
- Scout first, read-only. Produce a ranked gap map before a line of code is written. Unranked findings are noise.
- Sequence into phases, and commit each one. Parallel inside a phase, strictly serial between them. A bad phase should cost a phase.
- Integrate deliberately. Give collisions between parallel workers to someone whose job is the collision, not to whoever saved last.
- Assume interruption. Checkpoint to disk, resume from cache. Spend caps and dropped sessions are weather, not surprises.
- Finish with an adversarial pass. Independent reviewers, plus refuters to kill the weak findings. Treat agent output as competent but unverified until something hostile has read it.
The bottom line
AI can absolutely do a real legacy migration. What it cannot do is tell you it was thorough — because it will tell you it was thorough either way, sincerely, and it won't be lying about its own slice. Every agent reports honestly on the slice it can see, and the sum of honest local reports is not an audit. The same "read before you build" pass, scaled down to a single feature, is what six questions instead of an immediate build looked like.
A lot of companies here in Dubai and across the UAE are sitting on exactly this problem: the tool that routes customer support tickets, the thing finance closes the month on, the procurement or logistics system somebody built years ago that still runs the business and that nobody fully understands and everybody is afraid to touch. Those are the best candidates for this method, and the worst candidates for pointing an agent at the repository and hoping.
I migrated a platform I'd never seen the source of, and the decisive move was ten agents reading before a single line was written. If you have a legacy system you're scared to migrate, a day on-site is usually enough to build the gap map and the phase plan. Book a consulting day or send me an inquiry first if you'd rather talk before booking.