welc-legacy-code

Use when facing untested legacy code, test harness problems, dependency issues, or time pressure. Triggers on: legacy code, no tests, can't test, afraid to change, need to modify untested code.

211 stars

Best use case

welc-legacy-code is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Use when facing untested legacy code, test harness problems, dependency issues, or time pressure. Triggers on: legacy code, no tests, can't test, afraid to change, need to modify untested code.

Teams using welc-legacy-code should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/welc-legacy-code/SKILL.md --create-dirs "https://raw.githubusercontent.com/ryanthedev/code-foundations/main/skills/welc-legacy-code/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/welc-legacy-code/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How welc-legacy-code Compares

Feature / Agentwelc-legacy-codeStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Use when facing untested legacy code, test harness problems, dependency issues, or time pressure. Triggers on: legacy code, no tests, can't test, afraid to change, need to modify untested code.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# Working Effectively with Legacy Code

> "Legacy code is simply code without tests." - Michael Feathers

Get code under test before changing it. This is the core principle.

## Quick Reference

| 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 |

---

## The Legacy Code Change Algorithm

**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
```

---

## Sprout/Wrap Techniques (Time Pressure)

When you can't test everything but need to change code now:

- **Sprout Method:** Write new code in a new method, call from existing code. New code is separately testable.
- **Sprout Class:** Create new class for new functionality when original is untestable.
- **Wrap Method:** Add behavior before/after existing method. Original code untouched.
- **Wrap Class:** Decorator pattern — wrap existing class with new behavior layer.

These keep new code separate and testable even when old code isn't.

---

## Characterization Tests

When you don't know what tests to write:

1. Write assertion you know will fail
2. Let the failure message tell you what the code actually does
3. Change test to expect actual behavior
4. Repeat for each code path you need to cover

You're documenting what the code **does**, not what it **should** do. This is the test safety net for refactoring.

---

## Effect Sketching and Pinch Points

When you need to change many classes in one area:

1. **Effect Sketch:** From each change point, trace effects outward — what does this affect? Follow return values, modified parameters, modified fields, global/static data.
2. **Find Pinch Points:** Where do effects from multiple change points funnel together? Write tests there first.
3. **Pinch point tests are scaffolding** — delete them when you have proper unit tests. They exist to give you a safety net while breaking things apart.

---

## Dependency-Breaking Catalog

| 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 |

---

## Decision Trees

### Can't Instantiate Class?

```
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
```

### Can't Call Method?

```
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
```

### Under Time Pressure?

```
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)
```

---

## Key Principles

1. **Tests first, then change.** The Legacy Code Change Algorithm is non-negotiable.
2. **Break dependencies minimally.** Just enough to get tests in place.
3. **Preserve behavior.** Characterization tests document what IS, not what SHOULD BE.
4. **Small steps.** Each refactoring should be verifiable.
5. **Seams enable testing.** A seam is a place where you can alter behavior without editing in that place. Prefer object seams (override via inheritance/interfaces).

---

## Chain

| After | Next |
|-------|------|
| Code under test | cc-refactoring-guidance (safe refactoring process) |
| Dependencies broken | Continue with Legacy Code Change Algorithm step 4-5 |

Related Skills

whiteboarding-planning

211
from ryanthedev/code-foundations

Standard/Full planning pipeline for whiteboarding. Steps: discover, classify, explore, detail, save, check, confirm, handoff. Use when dispatched from whiteboarding command for Medium/Complex tasks. Triggers on 'planning pipeline', 'standard track', 'full track'.

performance-optimization

211
from ryanthedev/code-foundations

Use when code is too slow, has performance issues, timeouts, OOM errors, high CPU/memory, or doesn't scale. Triggers on: profiler hot spots, latency complaints, needs optimization, critical path analysis.

code-clarity-and-docs

211
from ryanthedev/code-foundations

Use when reviewing code clarity, writing comments, checking documentation accuracy, or auditing AI-facing docs. Triggers on: naming, comments, documentation, README, CLAUDE.md.

clarify

211
from ryanthedev/code-foundations

Decompose user intent through structured brainstorming. Detects underspecification, ambiguity, and false premises through hypothesis-driven questioning. Use when a request is unclear, could have multiple valid interpretations, or critical details are missing.

cc-routine-and-class-design

211
from ryanthedev/code-foundations

Use when designing routines or classes, reviewing class interfaces, choosing between inheritance and containment, or evaluating routine cohesion. Also trigger when inheritance is used without LSP verification, or when design issues are present despite passing tests

cc-refactoring-guidance

211
from ryanthedev/code-foundations

Use when modifying existing code, improving structure without changing behavior, or deciding between refactor, rewrite, or fix-first.

cc-quality-practices

211
from ryanthedev/code-foundations

Use when planning QA, choosing review methods, designing tests, or debugging fails. Triggers on: defects found late, tests pass but production bugs, coverage disputes, review ineffective, spending excessive time debugging.

cc-pseudocode-programming

211
from ryanthedev/code-foundations

Use when designing routines, stuck on where to start coding, caught in compile-debug loops, or code works but you don't understand why. Triggers on: starting a new coding task

cc-defensive-programming

211
from ryanthedev/code-foundations

Use when auditing defensive code, designing barricades, choosing assertion vs error handling, or deciding correctness vs robustness strategy. Triggers on: empty catch blocks, missing input validation, assertions with side effects, wrong exception abstraction level, garbage in garbage out mentality, deadline pressure to skip validation, trusted source rationalization.

cc-control-flow-quality

211
from ryanthedev/code-foundations

Use when code has deep nesting (3+ levels), complex conditionals, loop design questions, high cyclomatic complexity (McCabe >10), or callback hell. Symptoms: arrow-shaped code, repeated conditions, confusing loop exits, lengthy if-else chains

ca-architecture-boundaries

211
from ryanthedev/code-foundations

Use when designing system architecture, drawing boundaries between business logic and infrastructure, or when changes touch many unrelated files. Triggers on: architecture design, dependency direction, separating business rules from database/UI/frameworks.

aposd-verifying-correctness

211
from ryanthedev/code-foundations

Use after implementing code. Triggers on: is it done, ready to commit, verify correctness, did I miss anything, pre-commit check.