What Happens When an Agent Rewrites Your Architecture

A coding agent can rewrite a codebase in a weekend and still miss behavior only production knew. Pin the old behavior before the structure changes.

By Muhammad Saif and Aashan Khan · Techtribe co-founders

An agent can rewrite the structure of a project in an afternoon: new folders, a new framework, a new data layer, a pull request with more files than anyone will read. The app may even build. What it cannot do, unless you built a check for it, is prove that the behavior users rely on survived. Architecture is the set of boundaries that make change safe. An unbounded rewrite removes the boundaries and calls the result cleaner.

The pile of generated files that makes the next change expensive is in AI-generated code technical debt. The incidents after a tidy diff ships are in AI-generated code that passes review and breaks in production. This note is the decision to let the agent change the shape of the system, and the checks that have to exist first.

What a large agent rewrite actually is

Anthropic published how they run large code migrations with Claude Code, including a port of Bun from Zig to Rust and a port of an internal Python tool to TypeScript. The numbers are a warning as much as a demonstration. Individual developers migrated packages of tens to hundreds of thousands of lines. Jarred Sumner's Bun port produced a million lines in under two weeks, with the existing test suite passing in CI before merge. Nineteen regressions surfaced after merge and were fixed. The Rust port shipped inside Claude Code in June. Mike Krieger's port was 165,000 lines of TypeScript over a weekend, with hundreds of agents, eight phase gates, three adversarial review rounds, and a parity check that diffed every command's output against the Python original.

The cost was not zero because the agents were fast. The Bun migration consumed 5.9 billion uncached input tokens and 690 million output tokens, about $165,000 at API pricing. The main part of the Python port was 27 million tokens. Anthropic's point is that these projects no longer need to be multi-year bets, and that the worst case is deleting the branch. The point for a product with users is different. You may not have Bun's test suite. Deleting the branch is only the worst case if the branch was not merged, and if production was not migrated in the same motion.

The article's core line is the one to keep: you do not fix the code, you fix the loop that produced the code. Their six steps start with a judge. Categorize tests into ones that can run as external checks and ones that reach internals that will not exist after the port. Rewrite the external ones so both codebases can be scored. Run the judge on the original code and confirm it passes. Run it on deliberately broken code and confirm it fails. A judge that does not catch breakage is not a judge. For the Python port, the judge was seven real scenarios, and any behavior change was a bug. The old program was the ground truth.

Only then do they write a rulebook, a dependency map, and a gap inventory. The rulebook comes before the gap list, because the gaps are what the rules do not cover. They stress-test the rules on a few files and throw the translated files away. The point of the shakedown is better rules, not a head start. Translation happens in a queue a script can resume, with adversarial reviewers in separate contexts. When the same mistake shows up in many files, they add one sentence to the rulebook and regenerate, instead of hand-patching forever. Compile errors and test failures become the queue. A build daemon is the only thing allowed to rebuild, so agents do not stampede the expensive step.

That is a serious process. It is not "ask the agent to restructure the repo." If you do not have a judge, you have the expensive part without the safety part.

The failure mode in a generated app

Most AI-built products do not have a million-line test suite in a third language. They have a demo that works for the author. An agent asked to "make this a proper architecture" will invent folders, split files, swap the database client, and rename concepts. The happy path may survive because the agent clicked through it. The webhook, the cron job, the admin route, and the edge case in a comment it deleted will not.

Characterization tests are how you pin what the system does today, including the odd parts, before you move it. A characterization test does not say the behavior is good. It says the behavior is this, so a refactor that changes it will go red. Write them for the paths users have already exercised. Put them where the agent cannot edit them, using the approach in testing an AI-built app. Land them in their own change and merge them before the refactor starts. The refactor's rule is then simple: it does not change those tests to make itself pass.

An architecture rule is the other oracle. "Domain code does not import the database driver directly," or "only this folder may call the payment provider." A dependency check that fails when the rule is broken gives you a count of remaining violations. The agent can work the count down. It should not be allowed to edit the rule in the same pull request. Otherwise the count goes to zero because the rule got weaker, which is the test-deletion problem one level up.

If other systems call your API, record the current responses for the requests you care about and diff them after the change. Krieger's parity check is that idea with seven scenarios. You can do it with a handful of captured calls. You cannot do it from the agent's assurance that the API is "equivalent."

Small steps, or you cannot undo it

A rewrite that touches the routing, the database, and the UI in one branch cannot be reverted without reverting the feature someone snuck in, and it cannot be deployed halfway. The recoverable version is a sequence of steps that each leave the app working.

Extract one piece, run the characterization tests, commit. Change the inside of that piece, tests still passing, commit. Point one caller at the new piece. Commit. Leave the old path until the callers are gone. The last deletion is its own change, after the new path has been live. This is the same expand-and-contract instinct as a schema migration, applied to code. The migration note is what you follow if the rewrite also wants to change tables. Do not let a "cleanup" branch be the thing that drops a column.

Anthropic's process throws away shakedown translations on purpose. Copy that. If the agent produces a spike, read it, keep the lessons, delete the code, and do the real change in reviewable steps. A spike merged because it "mostly works" is how you inherit a second architecture with none of the discipline that produced the spike.

Parallel agents make the mess faster. Two agents editing overlapping files will each pass their own tests and conflict in the places neither owned. If you split work, split by files that do not overlap, and have a person or a check that the pieces still call each other. A plan the agents wrote for each other is not that check. A request that runs through the real boundary is.

What to look for in the diff you will not fully read

You will not read ten thousand lines. Read these categories, all of them:

  • Deleted tests, weakened assertions, and edited fixtures. Reject the branch if the judge moved.
  • Changes to CI, agent rules, and dependency files. A refactor does not need a new install script. Supply chain and that diff are the same review, in malicious packages and the agent.
  • New frameworks added beside the old ones. Two ways to do everything is not a migration. It is a permanent tax, which is the technical-debt outcome.
  • Public API changes hidden in a "internal cleanup." A renamed JSON field is a break for every client you forgot.
  • Error handling that got broader. Catching every exception and returning success is how a refactor ships green and fails silent. The production-failure note is full of this shape.
  • Configuration and feature flags with no owner. A flag the agent added to "roll out safely" and that nobody can turn off is the old path and the new path welded together.

If those categories are dirty, stop. Do not ask the agent to continue from a dirty branch. Revert to the last step that still matched the judge.

A process small teams can actually run

You do not need hundreds of subagents. You need the judge and the sequence.

  1. List the user-visible behaviors that must not change. Ten is enough to start if they include login, the core write, and the payment or message path.
  2. Turn those into checks you can run in one command. Merge them first.
  3. Write the architecture rule in a sentence, and add a check if you can. "No new direct calls to the vendor SDK outside this folder" is a sentence you can grep for.
  4. Ask the agent for one extract, not a new architecture. Review it. Run the checks. Merge.
  5. Repeat. When a step fails the checks, the step is reverted. The rulebook gets a line about why. The agent does not get to "push through."
  6. Delete the old path only after the new path has been in production long enough to see a real user, and after you can still deploy the previous artifact. Recovery is in failed deploys.
  7. Cap the spend. A structural refactor is exactly the open-ended prompt that burns tokens. Set a budget before the weekend run, using the limits in why agents burn tokens. A $165,000 migration can be a business decision. An accidental one cannot.

If you cannot do step 2, you are not ready for step 4. No amount of model capability replaces a check that fails when the product changes.

What to stop doing

Do not ask for a new architecture because the folders feel messy. Messy folders with working checks are safer than a clean tree with none. Do not merge a refactor you could not roll back on a Friday. Do not let the same agent session rewrite the code and the tests that judge it. Do not treat a passing typecheck as a passing product. The Bun port's nineteen post-merge regressions happened with a real suite. Your typecheck is a weaker judge than that.

Do not start a port because a model release made it fashionable. Start it when the current structure is blocking a change you can name, and when you can say what must remain true. Anthropic's article says there still has to be a business case. A year of pain in one bottleneck can be a case. "The agent offered" is not.

When the rewrite is the wrong project

Sometimes the architecture feels intolerable and the behavior is fine. The folders are odd because a builder emitted them. The app still takes payments and the paths you listed still pass. A rewrite then has to beat a smaller option: name the one boundary that is blocking the next feature, and extract only that. If the next feature is "add a second plan to billing," you need a billing boundary, not a new folder layout for the marketing pages.

Ask what the rewrite is for in one sentence a customer would recognize. "So the code looks like a framework tutorial" is not that sentence. "So a second person can change pricing without breaking checkout" is. If you cannot finish the sentence, do not start the branch. Spend the agent's budget on the characterization tests instead. They make the later rewrite possible and they make today's incidents smaller even if you never rewrite.

A generated app can also be two architectures already: the builder's runtime and a half-finished export. Rewriting both at once strands you with neither working. Get one path running from the repository you control, with the checks above, before you invite an agent to invent a third shape. Portability of the code you already have is a precondition, not a reward you get after the rewrite.

If a previous rewrite is already merged and the product is worse, stop adding steps. Restore the last artifact that matched the judge, or the last release customers could complete a task on, and delete the forward motion until the checks exist. More agent time on a branch that failed its only job will produce a larger branch that fails the same job.

If this is your app, send the repo.

A 30-minute scope call. We read what is live, name what is dangerous, and quote a fixed scope.

How Techtribe refactors an agent-built codebase without losing the product

We pin the behaviors that users already depend on, then change structure in steps that keep those checks green. We do not take a generated app and ask a model to replace it over a weekend. If the honest outcome is that one path should be repaired and the rest left alone, we will say that. A rewrite is a scope, not a default.

A defined rebuild with tests, monitoring, and an architecture someone can operate is a Production MVP. A product that keeps changing every week, with an agent in the loop, needs a Dedicated Team so the judge stays intact after we leave the call. Bring the repo and the one behavior you are afraid a refactor would break. If you do not know which behavior that is, that is the first thing the call is for.

The fixed scope for taking a generated app to production is a page per tool: Lovable, Bolt, v0, and Replit.

Questions

Can an agent safely rewrite a whole project?

It can move a lot of code quickly. Anthropic's migration write-up describes large ports that passed their tests and still had regressions after merge. Safety comes from a judge that fails when behavior changes, not from the size of the diff or the confidence of the summary.

What should exist before the agent edits the structure?

A statement of the behavior that must survive, checked by tests or a parity harness the agent does not get to weaken, and a rule for the structure you want. Land those before the refactor. A green suite the agent can edit will go green for the wrong architecture.

Why not one big pull request?

Because you cannot roll it back, review it, or deploy it as a unit once it is mixed with feature work. Small steps that each leave the system working are reversible. A single rewrite is a new product wearing the old name.

Is a language port the same as tidying a generated app?

No. A port of a tested codebase has an old version to compare against. A generated app often has no tests and no one who can say which quirks are load-bearing. Tidying that without pinning behavior is how working paths disappear.

Book a free call

Have a vibe-coded app in production?

30-minute free call. We'll read what you have, name what is dangerous, and quote a fixed scope.