legacy-code
Skill4 starsChanged 8 months agoUnlicenseView on GitHub
Changing code that is expensive to change safely because behavior is unclear, tests are weak or missing, dependencies are hidden, or runtime/framework setup blocks local feedback. Based on Working Effectively with Legacy Code (Michael Feathers). Covers: characterization tests, seams, dependency breaking, sprout/wrap techniques, incremental risk reduction. Triggers on: "legacy code", "untested code", "change existing code safely", "refactor legacy", "add tests to legacy code", "break dependencies", "no tests", "unclear behavior", "expensive to change", or when touching code with no tests or unclear behavior.
We split this skill into the rules it gives an agent
24 rules. About 0 are common, 23 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈23
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 18
If you are reading this, the user is working with code that lacks trustworthy tests or has unclear behavior.
- Distinctive: line 40
If current behavior is uncertain, characterize it — write tests that capture what the code does today, even if the behavior is ugly.
- Distinctive: line 45
Write characterization tests when you don't know whether current behavior is intentional.
- Distinctive: line 46
Test externally visible behavior first — narrow tests around the slice you're modifying.
- Distinctive: line 47
If real consumers rely on ugly behavior, capture it.
- Distinctive: line 48
Use sensing variables or temporary probes only to confirm a test reaches the intended path.
- Distinctive: line 55
Prefer seams that remain useful after the current task over one-off test hooks
- Distinctive: line 59
When code is hard to test, look for these dependency types and break them:
- Distinctive: line 69
When direct edits are too risky:
- Distinctive: line 72
Sprout class: Add a focused new collaborator.
- Distinctive: line 73
Wrap method: Add pre/post behavior around a risky method, or observe effects through a wrapper.
- Distinctive: line 83
Uncertain behavior: When behavior is unclear, consumers may rely on ugly behavior, or a branch is hard to prove — add characterization …
- Distinctive: line 84
Expensive setup: When tests require too much setup or a class can't be instantiated cheaply, break the first real barrier: constructor …
- Distinctive: line 85
Non-determinism: When time, randomness, environment, thread-local state, files, network, or DB writes block repeatable tests — wrap or …
- Distinctive: line 86
Large method/class: When a method or class defeats local reasoning, sketch effects, find interception or pinch points, extract pure …
- Very specific: line 87
Framework-bound code: When changing database-heavy, UI, framework, or API-boundary code — separate policy from …
- Distinctive: line 88
Temporary seam: When a seam is magical, public-for-test, subclass-only, or link/preprocessor-based — add a cleanup obligation.
- Distinctive: line 89
Repeated edits: When repeated edits cluster across several places, remove duplication incrementally under tests instead of launching a …
- Distinctive: line 90
Rewrite temptation: When a rewrite feels tempting, choose the smallest sprout, wrap, seam, or characterization step that makes today's …
- Distinctive: line 94
Rewrite as first move: Do not replace a subsystem before understanding current behavior.
- Distinctive: line 95
No-safety change: Do not change legacy code with no tests or observation strategy.
- Distinctive: line 96
Hidden dependency expansion: Do not add more globals, statics, or ambient context to already hard-to-test code.
- Distinctive: line 97
Cosmetic refactoring only: Do not rename and format while leaving the real dependency knots intact.
- Distinctive: line 101
Before finalizing any legacy change:
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Working Effectively with Legacy Code
When This Skill Loads
If you are reading this, the user is working with code that lacks trustworthy tests or has unclear behavior. Stop and follow this workflow. Do not start with a rewrite, a broad cleanup, or speculative refactoring.
Core Principle
Gain control before improving design.
The legacy loop:
- Identify the change point
- Characterize current behavior (write tests that capture what it does today)
- Find or create the smallest useful seam
- Break the dependency that blocks testing
- Change the requested behavior
- Refactor locally — leave the area more testable
Primary Directive
If a part of the code lacks trustworthy tests, treat it as legacy code.
Before editing, state:
- The requested behavior change (what should be different)
- The current behavior that must remain (what must not break)
If current behavior is uncertain, characterize it — write tests that capture what the code does today, even if the behavior is ugly. Do not silently "fix" behavior you don't understand.
Decision Rules
Characterization
- Write characterization tests when you don't know whether current behavior is intentional.
- Test externally visible behavior first — narrow tests around the slice you're modifying.
- If real consumers rely on ugly behavior, capture it. Mark it for clarification, don't silently fix it.
- Use sensing variables or temporary probes only to confirm a test reaches the intended path; remove them after use.
Seams
A seam is a place where you can alter behavior without editing in that place. Use the smallest seam that unlocks the change:
- Constructor injection, parameter injection, extracted method, wrapper around static call, adapter around a framework object, factory indirection, module boundary
- Subclass seam only when language constraints leave no better option
- Link, import, or preprocessing seams only when ordinary object seams are impractical
- Prefer seams that remain useful after the current task over one-off test hooks
- Decide whether each seam is for sensing (observing behavior), separation (substituting a dependency), or both
Dependency Breaking
When code is hard to test, look for these dependency types and break them:
| Dependency Type | Examples | Breaking Move |
| Hidden inputs | Time, randomness, env vars, thread-local state, globals, singletons | Inject clock, random generator, config |
| Hard outputs | File writes, network calls, DB writes, process exits, messages | Wrap behind an interface, inject the wrapper |
| Construction problems | Constructors doing real work, hidden new, buried factory calls | Split construction from use, parameterize constructor |
| Static/global reach-through | Direct static calls, global config access | Encapsulate behind a wrapper, inject the wrapper |
Sprout and Wrap Techniques
When direct edits are too risky:
- Sprout method: Extract new behavior into a new method. Keep old code mostly untouched. Route to the new method from a small insertion point.
- Sprout class: Add a focused new collaborator. Delegate from the legacy class. Move behavior over incrementally.
- Wrap method: Add pre/post behavior around a risky method, or observe effects through a wrapper.
- Wrap class: Mediate through a new abstraction when the original class is too hard to test directly.
Keep Changes Separate
- Behavior changes and structural refactorings go in separate commits.
- Characterization tests (capturing old behavior) and new behavior tests stay distinct.
- Exploratory restructuring for understanding: do not check it in.
Trigger Rules
- Uncertain behavior. When behavior is unclear, consumers may rely on ugly behavior, or a branch is hard to prove — add characterization before changing semantics.
- Expensive setup. When tests require too much setup or a class can't be instantiated cheaply, break the first real barrier: constructor work, hidden allocation, factory call, global state, static construction.
- Non-determinism. When time, randomness, environment, thread-local state, files, network, or DB writes block repeatable tests — wrap or inject that boundary.
- Large method/class. When a method or class defeats local reasoning, sketch effects, find interception or pinch points, extract pure computation first.
- Framework-bound code. When changing database-heavy, UI, framework, or API-boundary code — separate policy from query/mapping/persistence/handlers.
- Temporary seam. When a seam is magical, public-for-test, subclass-only, or link/preprocessor-based — add a cleanup obligation. Remove it once safer structure exists.
- Repeated edits. When repeated edits cluster across several places, remove duplication incrementally under tests instead of launching a broad redesign.
- Rewrite temptation. When a rewrite feels tempting, choose the smallest sprout, wrap, seam, or characterization step that makes today's requested change safer.
Forbidden Patterns
- Rewrite as first move. Do not replace a subsystem before understanding current behavior.
- No-safety change. Do not change legacy code with no tests or observation strategy.
- Hidden dependency expansion. Do not add more globals, statics, or ambient context to already hard-to-test code.
- Cosmetic refactoring only. Do not rename and format while leaving the real dependency knots intact.
Final Checklist
Before finalizing any legacy change:
- Untested area treated as legacy risk?
- Behavior delta and behavior-to-preserve stated?
- Uncertain current behavior characterized?
- Tests close enough and fast enough to diagnose the change?
- Smallest useful seam chosen (sensing vs separation clear)?
- Blocking dependency reduced without expanding hidden dependencies?
- Behavior change, refactoring, and cleanup kept separate?
- Temporary seam has a cleanup path?
- Touched area is more understandable, testable, or changeable than before?
Reference
| File | Use When |
| Dependency Breaking | Choosing which dependency-breaking technique applies to a specific barrier |
| Seam Techniques | Deciding between sprout method, sprout class, wrap method, wrap class, extract-and-override |
| Legacy Workflow | Full step-by-step walkthrough of the legacy change loop |
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- zeeshanalimughal/claude-skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository73 other files it gives its agents.
- agent/AGENTS.mdAGENTS.md
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- git-guardrails-claude-codeSkill
- setup-project-skillsSkill
- skill-creatorSkill
- websearchSkill
- writing-great-skillsSkill
- codebase-designSkill
- construction-craftSkill
- critical-partnerSkill
- data-intensiveSkill
- diagnosing-bugsSkill
- django-disciplineSkill
- docker-disciplineSkill
- documentation-lifecycleSkill
- domain-driven-designSkill
- enterprise-patternsSkill
- git-disciplineSkill
- production-readinessSkill
- refactoring-passSkill
- resolving-merge-conflictsSkill
- tddSkill
- testing-workflowSkill
- ui-design-directionSkill
- webapp-testingSkill
- writing-for-agentsSkill
- django-allauthSkill
- djangoSkill
- dockerSkill
- drfSkill
- full-calendarSkill
- giteaSkill
- htmxSkill
- http-status-codesSkill
- hyperscriptSkill
- langchainSkill
- litestarSkill
- prestashopSkill
- prSkill
- pykeSkill
- python-asyncSkill
- tablerSkill
- cleanroom-rewriteSkill
- councilSkill
- court-jesterSkill
- domain-modelingSkill
- fix-prSkill
- grillingSkill
- grill-meSkill
- grill-with-docsSkill
- handoffSkill
- humanifySkill
- implementSkill
- implement-specSkill
- improve-codebase-architectureSkill
- ponytail-auditSkill
- ponytail-debtSkill
- prototypeSkill
- qaSkill
- The first 60 shown.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

