Skip to content
Insights · Engineering

Rewrite or repair an AI-built codebase

The honest default is repair. Three conditions genuinely justify starting again, three more feel like they do and never survive contact — and the option nobody offers you is to replace it one seam at a time.

Scroll
EngineeringSep 16, 20268 min readBy Salman Naqvi, Founder & CEO
Rewrite or repair an AI-built codebase

Repair, almost always. Three conditions genuinely justify a rewrite, and all three are structural rather than aesthetic. The shape is wrong at the centre: the change you need is to the thing every other part is built around, so nothing can move until it does. Nobody can run it: there is no path from a clean machine to a working copy, so the system cannot be repaired even in principle, only guessed at. The floor has gone: the runtime, the framework major version or the hosted service underneath is out of support, so the upgrade is itself a rewrite. Everything else — ugly code, inconsistent naming, a stack you would not have picked, an engineer who says they would start over — is a repair job wearing a rewrite's clothes.

The instinct runs harder against AI-built code than it deserves to, and both reasons are about legibility rather than quality. Generated code is locally idiomatic and globally inconsistent: every prompt was answered on its own, so the same job gets done three ways in three files and one helper is implemented four times under four names. It reads like four contractors who never met, which is close to what happened. And there is no history to read — a repository with one initial commit, or two hundred commits all called update, cannot tell you which lines were decisions and which were the first thing that worked. Every choice therefore looks arbitrary, and a codebase that looks arbitrary feels like it should be thrown away. That feeling is not evidence.

Reframed properly this is not a question about code. It is a question about which option costs more to own, and the answer is rarely dominated by the part you can see. Reviewing federal legacy systems, the US Government Accountability Office reported that of the money the government planned to spend on IT that fiscal year, "About 80 percent of this amount is used to operate and maintain existing IT investments, including aging (also called legacy) systems", and that "As they age, legacy systems can be more costly to maintain, more exposed to cybersecurity risks, and less effective in meeting their intended purpose" (US Government Accountability Office, Information Technology: Agencies Need to Develop Modernization Plans for Critical Legacy Systems, GAO-19-471, June 2019). Building is the cheap half even where systems are measured in decades. A rewrite buys a lower operating cost with a large, uncertain build cost — a good trade only when the cost you escape is both large and certain.

Google's Site Reliability Engineering book borrows a distinction from Fred Brooks that settles most rewrites before anybody starts. "Essential complexity is the complexity inherent in a given situation that cannot be removed from a problem definition, whereas accidental complexity is more fluid and can be resolved with engineering effort" (Google, Simplicity, Site Reliability Engineering). A rewrite deletes accidental complexity, the part that annoys you, and inherits every scrap of essential complexity — including the essential complexity nobody has found yet, because in a system with no written specification the only record of those rules is the running code. The same chapter supplies the arithmetic: "Every line of code changed or added to a project creates the potential for introducing new defects and bugs." A rewrite changes every line at once and ships them as one batch, and on that the chapter is equally blunt — "It is much easier to measure and understand the impact of a single change rather than a batch of changes released simultaneously."

Whether what you have is repairable is testable this week, and it is not a reading exercise. Google Cloud's DORA research defines code maintainability by three conditions a team can simply check: "It's easy for the team to find examples in the codebase, reuse other people's code, and change code maintained by other teams if necessary"; "It's easy for the team to add new dependencies to their project, and to migrate to a new version of a dependency"; and "The team's dependencies are stable and rarely break the code" (Google Cloud, DORA capabilities: code maintainability). AI-built projects usually pass the first, since generated code is repetitive and easy to search, and fail the second and third, since the dependency versions were whatever the model had seen and nobody has moved them since. That is a finite repair with a visible end rather than a reason to start again — and DORA supplies the deadline: "As dependencies age, it is more likely that vulnerabilities will be discovered in them."

A fourth test comes from the same GAO review, and it is the one that has changed most since founders started building their own first version. The Department of Education's system, GAO found, runs on "Common Business Oriented Language (COBOL)—a programming language that has a dwindling number of people available with the skills needed to support it". The modern equivalent is rarely an old language; it is an unusual combination nobody else runs. Test it empirically: hand the repository to an engineer you might hire, pay for a day, ask for one small real change. Three capable people each taking a week is evidence. Your own distaste is not.

Condition one: the shape is wrong at the centre. This is the only condition that justifies replacing everything, and it always arrives with a specific named change attached. The system was built for one customer at a time and you now sell to companies whose staff must not see each other's records. The schema models the wrong noun — it knows about orders when the business is really about subscriptions. Identity was an email address and one person now belongs to three organisations. Each of those touches nearly every file and nearly every table, which is the test itself: name the change, then ask how much of the system it moves. Nearly all of it is structural. One module, however unpleasant, is repair.

Condition two: nobody can run it. Decisive, and routinely mistaken for a code-quality problem. If there is no documented path from a clean machine to a running local copy — the repeatability that belongs in the handover you should receive from anyone who builds for you — then you do not have a codebase. You have a running instance and some files that resemble it, and nothing can be repaired safely because nothing can be tested before it is live. The good news is that this is usually a week of work rather than a verdict. Restore reproduction first, then decide: the decision is far better informed from the other side of it.

Condition three: the floor has gone. A runtime out of support. A major version whose upgrade path rewrites every call site. A hosted service being retired on a date the provider has already published. Here the work is not optional and the only question is scope: rarely everything, usually just the parts standing directly on the floor that moved. The vendor's deprecation date is also the one genuinely reliable deadline in this decision, so put it on the plan first.

And the three that feel like conditions and are not. *A new engineer says they would start over.* The most common cause of an unnecessary rewrite, and entirely sincere: unfamiliar code feels worse than familiar code, always, and the estimate is produced by the one person who has not yet discovered what the system does. Ask for the named change they cannot make instead. *You want a different framework.* A preference with a price attached, so price it. *The code is ugly.* Ugliness costs as a rate rather than a wall: it makes every change slower by a multiple you can measure in a month. The SRE book names the opposite bias with equal directness: "Because engineers are human beings who often form an emotional attachment to their creations, confrontations over large-scale purges of the source tree are not uncommon." Attachment and disgust are both feelings about code. Neither is a plan.

The option nobody offers you usually wins: replace it a seam at a time. Pick the smallest piece with a clean boundary — one job, one service, one screen — rewrite that piece behind the interface the rest of the system already calls, ship it, and go again. Every step is releasable and reversible, you can stop the moment the remaining code stops hurting, and you learn in week two rather than month six whether the new thing is better. Keep the data where it is for as long as you can, because migration is the one step here that does not reverse cleanly. Consolidating a home-security platform's three disconnected systems into one operator experience was that shape of job rather than a cutover: content, device provisioning and subscriptions had each grown their own tooling, and the replacement went out behind blue/green deploys.

If you do rewrite, write the plan federal auditors ask for, because it is three items long and the third is the one founders skip. Of the ten agencies holding the most critical legacy systems in GAO's review, seven had documented modernization plans and only two — Defense and the Interior — included all the key elements, namely "milestones, a description of the work necessary to complete the modernization, and a plan for the disposition of the legacy system". Three had no documented plan at all. Disposition decides whether a rewrite subtracts a system or adds one: who runs the old thing meanwhile, what happens to its data, who is still paying for it, and the date it gets switched off. A rewrite with no written switch-off date tends to leave two systems running, which is the most expensive outcome available.

Either way the first artefact is the same: a written list of the behaviours real users now depend on — including the ones you consider bugs, because somebody has built a workflow on at least one of them, and the quiet ones, like the export somebody's accountant relies on or the URL a customer has bookmarked. If you cannot write that list you cannot scope a rewrite, and a rewrite you cannot scope has no end date and no acceptance test.

Five things to do before deciding, in this order. Name the change you cannot make, in one sentence, and mark how much of the system it moves. Take a clean machine and try to get the thing running from the repository alone; time it. Pick the most outdated dependency, upgrade it, see what breaks. Hand the repository to one engineer for one paid day and ask for one small real change. Write the behaviour inventory. Four of the five are worth doing whichever way you decide: the assessment is not overhead on the decision, it is the first week of the work either way.

What usually comes out of that week is an unglamorous middle answer: the data model survives, one or two modules get replaced, the dependency floor gets lifted, and the operate half — tests, logs, permissions enforced at the endpoint, a spend ceiling — gets built for the first time, because the things that break when an AI-built prototype meets real users are mostly missing rather than wrong. If the front end is React, the checks that decide what an existing React application costs to own are the technical version of the tests above, and the free production-readiness diagnostic names the failure modes in your own system rather than in general. The reason a startup software development company is usually worth hiring for the repair rather than the rewrite is simple: repair keeps your users, your data and your revenue while it happens. And whoever does the work, the next decision is who you let near it — a judgement about people rather than code.

Find this useful? Tell Google to show you more of it.

Let's put AI to work in your business.

A 30-minute call. You bring the workflow or the roadmap — we'll tell you what's feasible, what it costs, and what we'd build first.

Book a call