working-with-legacy-code
Safely change and test untested codebases using Feathers'' "Working Effectively with Legacy Code". Use when the user mentions "legacy code", "no tests", "untested codebase", "how do I test this", "seams", "characterization tests", "golden master", "sprout method", "afraid to change this code", "monster method", "dependency breaking", or "inherited a messy codebase". Also trigger when changing code without tests safely, getting a class under test when constructors, statics, or singletons block it, adding features to tangled modules, or planning incremental test coverage for an old codebase. Cov
npx skills add wondelai/skills --skill working-with-legacy-code --agent claude-code
Same command for any agent — swap --agent for codex, cursor, copilot.
Weekly change comes from our own snapshots, not the repository page — it measures attention, not adoption.
What it does
The skill guides handling legacy code by applying a fixed sequence to safely introduce tests before changes, using characterization tests to pin current behavior, and employing techniques like seams, sprout/wrap, and dependency-breaking methods to enable safe modifications without rewrites.
How it works
It instructs identifying change points and test points, then breaking dependencies with least-invasive moves so tests can be written first. It prescribes creating seams through constructor parameters, modified factories, or test doubles, and using sprout or wrap to add new behavior when the host cannot be tested directly. It emphasizes writing characterization tests to capture current behavior, using golden masters or snapshots for complex outputs, and splitting changes into structure-only and behavior-only commits. For blockers like static calls or singletons, it recommends strategies such as introducing an instance delegator or parameterizing constructors, and then applying the change algorithm in verifiable steps before touching the core logic.
When to use it
Use when code has no tests or is hard to test (untested codebase, monster methods,dependency-breaking scenarios) and you need to safely implement changes or plan incremental test coverage. Triggered when legacy changes must be made without risking behavior loss, especially if attempting to add tests to a tangled module or when preparing for gradual test coverage.
What it can touch
The skill references techniques and patterns that touch constructors, factories, static calls, and test doubles to enable testing, including parameterization of constructors, introducing interfaces, instance delegators, and wrap/sprout patterns. It implies modifying how dependencies are wired and how code is invoked in tests, with guidance on where to apply changes for testability.
Caveats
License: MIT. The content describes a process for legacy-code work and does not guarantee outcomes; results depend on project context and proper application of the techniques. It notes that the change algorithm is applied step-by-step and that some techniques may require creating new classes or wrappers and writing new tests before altering production code.
# Working Effectively with Legacy Code A field manual for changing code that has no tests, distilled from Michael C. Feathers' *Working Effectively with Legacy Code*. Use it to get untestable classes into a harness, pin down current behavior with characterization tests, and make changes one safe, verifiable step at a time — without resorting to a rewrite. ## Core Principle **Legacy code is simply code without tests.** Not old code, not ugly code — untested code: without tests you cannot know whether a change preserves behavior, so every edit is a gamble. The craft is breaking dependencies just enough to get tests in place before changing anything — cover and modify, never edit and pray. ## Scoring **Goal: 10/10.** Rate changes to untested code 0-10 against the principles below. Report the current score and the specific steps needed to reach 10/10. - **9-10:** Change points covered by characterization tests before any edit; behavior changes and refactoring shipped as separate verified steps; dependencies broken with the least invasive technique - **7-8:** Tests at most change points, but occasional mixed refactor-plus-behavior commits or heavier dependency surgery than needed - **5-
- Core Principle
- Scoring
- Framework
- 1. The Legacy Code Dilemma and Change Algorithm
- 2. Seams: Where to Pry Code Apart
- 3. Characterization Tests
- 4. Sprout and Wrap: Changing Without Tests First
- 5. Dependency-Breaking Techniques
- 6. Untangling and Understanding
- Common Mistakes
- Quick Diagnostic
- Further Reading
- About the Author
What does the working-with-legacy-code skill do?
Safely change and test untested codebases using Feathers'' "Working Effectively with Legacy Code". Use when the user mentions "legacy code", "no tests", "untested codebase", "how do I test this", "seams", "characterization tests", "golden master", "sprout method", "afraid to change this code", "monster method", "dependency breaking", or "inherited a messy codebase". Also trigger when changing code without tests safely, getting a class under test when constructors, statics, or singletons block it, adding features to tangled modules, or planning incremental test coverage for an old codebase. Cov
How do I install it?
Run `npx skills add wondelai/skills --skill working-with-legacy-code --agent claude-code` — it drops the skill into your project so the agent can pick it up. Swap the --agent value for codex, cursor or copilot if you use one of those.
Where does this skill come from?
From wondelai/skills, a repository with 1,835 stars. We read it straight from the repository tree rather than a submitted listing, so what you see here is what is actually published.
Is a popular skill a good skill?
Not necessarily. Stars measure attention, not adoption — a repository can trend for a week and be abandoned. That is why we show the weekly change from our own snapshots next to the total, instead of a single flattering number.
