The Refactoring Agent

Claude Code Agents: Fundamentals

Chapter 8 · The Refactoring Agent

Every agent covered so far succeeds by producing something new or catching something wrong. A refactoring agent has a stranger, almost inverted goal: success means the code's observable behavior stays exactly, provably the same, while its internal structure changes. This chapter covers designing an agent for that specific, unusual discipline.

What Makes Refactoring a Different Kind of Task

Chapter 5's coding agent succeeds by producing new, correct behavior. A refactoring agent succeeds by producing no behavior change at all — only a structural one (renaming, extracting a shared function, simplifying convoluted logic). This constraint changes what "done correctly" even means: it's not enough for the new code to look cleaner or work correctly in isolation — it has to be provably indistinguishable, from the outside, from the code it replaced.

Designing a Refactoring Agent's Definition

"Use this agent to restructure or clean up existing code — renaming, extracting shared logic, simplifying convoluted conditionals — without changing what the code actually does. Not for adding features or fixing bugs; use a coding agent for that." Tool access looks similar to the coding agent's own — Read, Edit, Grep, Glob, and Bash — but Bash here serves a specifically different purpose: confirming behavior didn't change, not just confirming new behavior works.

Writing the System Prompt for a Refactoring Agent

A refactoring agent's system prompt should instruct it to:

  • Run the existing test suite before making any change, establishing a baseline of current behavior
  • Make the structural change without altering any public-facing behavior
  • Run the exact same test suite after the change and confirm identical results
  • If the code being touched has no adequate existing test coverage, flag this explicitly rather than proceeding as though the absence of failing tests proves nothing broke

Briefing a Refactoring Agent Well

A good brief states what to restructure and why (reducing duplication, improving readability, isolating a pattern), and — critically — names explicit boundaries on what must not change, especially any exported or public-facing function signature that other code elsewhere depends on. It should also state what test coverage already exists for the code being touched, since that coverage is literally what the "before and after" comparison depends on.

A Worked Example

"Extract the repeated validation logic duplicated across the three functions in src/forms/signup.ts, login.ts, and resetPassword.ts into a single shared helper function. The exported public function signatures in all three files must not change — only their internal implementation. Run the existing form-validation test suite before and after the change to confirm identical behavior." This gives the agent a specific structural goal, an explicit boundary (unchanged public signatures), and a concrete way to verify nothing else changed (the existing test suite, run both before and after).

AspectCoding Agent (Ch.5)Refactoring Agent
GoalProduce new, correct behaviorChange structure while behavior stays identical
Success criterionThe new feature works as specifiedBefore/after test results are identical
Bash's roleVerify new behavior worksConfirm behavior didn't change at all
Check test coverage before refactoring, not after
The "before and after" comparison a refactor depends on is only as trustworthy as the tests it's measured against. If the code being touched has thin or no existing coverage, confirm adequate coverage exists first — potentially delegating to a testing agent (Chapter 7) beforehand — rather than refactoring first and hoping nothing important was missed.
"Tests still pass" only covers what the tests actually check
Exactly as Chapter 7 warned for testing generally, a refactor confirmed only by "the existing tests still pass" has only been verified against whatever behavior those tests happen to check — not against every behavior the code actually has. A refactor that subtly changes an untested edge case can pass every existing test while still having genuinely altered real behavior somewhere nobody thought to check.

Hands-On Exercises

Exercise 1

Explain how "success" is defined differently for a coding agent (Chapter 5) versus a refactoring agent, and why that difference changes what each agent's own Bash access is actually used for.

📄 View solution
Exercise 2

A refactoring agent is asked to simplify a function that has no existing tests at all. Explain what it should do before making any structural change, using this chapter's own guidance.

📄 View solution
Exercise 3

A refactor is completed, and the existing test suite passes identically before and after. The developer concludes the refactor definitely introduced no behavior change anywhere. Using this chapter's own warning box, explain what's wrong with that conclusion.

📄 View solution

Chapter 8 Quick Reference

  • A refactoring agent's goal is no observable behavior change — only structure changes
  • Run the test suite before and after to establish and confirm a behavior baseline
  • Flag missing test coverage before refactoring rather than proceeding without a way to verify nothing changed
  • A good brief states what to restructure, why, and which public-facing signatures must not change
  • Bash's role here is confirming behavior didn't change — a different purpose than a coding agent's own use of it
  • Identical before/after test results only cover what those tests actually check, not every possible behavior