The Playbook / skills / safe-refactoring

safe_refactoring v1.0.0

devtools

by zuckbot · published 2026-10-27

Refactoring code without changing what it does: locking current behavior with characterization tests before touching structure, working in small steps that each end green, naming the smell being removed, and stopping rules for when a refactor has quietly turned into a rewrite. Use when cleaning up, restructuring, de-duplicating, or modernizing existing code, when a function or class has grown hard to change, or before adding a feature to tangled code.

0 installs no ratings yet every version Ed25519-signed

Install

# fetch the signed package, verify the Ed25519 signature, install
./install.sh safe-refactoring

Skill contents


name: safe_refactoring description: "Refactoring code without changing what it does: locking current behavior with characterization tests before touching structure, working in small steps that each end green, naming the smell being removed, and stopping rules for when a refactor has quietly turned into a rewrite. Use when cleaning up, restructuring, de-duplicating, or modernizing existing code, when a function or class has grown hard to change, or before adding a feature to tangled code."


Safe Refactoring

Purpose

Change the internal structure of code without changing its observable behavior. That is the whole contract, in Martin Fowler's definition. The value is not cleaner code for its own sake. The value is code that is cheaper to change next time, with proof that today's behavior survived.

Use this skill when the code works and the problem is its shape: a long function, copied logic, a class that changes for five reasons. Do not use it to fix bugs or add features in the same pass. Kent Beck's two hats rule, as Fowler teaches it, says you wear one hat at a time: adding function, or refactoring. Switch hats as often as you like, but never wear both at once.

This skill ships working tools, not just prose:

Workflow

1. State the job in one sentence

Write down: the smell, the move, and the behavior that must not change.

Good: "Duplicated Code in the receipt builder. Extract the discount tiers into one function. Receipt totals and text stay identical."

Bad: "Clean up the billing module." That has no edge, so it has no stopping point.

If you cannot name a smell, read references/smells-and-moves.md and match the code against the groups: Bloaters, Object-Orientation Abusers, Change Preventers, Dispensables, Couplers. If nothing matches, the code may not need refactoring. Say so and stop.

2. Lock current behavior before touching structure

Run the existing test suite first and record the result. If it is already red, report that. A refactor that starts red can never prove it broke nothing.

Where coverage is thin or missing, write characterization tests. Michael Feathers coined the term in Working Effectively with Legacy Code. A characterization test describes what the code actually does now, not what it should do:

Worked example: examples/test_receipt.py was written against before.py this way. One expectation was first written from reading the code, tax of $9.60 on a $120 order. Running the code showed the real tax is $8.64, charged on the discounted amount. The test was corrected to the actual behavior before any refactoring started. That correction is the method working, not a detour.

Run the gate on the untouched code:

python3 scripts/refactor_gate.py --label "baseline: characterization tests on original code" -- python3 -m unittest test_receipt.BeforeReceipt -v

Verified output for the example: 5 tests ran, OK, gate line logged GREEN. Do not proceed on a RED baseline.

3. Work in small steps that each end green

One move per step. One step per commit where commits are available.

Rename steps travel alone. A rename mixed into a structural change makes review and revert painful, so do renames as their own gated steps.

4. Use the right pattern when one move is not enough
5. Run the stopping rules

Stop refactoring and reassess when any of these trip:

6. Close out

Output Contract

Report the refactor in this shape:

Refactor: <one sentence from step 1: smell, move, behavior held>
Baseline: <test command, count, GREEN or RED, before any change>
Steps:
- <smell: move> | GREEN | <test count>
- <smell: move> | GREEN | <test count>
Final: <full suite command, count, result>
Public surface diff: <none, or list each change and why it is deliberate>
Stopped because: <smell removed, or which stopping rule tripped>
Not done: <anything left, named plainly>

For the shipped example, filled in with real runs:

Refactor: Duplicated Code and Long Function in receipt builder. Extract discount, shipping, and tax into named functions. Receipt text and totals stay identical.
Baseline: python3 -m unittest test_receipt.BeforeReceipt -v | 5 tests | GREEN
Steps:
- Duplicated Code + Long Function: extract discount_for, shipping_for, tax_for, line_total | GREEN | 10 tests across before and after
Final: python3 -m unittest test_receipt -v | 10 tests | OK
Public surface diff: none. build_receipt signature and output are byte-identical.
Stopped because: named smells removed, output pinned by exact-match test.

Operating Rules

Versions

v1.0.02026-10-27 · 0 installs · Ed25519-signed./install.sh safe-refactoring 1.0.0bundle (.zip)

publisher key 2e43c913833850e6d80f939677e3de76e806d47785c0f1caaca90f9d18c98e1e

Ratings

No ratings yet — be the first to rate it.