all postsAugust 4, 2026

Picking up where AI left off

Somebody generated it, shipped it, and moved on. Now it is yours. A practical approach to taking ownership of a system that was built faster than it was understood.

maintenanceaiengineering

We are seeing a new kind of legacy system. It is not old. It might be six months old. It works, mostly. It has tests, sort of. And nobody on the team can explain how it fits together, because most of it was generated in a burst of productivity by someone who has since moved to another project, or because the whole thing was built by an agent with a human occasionally clicking accept.

This is the maintenance bill for AI assisted development, and it comes due. Here is how we approach it.

Start by finding out what is actually true

Generated systems tend to have documentation that describes what the author asked for rather than what was built. Treat the README as a hypothesis. Then:

  • Run everything. Build it, run the tests, run the app. Note what fails. A system that has been "working" for months often has a test suite that was quietly disabled somewhere along the way.
  • Map the boundaries. Identify every place the system talks to something else: databases, APIs, queues, files, other services. Generated code is most likely to be subtly wrong at boundaries, because that is where the model had to guess an interface.
  • Find the duplicates. Models do not remember what they wrote last week. Expect three slightly different implementations of the same helper, each with its own bug.
  • Read the tests as documentation. Generated tests often encode what the model thought the code did, which is sometimes a better specification than the comments.

Decide what to keep

Not everything needs to be rewritten. The goal is ownership, not purity. For each significant module, ask three questions: Does it work? Can someone on the team explain it? Would we be comfortable changing it? A module that passes all three is fine as it is. A module that fails the second question needs a design note before anything else. A module that fails the third is a candidate for a rewrite, and only then.

Put the tests you trust in place first

Before changing anything, build a verification net you believe in. Characterization tests that capture current behavior, even the behavior you suspect is wrong, give you a baseline. Once you have it, every change is measurable. This is the step teams skip when they are in a hurry, and it is the step that turns a scary codebase into a normal one.

Where the existing tests are shallow, mutation testing will tell you quickly. If you can delete a line of logic and the suite stays green, the suite is not protecting that line.

Write the design notes the original author did not

For each module you decide to keep, write the paragraph that should have shipped with it. What it does, what it assumes, where its edges are, what would break if the assumption changed. This is where the model can help: ask it to explain the code, then correct it. The correction is where your understanding forms.

Keep the notes next to the code. A design note in a wiki nobody opens is not a design note.

Remove what does not earn its keep

Generated code carries a lot of hedging: defensive checks for conditions that cannot occur, configuration for options nobody uses, abstractions introduced for flexibility that never arrived. Every line you delete is a line you no longer maintain. Be aggressive, and let the verification net tell you when you went too far.

Fix the process, not just the code

If the system got this way once, it will get this way again unless something changes. The process changes that matter are small:

  1. Every generated change has a human author of record who can explain it.
  2. A different person reviews it, and the review shows evidence of reading.
  3. Nothing ships without a design note a newcomer could follow.
  4. Tests that demonstrate a requirement are written by a person.

None of this slows a team down much. It slows the generation down to the speed of understanding, which is the speed the team was always going to have to move at eventually.

Why we are comfortable with this work

Taking over a system you did not write is ordinary engineering. It has always been most of the job. What is new is the volume, and the fact that the original author may not have understood the code either. That makes engineering judgment more valuable, not less. Being able to build the system from scratch is what lets us decide, module by module, whether to keep, repair, or replace, and to do it with confidence rather than hope.