Skip to main content
Harden/Chapter 57 · Refactoring

Paying Down AI Debt

Share

Share this page

Pass it to someone who needs it.

Your app works, but the code behind it has quietly bloated. Every feature you asked for, the agent answered with more structure than the job needed: an extra layer, an abstraction nobody calls twice. The codebase is harder to move through now, and you are not always sure which parts even run. This chapter gets you cleaning it up safely, by deleting more than you add.

8.5.1Improve without changing behavior

Refactoring is improving the structure of code without changing what it does. The behavior your users see stays identical; only the shape underneath gets clearer. Martin Fowler named the practice and gave it one core discipline: change structure and behavior in separate steps, never both in the same move.

This is why tests come first. The suite that pins your app's behavior down is what lets you reshape the code without fear: change the structure, run the tests, and if they stay green, only the shape moved.

8.5.2Delete more than you add

A human refactor usually means adding structure: extracting a shared function, adding a layer that was missing. With an agent the debt runs the other way. It already over-produced, so your cleanup is mostly subtraction, and every line you delete is a line you never have to debug.

Hunt for and delete:

  • dead code and branches that never run
  • options, parameters, and config nothing sets
  • duplicate helpers doing the same job
  • abstraction built for a second caller that never arrived

Rule of thumb: if you cannot say in one sentence why a layer exists, delete the layer, not the sentence.

8.5.3Simplify what the agent overbuilt

Deletion removes what is unused. Simplification shrinks what is used but overbuilt. The agent reaches for enterprise patterns on a problem that wants a plain function: a factory to build one object, an interface with a single implementation. Here it was asked only to join a first and last name.

// The agent's version: a factory, an interface, and a
// config object, all to join two strings.
interface NameFormatter {
  format(user: User): string;
}

class DefaultNameFormatter implements NameFormatter {
  constructor(private config: FormatConfig = {}) {}
  format(user: User): string {
    const sep = this.config.separator ?? " ";
    return [user.first, user.last].filter(Boolean).join(sep);
  }
}

export function createNameFormatter() {
  return new DefaultNameFormatter();
}
// Same behavior. Nothing else set the separator, so
// it is gone too. One function does the whole job.
export function fullName(user: User): string {
  return [user.first, user.last].filter(Boolean).join(" ");
}

Same inputs, same output, a quarter of the code and one obvious place to change it. Have your agent run that same pass on your worst file:

Act as a senior engineer refactoring for simplicity,
not adding features. Behavior must not change.

First, state in one sentence what this code does, so
we have a fixed target to preserve.

Then find everything safe to remove:
- dead code and branches that never run
- options, params, and config nothing sets
- duplicate helpers that do the same job
- factories, interfaces, or strategies with a
  single implementation

Propose the smallest version with identical behavior.
Show a before/after diff and say what you removed and
why removing it was safe.

Do not add features, comments, or new abstraction. If
tests exist, keep them green; if not, tell me what to
test before I delete anything.

The code that feels overbuilt:

Do this now: open the file in your app you least want to touch, run the prompt on it, and let your agent show you a smaller version that keeps every test green.

Discussion

Questions, ideas, and feedback on this chapter.

Mahmoud Zalt

Mahmoud Zalt

Software engineer, 16+ yrs · built Sistava.com in 3 months, idea to production, using these methods

ExploreBook a call
Companion RepoContribute
Support me

Support my work

A small tip keeps the free work coming.

© 2026 Mahmoud Zalt. Free to read, not to republish. Copyright & license