From code-foundations
Guides refactoring untested legacy code using characterization tests, sprout/wrap techniques, dependency breaking, pinch points, and effect sketching. For test harness issues, time pressure, or fear of breakage.
npx claudepluginhub ryanthedev/code-foundationsThis skill uses the workspace's default tool permissions.
> "Legacy code is simply code without tests." - Michael Feathers
Guides safe refactoring of untested legacy code using RGR workflow and characterization tests. Use when modifying code without test coverage.
Modernizes legacy codebases via characterization tests, golden master testing, incremental refactoring, dependency upgrades, and dead code removal. Activates on tech debt, untested code, or deprecated deps.
Refactors legacy codebases incrementally to modernize patterns, reduce technical debt, and improve maintainability while preserving functionality. Use for outdated code or deprecated APIs.
Share bugs, ideas, or general feedback.
"Legacy code is simply code without tests." - Michael Feathers
Get code under test before changing it. This is the core principle.
| Symptom | Solution |
|---|---|
| Can't get class into test harness | Decision Tree: Can't Instantiate |
| Can't run method in test harness | Decision Tree: Can't Call Method |
| Don't know what tests to write | Characterization Tests |
| Under time pressure, need to change now | Sprout/Wrap Techniques |
| Need to change many classes in one area | Pinch Point Testing |
| Class is too big / monster method | Effect Sketching → find clusters |
| Afraid I'll break something | Single-Goal Editing, Preserve Signatures |
Use this for every change to untested code.
1. IDENTIFY change points
- Where in the code do you need to make modifications?
2. FIND test points
- Where can you write tests to cover the change?
- Look for pinch points where tests cover multiple changes
3. BREAK dependencies
- Remove obstacles that prevent testing
- Use dependency-breaking techniques (catalog below)
4. WRITE characterization tests
- Document what the code DOES, not what it SHOULD do
- Process: write assertion you know will fail → let failure
tell you actual behavior → change test to match
5. MAKE changes and refactor
- Implement the change with test safety net
When you can't test everything but need to change code now:
These keep new code separate and testable even when old code isn't.
When you don't know what tests to write:
You're documenting what the code does, not what it should do. This is the test safety net for refactoring.
When you need to change many classes in one area:
| Technique | When to Use |
|---|---|
| Adapt Parameter | Parameter class hard to fake |
| Break Out Method Object | Long method with local state |
| Encapsulate Global References | Global variables block testing |
| Expose Static Method | Method uses no instance state |
| Extract and Override Call | Single problematic call |
| Extract and Override Factory Method | Constructor creates dependencies |
| Extract and Override Getter | Instance variable holds problem |
| Extract Implementer | Turn class into interface |
| Extract Interface | Need to fake a class |
| Introduce Instance Delegator | Static method blocks testing |
| Introduce Static Setter | Singleton blocks testing |
| Parameterize Constructor | Constructor creates its dependencies |
| Parameterize Method | Method obtains dependency internally |
| Primitivize Parameter | Parameter hard to create, only data needed |
| Pull Up Feature | Test feature in isolation |
| Push Down Dependency | Separate testable logic from dependencies |
| Replace Global Reference with Getter | Global variable access |
| Subclass and Override Method | Method behavior blocks testing |
| Supersede Instance Variable | Replace after construction |
Is the problem in the constructor?
├── YES: Does constructor CREATE objects?
│ ├── YES: Parameterize Constructor or Extract and Override Factory Method
│ └── NO: Does constructor USE passed objects?
│ ├── YES: Extract Interface on parameter, pass fake
│ └── NO: Hidden dependency — find the `new` and externalize it
└── NO: Is it a singleton/global?
├── YES: Introduce Static Setter
└── NO: Include/import dependency chain
└── Extract Interface or Extract Implementer
Is the method private?
├── YES: Make protected, create testing subclass
└── NO: Is a parameter hard to create?
├── YES: Is parameter sealed/final?
│ ├── YES: Adapt Parameter
│ └── NO: Extract Interface from parameter
└── NO: Does method have invisible side effects?
└── YES: Extract side effects to separate methods, override in test
Can you test the new code in isolation?
├── YES: Sprout Method or Sprout Class
└── NO: Does new behavior go before/after existing?
├── YES: Wrap Method
└── NO: Can you not modify the class at all?
└── YES: Wrap Class (Decorator)
| After | Next |
|---|---|
| Code under test | cc-refactoring-guidance (safe refactoring process) |
| Dependencies broken | Continue with Legacy Code Change Algorithm step 4-5 |