Legacy Modernization & Migrations

Vibe Coding Cleanup: Is Your Prompt-Built App Already Legacy Code?

Prompt-built code ships fast but arrives without the record of why it works. Here is a four-phase takeover plan: audit it, pin its behavior with characterization tests, prune in small diffs, and guard against drift.

Legacy Modernization

Stone Age Software cover reading Vibe Coding Cleanup: Is Your Prompt-Built App Already Legacy Code?, with tags for audit, characterization tests, small diffs and guardrails.

A founder ships a working product in two weeks with an AI coding assistant. Customers sign up. Then real traffic arrives, checkout times out twice a day, and an engineering team inherits the repository. They find three date libraries in one Node.js backend, two ways of talking to the database, and no commit message that explains why. This is where vibe coding cleanup starts. It rarely starts with writing new code.

The timing matters. GitHub's Octoverse 2026 report says agents drafted more than 40 percent of pull requests by September, up from 17 percent in March. A week earlier, Gartner named its technology trends for 2027. Its analysts warned of an "architectural vacuum," with software generated faster than people can review it, as TechTarget reported.

The frame that holds up is simple. Generated code is legacy code on the day it lands. It is fast to write and slow to change. The team taking it over has to stop generating and start doing forensics.

Why generated code is legacy on day one

Michael Feathers defined legacy code as code without tests. A wider reading fits here: legacy code is code whose reasons you cannot recover. A repository built by a team carries memory. Commit messages explain changes. Review threads record the arguments. Decision records say why one database won. A prompt session leaves the code and little else. Nobody did anything wrong. The process just never writes down why.

That gap shows up in four predictable ways:

  • No recorded intent. Each session starts fresh, so constraints never reach the repository.
  • Plausible boundary bugs. The happy path works. The edges fail: an invented API parameter, a missing lock, a transaction that commits half an order. A 2026 GitClear analysis covered by LeadDev found error-masking patterns, like catch blocks that swallow bad input, up 47 percent.
  • Helper bloat and pattern drift. Isolated sessions re-solve solved problems. Across 623 million changes, the same analysis found duplicated code blocks up 81 percent and refactoring moves down 70 percent.
  • Phantom dependencies. Models sometimes suggest packages that do not exist. A 2026 re-test of five frontier models found 4.6 to 6.1 percent of suggested packages were not real. Open-source models averaged 21.7 percent in the original 2025 study. The re-test found 127 invented names that all five models shared, and 53 were still open for anyone to register.

The smells differ from the ones senior engineers already know:

| Human-built codebase | Prompt-built codebase | |---|---| | Over-abstracted inheritance | Flat procedural logic copied across unrelated files | | One clever helper nobody can read | Five similar helpers nobody knew existed | | Stale comments that lie | Confident comments about code that is not there | | A dependency pinned years ago | A dependency that never existed | | Patterns that drift over years | Repository and Active Record mixed in one sprint |

Comparison of what a team-built repository records, from commit reasons to decision records, against a prompt-built repository that usually holds little beyond the code.

Phase 1: audit before you run anything

The first pass is read-only. You are drawing a map, not fixing anything.

Four-phase plan for taking over generated code: audit, lock behavior, prune in small diffs, and guard against drift.
  • Inventory the supply chain. Generate a software bill of materials. CISA's 2026 Minimum Elements for an SBOM now lists a component hash, license, generating tool, and generation context. Run a scanner such as Trivy or Snyk against it. Confirm each package exists, has a maintainer, and shipped a recent release.
  • Scan for security holes. Run static application security testing (SAST). Generated code compiles nearly every time. In Veracode's 2026 testing, it passed security checks only 56 percent of the time.
  • Map the redundancy. Run a copy-paste detector such as jscpd and a dependency graph tool such as madge. Flag circular imports, duplicate utilities, and mixed naming. Rank functions by cyclomatic complexity.
  • Recover the prompts. Check the repository root and docs for a .cursorrules file, AGENTS.md, copilot-instructions.md, or exported chat logs. A prompt file is the closest thing this codebase has to a design document.

Phase 2: lock behavior before you change it

The golden rule: never refactor generated code based on what you think it does. Verify what it actually does.

  • Write characterization tests. Feathers coined the term for tests that record current behavior, right or wrong. Start with sign-in, billing, and every write to core data. Snapshot the outputs. When one of these tests fails later, behavior changed, and a person decides whether that was the point.
  • Cover the money paths end to end. One browser-level test per revenue path catches what unit tests miss.
  • Add observability first. Structured logs, traces, and per-route latency, with alerts on error rate and p95, expose silent failures before a customer reports them. A query that runs once per order line looks fine in a demo and falls over at peak.

We did this for an internal platform that ran a whole site and reported nothing about itself. The platform observability project times every request, records each background job, and shows p95 latency per route on one screen. On an inherited codebase, that screen becomes your baseline.

Phase 3: prune first, then refactor in small diffs

Deleting code is the highest-leverage change on a prompt-built repository. Our rule of thumb is to plan for removing a quarter to a third of it. Most of that is duplicate helpers, dead endpoints, and packages nothing calls. Start with the easy kill: a formatDate, a formatTimestamp, and a dateHelper that do the same job become one function with one test.

  • Keep each pull request to one intent. Aim for diffs under 200 to 300 lines. A small diff lets a reviewer catch the regression a cleanup tool slipped in.
  • Write invariants down as tests. An order never ships a negative quantity. A refund never exceeds the charge. Then split the 600-line handler into modules with explicit interfaces around those rules.

Before touching a module, decide whether to refactor, wrap, or rewrite it. Score it one point per yes:

  1. Characterization tests pin its current behavior.
  2. You can state each of its invariants in one sentence.
  3. Its duplication stays inside its own files.
  4. Every dependency exists, has a maintainer, and passes the scanner.
  5. Callers reach it through one clear entry point.

Four or five points: refactor in place. Two or three: wrap it behind an interface and replace it in pieces. Zero or one: rewrite it against fresh characterization tests if it is small, and wrap it first if it is not.

Five-question rubric that scores an inherited module and routes it to refactor in place, wrap and replace in pieces, or rewrite against characterization tests.

Phase 4: guardrails that keep the drift out

Generated code is not the problem. Ungoverned generation is. Treat the assistant as a fast junior contributor whose work gets senior review.

  • Tests first. New generated code must pass a test that a person wrote or approved before the code existed.
  • Labeled diffs. Pull requests flag generated changes and link the prompt or task behind them.
  • CI quality gates. Lint rules, type checks, a coverage floor on changed lines, and a dependency allowlist that blocks unknown packages.
  • Architecture rules in the build. Encode the layering as a failing check. No database calls from route handlers means none, whoever or whatever wrote the diff.
  • Decision records. Michael Nygard's original ADR format fits on one page: context, decision, status, consequences. Ten minutes now saves a week of archaeology later.

Gates catch bad code. Safe releases catch the rest. Our release engineering work keeps the old version serving when a new deploy fails. None of this replaces engineers. It frees them for the judgment calls and exceptions that generated code cannot make.

Prompts that audit instead of "clean up"

Asking a model to "clean up this file" invites a confident rewrite that nobody can review. Ask it to explain and test instead:

  • "List every invariant this function enforces, and cite the line for each."
  • "Write characterization tests for this module's current behavior, including edge cases it handles badly. Do not change the code."
  • "Find every helper in this repository that formats or parses dates, and show their signatures side by side."
  • "Which branches in this function does no test reach?"

Treat every answer as a lead to verify, not a finding.

A vibe coding cleanup check you can run this week

Pick the one path where money moves, usually checkout or billing. Answer four questions with tools, not memory:

  1. How many helpers do the same job on that path? Search for date and currency formatting, then count.
  2. Does every package it imports exist, have a maintainer, and show a release in the past year?
  3. Is there at least one characterization test on that path today?
  4. Can anyone point to where its design decisions are written down?

Two wrong answers mean the repository is legacy, whatever its age. Baseline it before the next feature ships.

That baseline is the same discipline we bring to green-screen systems and decades-old ERPs. Our legacy modernization and migration practice reads and documents a system before proposing a change, then replaces it piece by piece, fixed price, quoted up front.

Where this fits

This is part of our Legacy Modernization & Migrations work. Bring decades-old systems into the modern era without a risky rip-and-replace.

← All articles

Keep reading

Further reading on legacy modernization

Legacy Modernization · · 9 min read

The Stone Age Software Playbook: How to Modernize 20-Year-Old Code with AI (Without Breaking Production)

Cisco just gave one engineer unlimited tokens to refactor 98% of its legacy code. Here is the safer order for revenue-critical systems: read first, pin behavior with characterization tests, then strangle the monolith one routed seam at a time.

Read the article →
Legacy Modernization · · 6 min read

Legacy ERP Carrier Integration: Can an Old System Handle Modern Carrier Logins?

Carriers now issue logins that expire within hours, and many older ERPs were built for a password that never changes. A small service beside the ERP handles the modern connection, so nothing gets ripped out.

Read the article →
Legacy Modernization · · 6 min read

Custom Software Application or Software Customization: What Do You Own?

Building a custom software application and customizing a platform you subscribe to leave you owning very different things. Here is how code, data, accounts and upgrades split in each path, what the contract has to say, and a seven-item ownership check.

Read the article →
Get in touch

Got the same problem in your shop?

Describe it in a few lines — you'll get an engineer's read on it, and a free assessment if it's worth one.

Prefer to talk?

Call 832-598-8234 or email msco@stoneagesoftware.com. Houston, Texas — serving Houston, The Woodlands, Conroe, Sugar Land, Katy, Pearland, and the Greater Houston metro.

What happens next

We read your message, an engineer replies with questions or a straight answer, and if a call would help, we book one. From the first reply on, you're talking to the people who'd do the work.

No obligation — we reply within one business day. This form sends your details to Stone Age Software LLC so we can respond to you. See our privacy policy for how we handle them. This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.