Safe Refactor

Enables test-driven refactoring with automatic rollback on test failure. Each change is isolated, tested, and committed atomicall…

DrakeCaraker 3 updated 23d ago
Claude CodeGeneric
View source ↗
# /safe-refactor — Test-Gated Refactoring with Automatic Rollback

Enables test-driven refactoring with automatic rollback on test failure. Each change is isolated, tested, and committed atomically. If tests fail, the change is rolled back and an alternative approach is attempted.

## Usage

/safe-refactor


**target**: module path, file path, or description

**Examples**:
- `/safe-refactor dash_shap/utils/thread_budget.py`
- `/safe-refactor checkpoint system`
- `/safe-refactor consensus.py`

## Implementation

Safe refactoring follows three phases: **Characterize**, **Refactor**, and **Cleanup**. Each phase has explicit stopping conditions and rollback procedures.

---

## Phase 1: Characterize (Read-Only — No Source Changes)

Characterization captures the **current behavior** of the target before any refactoring begins. This creates a regression tripwire: if a refactoring change breaks existing behavior, tests will fail immediately and trigger automatic rollback.

### 1A. Create a new branch

```bash
git checkout -b refactor/<target-name-slugified>

Slugify the target: lowercase, spaces → hyphens, slashes → hyphens.

Examples:

  • dash_shap/utils/thread_budget.pyrefactor/dash-shap-utils-thread-budget
  • checkpoint systemrefactor/checkpoint-system
  • consensus.pyrefactor/consensus

1B. Read the target thoroughly

Read every file in scope. List all public functions/classes (anything not starting with _).

Identify:

  • Public API surface (functions, classes, constants)
  • Key behaviors and edge cases
  • Current data flows and dependencies

1C. Write characterization tests

File: tests/test_<target_slug>_characterization.py

These tests capture CURRENT behavior — not correctness. They are regression tripwires, not correctness checks. If the code currently has bugs, the tests will reflect those bugs.

Pattern:

"""Characterization tests for <target> — temporary scaffolding, deleted after refactoring."""
import pytest

def test_<function>_current_behavior():
    """Capture current behavior of <function>."""
    from <module> import <function>
    result = <function>(<representative_input>)
    assert result == <actual_current_output>  # captured from running the function

def test_<function>_edge_case():
    """Capture edge case behavior."""
    from <module> import <function>
    result = <function>(<edge_case_input>)
    assert result == <actual_current_output>

For common output types:

  • NumPy arrays: np.testing.assert_array_almost_equal(result, expected)
  • Dicts of arrays: assert on keys and individual array values
  • Model/complex objects: assert on key attributes (result.some_attr == expected_attr)
  • Float values: pytest.approx(result) == expected_float

To get actual current outputs, run:

python3 -c "from <module> import <function>; import json; print(json.dumps(<function>(<input>), default=str))"

Coverage: Every public function/class, happy path + at least one edge case each.

1D. Run characterization tests — ALL must pass

pytest tests/test_<target_slug>_characterization.py -v

STOP condition: If any test fails before any refactoring:

This characterization test failed on the baseline (before any refactoring).
Fix the test to match the actual current output, NOT the code.
Actual output: [show output]
Expected output in test: [show expectation]

Repeat: adjust expected values in tests until all pass. Do not modify source code.

1E. Commit characterization tests

git add tests/test_<target_slug>_characterization.py
git commit -m "test: add characterization tests for <target> refactor"

Phase 2: Refactor (One Change at a Time)

Before starting any changes, write out your complete planned change list as a numbered list. Example:

  1. Rename variable _rp_mod_runner_module in run_experiments_parallel.py
  2. Extract _validate_budget() helper into its own function
  3. Add type hints to public API functions

Only start step 2A after this list is written. Work through the list in order. Phase 2 is complete when all items are checked off.


For EACH planned change, repeat this cycle exactly:

2A. State the change

One sentence describing exactly what you are changing.

Example: "Extract _validate_threshold() into a separate utility function in utils/validation.py."

2B. Apply the change to ONE file only

Make exactly one logical change. If your plan involves multiple files or multiple changes, apply them sequentially in separate iterations, not all at once.

Exception: If the change logically requires creating one new file (e.g., extracting a function into a new helper file), that counts as one logical change affecting two files. Treat both files atomically — rollback both on failure. Do NOT split into two separate commits.

2C. Run characterization tests

pytest tests/test_<target_slug>_characterization.py -v

2D. Commit or Rollback

If tests pass:

git add <modified_file>
git commit -m "refactor: <one-sentence description>"

Continue to the next planned change.

If tests fail:

Rollback immediately. The change broke existing behavior.

Rollback for a modified file:

git checkout -- <file>

Rollback for a newly created file:

  • If the new file was staged: git rm --cached <file> && rm <file>
  • If the new file was NOT yet staged: rm <file>

After rollback, explain what broke:

Rollback: [file] — [error message].
Reason: [explain why the change broke tests].
Alternative: [try a different approach, or ask for human decision].

Then try an alternative approach if one exists.

STOP condition: If the same change fails twice with the same error:

This change conflicts with existing behavior in a way I cannot resolve without changing the public interface.
Conflict: [describe the underlying issue].
Human decision needed: [describe 

Maintain Safe Refactor?

Let people know it's listed here — add the badge (live metrics, light/dark aware) or a plain link to your README or docs.

[Safe Refactor on getagentictools](https://getagentictools.com/loops/drakecaraker-safe-refactor-test-gated-refactoring-with-automatic-rollback?ref=badge)