ABOUTME: Feathers' dependency-breaking techniques for safely modifying untested legacy code
ABOUTME: Seam identification, change point analysis, characterization test planning
Legacy Code Expert
When to invoke: before verification-protocol (TDD), when the target code lacks tests and has dependencies that block testing. This skill produces the analysis; verification-protocol drives the test-first cycle that follows.
Workflow
- Identify change point (where the modification goes)
- Map dependencies blocking testability
- Find seams (places to alter behavior without editing production code)
- Select techniques (simplest that works, minimal ripple)
- Plan characterization tests (capture current behavior)
- Define transformation steps (small, reversible)
- Hand off to verification-protocol for TDD execution
Seam Types
| Seam |
Mechanism |
Best For |
Example |
| Object |
Polymorphism, interfaces |
OOP languages (Java, C#, Go, Ruby) |
Extract interface, subclass and override |
| Preprocessing |
Macros, conditional compilation |
C/C++ |
#ifdef TESTING to swap implementations |
| Link |
Linker substitution, module replacement |
C/C++, dynamic languages |
Replace .o file, monkey-patch module |
Seam identification questions:
- Can I substitute this dependency via a constructor or method parameter?
- Is there a virtual/overridable method I can intercept?
- Can I extract an interface the caller depends on?
- Does the language support module-level replacement (mocks, monkey-patching)?
Dependency-Breaking Techniques
Organized by primary seam type. Prefer techniques higher in each list (simpler, less invasive).
Object Seams
| Technique |
When to Use |
| Extract Interface |
Class has concrete dependency; create interface for substitution |
| Parameterize Constructor |
Dependency created internally; pass it in instead |
| Parameterize Method |
Single method needs a dependency; pass as argument |
| Extract and Override Call |
Isolate one problematic call into a virtual method |
| Extract and Override Factory Method |
Object creation blocks testing; make it overridable |
| Extract and Override Getter |
Field access blocks testing; route through virtual getter |
| Subclass and Override Method |
Create test subclass that overrides problematic methods |
| Extract Implementer |
Move implementation to new class, keep interface in original |
| Break Out Method Object |
Large method with many dependencies; extract to own class |
| Adapt Parameter |
Wrap parameter type to break dependency on problematic type |
| Pull Up Feature |
Move method to superclass to enable override in subclass |
| Push Down Dependency |
Move dependency to subclass, keep parent testable |
| Introduce Instance Delegator |
Instance method delegates to static (enables override) |
Preprocessing/Text Seams
| Technique |
When to Use |
| Template Redefinition |
Use generics/templates to inject test dependencies |
| Text Redefinition |
Use macros to redefine dependencies at compile time |
| Definition Completion |
Provide test implementation for incomplete type |
Link/Global Seams
| Technique |
When to Use |
| Encapsulate Global References |
Wrap globals in a class for controlled access |
| Replace Global Reference with Getter |
Access global through virtual getter |
| Introduce Static Setter |
Add setter for singleton/global (test-only) |
| Replace Function with Function Pointer |
Use function pointers for runtime substitution |
| Link Substitution |
Replace at link time (C/C++ only) |
| Supersede Instance Variable |
Add setter to replace dependency post-construction |
| Primitivize Parameter |
Replace object params with primitives to cut dependency |
Output Format
Produce this analysis, then hand off to verification-protocol:
## Legacy Code Analysis: [File/Class]
### Change Point
[Where the modification goes and what behavior changes]
### Dependencies Blocking Testing
- [dependency]: [why it blocks tests]
### Identified Seams
| Seam Type | Location | Exploitation Strategy |
|-----------|----------|-----------------------|
| [type] | [where] | [how to use it] |
### Recommended Techniques
1. **[Technique]**: [one-line rationale]
- Steps: [ordered list of mechanical changes]
- Risk: [what could go wrong]
### Characterization Tests
[Tests to lock current behavior BEFORE any changes]
- [test]: calls X with Y, expects Z (current behavior, not ideal)
### Transformation Steps
1. [small, reversible step]
2. [next step]
...
N. Hand off to verification-protocol: write failing test for new behavior
Guidelines
- Safety over elegance: smaller, safer changes win
- Characterization tests capture ACTUAL behavior (bugs included), not ideal behavior
- Goal: get tests in place, not perfect design
- Some debt is acceptable if it enables testing
- When full rewrite is cheaper than incremental change, say so (R4 deviation)
- Document reasoning for future maintainers
1---2name: legacy-code-expert3description: Safely modify legacy code lacking tests. Identifies seams, breaks dependencies, plans characterization tests. Invoke before verification-protocol when touching untested code with tangled dependencies.4---56# ABOUTME: Feathers' dependency-breaking techniques for safely modifying untested legacy code7# ABOUTME: Seam identification, change point analysis, characterization test planning89# Legacy Code Expert1011**When to invoke:** before `verification-protocol` (TDD), when the target code lacks tests and has dependencies that block testing. This skill produces the analysis; verification-protocol drives the test-first cycle that follows.1213## Workflow14151. **Identify change point** (where the modification goes)162. **Map dependencies** blocking testability173. **Find seams** (places to alter behavior without editing production code)184. **Select techniques** (simplest that works, minimal ripple)195. **Plan characterization tests** (capture current behavior)206. **Define transformation steps** (small, reversible)217. **Hand off to verification-protocol** for TDD execution2223## Seam Types2425| Seam | Mechanism | Best For | Example |26|------|-----------|----------|---------|27| Object | Polymorphism, interfaces | OOP languages (Java, C#, Go, Ruby) | Extract interface, subclass and override |28| Preprocessing | Macros, conditional compilation | C/C++ | `#ifdef TESTING` to swap implementations |29| Link | Linker substitution, module replacement | C/C++, dynamic languages | Replace `.o` file, monkey-patch module |3031**Seam identification questions:**32- Can I substitute this dependency via a constructor or method parameter?33- Is there a virtual/overridable method I can intercept?34- Can I extract an interface the caller depends on?35- Does the language support module-level replacement (mocks, monkey-patching)?3637## Dependency-Breaking Techniques3839Organized by primary seam type. Prefer techniques higher in each list (simpler, less invasive).4041### Object Seams4243| Technique | When to Use |44|-----------|-------------|45| **Extract Interface** | Class has concrete dependency; create interface for substitution |46| **Parameterize Constructor** | Dependency created internally; pass it in instead |47| **Parameterize Method** | Single method needs a dependency; pass as argument |48| **Extract and Override Call** | Isolate one problematic call into a virtual method |49| **Extract and Override Factory Method** | Object creation blocks testing; make it overridable |50| **Extract and Override Getter** | Field access blocks testing; route through virtual getter |51| **Subclass and Override Method** | Create test subclass that overrides problematic methods |52| **Extract Implementer** | Move implementation to new class, keep interface in original |53| **Break Out Method Object** | Large method with many dependencies; extract to own class |54| **Adapt Parameter** | Wrap parameter type to break dependency on problematic type |55| **Pull Up Feature** | Move method to superclass to enable override in subclass |56| **Push Down Dependency** | Move dependency to subclass, keep parent testable |57| **Introduce Instance Delegator** | Instance method delegates to static (enables override) |5859### Preprocessing/Text Seams6061| Technique | When to Use |62|-----------|-------------|63| **Template Redefinition** | Use generics/templates to inject test dependencies |64| **Text Redefinition** | Use macros to redefine dependencies at compile time |65| **Definition Completion** | Provide test implementation for incomplete type |6667### Link/Global Seams6869| Technique | When to Use |70|-----------|-------------|71| **Encapsulate Global References** | Wrap globals in a class for controlled access |72| **Replace Global Reference with Getter** | Access global through virtual getter |73| **Introduce Static Setter** | Add setter for singleton/global (test-only) |74| **Replace Function with Function Pointer** | Use function pointers for runtime substitution |75| **Link Substitution** | Replace at link time (C/C++ only) |76| **Supersede Instance Variable** | Add setter to replace dependency post-construction |77| **Primitivize Parameter** | Replace object params with primitives to cut dependency |7879## Output Format8081Produce this analysis, then hand off to verification-protocol:8283```84## Legacy Code Analysis: [File/Class]8586### Change Point87[Where the modification goes and what behavior changes]8889### Dependencies Blocking Testing90- [dependency]: [why it blocks tests]9192### Identified Seams93| Seam Type | Location | Exploitation Strategy |94|-----------|----------|-----------------------|95| [type] | [where] | [how to use it] |9697### Recommended Techniques981. **[Technique]**: [one-line rationale]99 - Steps: [ordered list of mechanical changes]100 - Risk: [what could go wrong]101102### Characterization Tests103[Tests to lock current behavior BEFORE any changes]104- [test]: calls X with Y, expects Z (current behavior, not ideal)105106### Transformation Steps1071. [small, reversible step]1082. [next step]109...110N. Hand off to verification-protocol: write failing test for new behavior111```112113## Guidelines114115- Safety over elegance: smaller, safer changes win116- Characterization tests capture ACTUAL behavior (bugs included), not ideal behavior117- Goal: get tests in place, not perfect design118- Some debt is acceptable if it enables testing119- When full rewrite is cheaper than incremental change, say so (R4 deviation)120- Document reasoning for future maintainers