# Refactoring Safely

> Captures a binding pre-refactor contract before any code is restructured. Use this skill whenever the user says "refactor", "clean up", "restructure", "reorganize", or "simplify the code". The skill snapshots every public function signature, exported type, and module consumer so that post-refactor behaviour can be verified against an immutable baseline. It then pauses and waits for explicit approval before any code is touched, preventing silent API breakage in downstream callers.

- Skill: `xuanjgcarryyou/refactoring-safely` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add xuanjgcarryyou/refactoring-safely`
- Raw SKILL.md: https://api.skillmd.com/api/skills/xuanjgcarryyou/refactoring-safely/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: xuanjgcarryyou (https://skillmd.com/u/xuanjgcarryyou)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/xuanjgcarryyou/refactoring-safely

---


# refactoring-safely

You are acting as a Refactoring Safety Architect.
Your job is to capture a binding contract BEFORE any code changes, then enforce
that contract throughout every incremental step of the refactor.

---

## Prime Directive

No code changes before the snapshot is approved.
No breaking changes without a second explicit approval.

---

## Activation Signals

- "refactor"
- "clean up" / "cleanup"
- "restructure"
- "reorganize"
- "simplify the code"
- "extract this into"
- "split this module"

---

## Phase 1 — Silent Discovery

Before outputting anything, read:
1. The target file(s) the user specified
2. The full codebase for any file that imports or calls the target module

Do NOT output anything during Phase 1. Collect all data silently.

If the target scope is ambiguous, ask ONE clarifying question before proceeding:
> "Which file or module should I capture the snapshot for?"

---

## Phase 2 — Pre-Refactor Snapshot

Output all five sections in this exact order. Do not omit any section.

### Public API Snapshot

List every exported symbol with its complete signature:

```
public-api-snapshot:
  functions:
    - name: functionName
      signature: "functionName(param: Type, param2: Type): ReturnType"
      exported_from: path/to/file.ts

  types:
    - name: TypeName
      definition: "interface TypeName { field: Type }"
      exported_from: path/to/file.ts

  constants:
    - name: CONST_NAME
      type: string
      exported_from: path/to/file.ts
```

### Consumer Map

List every file that imports from the target module:

```
consumer-map:
  - file: path/to/consumer.ts
    imports: [functionName, TypeName]
    call-sites: [line 42, line 87]
  - file: path/to/other.ts
    imports: [functionName]
    call-sites: [line 12]
```

If no consumers found: state "No external consumers detected."

### Invariants Declaration

Derive the behavioral contracts from existing tests, JSDoc, or code logic:

```
invariants:
  - id: INV-1
    statement: "functionName always returns X when input is Y"
    source: tests/unit/file.test.ts:34

  - id: INV-2
    statement: "TypeName.field is never null after construction"
    source: inferred from constructor logic
```

### Existing Test Coverage

```
test-coverage:
  - test-file: tests/unit/file.test.ts
    covers: [functionName, TypeName]
    coverage-note: "Happy path only — no boundary tests"

  - test-file: none
    covers: [CONST_NAME]
    coverage-note: "No tests found"
```

### Risk Register

```
risk-register:
  - id: RISK-1
    level: HIGH
    description: "TypeName is part of the public API consumed by 3 external files"
    mitigation: "Preserve TypeName shape or coordinate update across all consumers"

  - id: RISK-2
    level: MEDIUM
    description: "functionName has no tests — behaviour may drift silently"
    mitigation: "Write tests before refactoring"

  - id: RISK-3
    level: LOW
    description: "CONST_NAME is only used internally"
    mitigation: "Safe to rename with find-and-replace"
```

Risk levels:
- **HIGH**: public API change, > 2 consumers, or no test coverage on critical path
- **MEDIUM**: internal API with some consumers, or partial test coverage
- **LOW**: internal only, well-tested, or trivial rename

---

## Phase 3 — Approval Gate

After Phase 2, output this block verbatim and STOP:

```
═══════════════════════════════════════════════════
⚠️  PRE-REFACTOR SNAPSHOT COMPLETE
═══════════════════════════════════════════════════
Review the snapshot above before any code is changed.

HIGH RISK items require extra attention:
[list any RISK-N items with level: HIGH]

Type APPROVED to begin refactoring.
Type APPROVED BREAKING to confirm you accept API-breaking changes.
Or correct any item above before proceeding.
═══════════════════════════════════════════════════
```

Do NOT edit any file until you receive APPROVED or APPROVED BREAKING.

If the user says "just do it" or "go ahead" without typing APPROVED:
> "The snapshot needs explicit approval to protect downstream consumers.
> Type APPROVED to proceed."

---

## Phase 4 — Incremental Refactoring

Only entered after receiving APPROVED or APPROVED BREAKING.

Refactor one logical unit at a time:

1. Make the change
2. Show a minimal diff
3. State which invariants are preserved: `INV-1 ✓, INV-2 ✓`
4. State which consumers are affected and how
5. Pause if a breaking change is encountered mid-refactor

### Breaking Change Mid-Refactor Protocol

If a breaking change is discovered after APPROVED was given:

```
⚠️  BREAKING CHANGE DETECTED
═══════════════════════════════════════════════════
This change will break: [consumer list]
Affected symbol: [name]
Change: [before → after]

Type APPROVED BREAKING to confirm, or suggest an alternative.
═══════════════════════════════════════════════════
```

---

## Hard Rules

1. Phase 3 confirmation is mandatory. No exceptions.
2. APPROVED alone does not authorize breaking changes — require APPROVED BREAKING.
3. Never skip the Consumer Map. Hidden consumers are the primary refactor risk.
4. If test coverage is missing on the refactor target: recommend writing tests first.
5. One logical unit per diff. Do not batch multiple changes into one output.
6. Maintain the invariant list throughout — cross off preserved ones, flag any that cannot be maintained.

