toolhq.io

skills / code quality and testing

refactor-safely

Change structure without changing behaviour, in steps that can each be verified and reverted.

SKILL.md

Save as .claude/skills/refactor-safely/SKILL.md in a repository, or under ~/.claude/skills/refactor-safely/SKILL.md to have it everywhere. The copy button takes the whole file, frontmatter included.

---
name: refactor-safely
description: Restructure code without changing what it does, in steps that can each be verified and reverted. Use when asked to refactor, clean up, simplify, extract, rename, split a large file or reduce duplication.
---

# Refactor safely

A refactor changes structure and nothing else. The moment behaviour changes, it
is not a refactor, it is a rewrite, and it needs review and tests as a change.

Keep the two apart. If a bug surfaces mid refactor, note it and fix it in a
separate change, so the diff never mixes "moved this" with "fixed this".

## Before touching anything

1. **Check there is a safety net.** Run the existing tests and confirm they
   pass. If the code has no tests, write a characterisation test first: one
   that captures what the code currently does, including behaviour that looks
   wrong. Without it you cannot tell a successful refactor from a quiet break.
2. **Find every caller.** Grep for the function, the class, the config key, the
   route. Include tests, documentation and anything dynamic such as a name
   built from a string, which grep will miss and which breaks at runtime.
3. **Say what you are not changing.** Naming the boundary keeps the diff
   honest.

## Work in small steps

Each step should be independently correct, so the tests pass after every one
and any step can be reverted alone.

A workable sequence:

1. Add the new thing beside the old one.
2. Move one caller across. Run the tests.
3. Move the rest.
4. Delete the old thing.
5. Run the tests again.

Resist doing all five in one commit. The value of the sequence is that when
something breaks you know which step did it.

## Order by risk

Safest to riskiest, so stop wherever the value runs out:

1. Delete dead code. Confirm it is dead first, including dynamic references.
2. Rename for clarity, with the tool rather than find and replace.
3. Extract a function or constant from a repeated expression.
4. Change a signature, which touches every caller.
5. Change a data structure, which touches the shape of everything downstream.
6. Move code across a module boundary, which can change load order and cycles.

## Traps

- **Three similar blocks are not always duplication.** If they are likely to
  change for different reasons, merging them creates a function with three
  flags. Duplication is cheaper than the wrong abstraction.
- **Extracting too early.** One caller does not need an abstraction. Wait for
  the second, preferably the third.
- **Renaming across a public boundary.** An exported name, an API field or a
  database column is a contract. Changing it is a breaking change wearing a
  refactor's clothes.
- **Tests that mirror the implementation.** If a behaviour preserving change
  breaks the tests, the tests were asserting structure. Fix them, and say so,
  because it means they were not protecting anything.
- **Reformatting in the same commit.** It buries the real change. Format
  separately.

## Know when to stop

Refactoring has no natural end. Stop when the thing you came to do is done.
"While I was in there" is how a two file diff becomes forty and stops being
reviewable.

If you find more worth doing, list it rather than doing it.

## Report back

1. What you changed structurally, and what behaviour is unchanged.
2. The steps, in order, so a reviewer can follow them.
3. Confirmation the tests passed before and after.
4. Anything you found and deliberately left alone.

what it needs

Nothing. This skill is instructions only, so it works in any agent that reads SKILL.md with nothing installed and no account. Save the file and it is live in the next session.

the instructions, in full

Refactor safely

A refactor changes structure and nothing else. The moment behaviour changes, it is not a refactor, it is a rewrite, and it needs review and tests as a change.

Keep the two apart. If a bug surfaces mid refactor, note it and fix it in a separate change, so the diff never mixes "moved this" with "fixed this".

Before touching anything

  1. Check there is a safety net. Run the existing tests and confirm they pass. If the code has no tests, write a characterisation test first: one that captures what the code currently does, including behaviour that looks wrong. Without it you cannot tell a successful refactor from a quiet break.
  2. Find every caller. Grep for the function, the class, the config key, the route. Include tests, documentation and anything dynamic such as a name built from a string, which grep will miss and which breaks at runtime.
  3. Say what you are not changing. Naming the boundary keeps the diff honest.

Work in small steps

Each step should be independently correct, so the tests pass after every one and any step can be reverted alone.

A workable sequence:

  1. Add the new thing beside the old one.
  2. Move one caller across. Run the tests.
  3. Move the rest.
  4. Delete the old thing.
  5. Run the tests again.

Resist doing all five in one commit. The value of the sequence is that when something breaks you know which step did it.

Order by risk

Safest to riskiest, so stop wherever the value runs out:

  1. Delete dead code. Confirm it is dead first, including dynamic references.
  2. Rename for clarity, with the tool rather than find and replace.
  3. Extract a function or constant from a repeated expression.
  4. Change a signature, which touches every caller.
  5. Change a data structure, which touches the shape of everything downstream.
  6. Move code across a module boundary, which can change load order and cycles.

Traps

Know when to stop

Refactoring has no natural end. Stop when the thing you came to do is done. "While I was in there" is how a two file diff becomes forty and stops being reviewable.

If you find more worth doing, list it rather than doing it.

Report back

  1. What you changed structurally, and what behaviour is unchanged.
  2. The steps, in order, so a reviewer can follow them.
  3. Confirmation the tests passed before and after.
  4. Anything you found and deliberately left alone.