
Legacy Modernizer
Lock in legacy behavior with characterization tests before your agent refactors unclear production code.
Overview
legacy-modernizer is an agent skill most often used in Ship (also Build, Operate) that guides characterization testing to pin legacy behavior before refactoring.
Install
npx skills add https://github.com/jeffallan/claude-skills --skill legacy-modernizerWhat is this skill?
- Characterization tests document legacy behavior before any refactor
- Explicit pattern: capture mysterious branches (weight, destination, priority) as asserted outputs
- pytest-oriented examples for Python legacy modules
- Separates “current behavior” from “correct behavior” to reduce refactor risk
- Designed as a modernization gate—not a greenfield TDD starter
Adoption & trust: 2.3k installs on skills.sh; 9.7k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You need to change legacy code whose rules are unclear, and you are afraid refactors will break production behavior nobody documented.
Who is it for?
Solo maintainers refactoring brownfield Python (or similar) services where business logic accumulated without specs.
Skip if: Greenfield features with clear requirements, or teams that only need lint/format passes without behavior preservation.
When should I use this skill?
You are about to refactor or replace legacy code whose behavior is undocumented and need tests that capture current outputs first.
What do I get? / Deliverables
You get a characterization test suite that locks current outputs so modernization can proceed with regressions caught immediately when semantics drift.
- Characterization test class documenting legacy outputs
- Regression signal when refactor changes preserved behavior
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Ship/testing is the canonical shelf because the skill’s core artifact is tests that document existing behavior prior to safe modernization. Testing fits characterization suites that capture current outputs—even when wrong—so refactors do not silently change semantics.
Where it fits
Add characterization cases around calculate_shipping_cost before splitting it into a pricing service.
Wrap a legacy payment helper with tests that record fee rounding before extracting it to a module.
Re-run characterization suite after a hotfix to ensure ticket totals still match pre-fix outputs.
How it compares
Use instead of refactoring-first TDD when you cannot yet define correct behavior—tests document reality, not ideals.
Common Questions / FAQ
Who is legacy-modernizer for?
Indie builders and small teams owning legacy backends or scripts who need a disciplined test harness before structural changes.
When should I use legacy-modernizer?
Use it in Ship before large refactors; in Build when touching legacy modules during feature work; in Operate when fixing bugs where you must prove you did not widen behavioral drift.
Is legacy-modernizer safe to install?
Check the Security Audits panel on this page; the skill is documentation and test patterns—it does not execute refactors until your agent applies them in your repo.
SKILL.md
READMESKILL.md - Legacy Modernizer
# Legacy Testing Strategies ## Characterization Tests Tests that document current behavior (even if buggy) before refactoring. ```python # Legacy function with unknown behavior def calculate_shipping_cost(order): """Legacy shipping calculator - behavior unclear""" cost = 0 if order['weight'] > 10: cost += order['weight'] * 0.5 if order['destination'] == 'international': cost *= 2 if order['priority']: cost *= 1.5 # ... more mysterious logic return round(cost, 2) # Characterization test: Capture current behavior import pytest class TestShippingCostCharacterization: """These tests document existing behavior, not correct behavior""" def test_domestic_lightweight(self): order = {'weight': 5, 'destination': 'domestic', 'priority': False} # This IS the current behavior (0.0 might be wrong!) assert calculate_shipping_cost(order) == 0.0 def test_domestic_heavy(self): order = {'weight': 15, 'destination': 'domestic', 'priority': False} assert calculate_shipping_cost(order) == 7.5 # weight * 0.5 def test_international_heavy(self): order = {'weight': 15, 'destination': 'international', 'priority': False} assert calculate_shipping_cost(order) == 15.0 # (15 * 0.5) * 2 def test_priority_international_heavy(self): order = {'weight': 15, 'destination': 'international', 'priority': True} assert calculate_shipping_cost(order) == 22.5 # ((15 * 0.5) * 2) * 1.5 # After characterization, refactor with confidence def calculate_shipping_cost_v2(order: Order) -> Decimal: """Refactored with clear logic""" base_cost = Decimal('0') if order.weight > 10: base_cost = Decimal(str(order.weight)) * Decimal('0.5') if order.destination == Destination.INTERNATIONAL: base_cost *= Decimal('2') if order.priority: base_cost *= Decimal('1.5') return base_cost.quantize(Decimal('0.01')) # Characterization tests should still pass ``` ## Golden Master Testing Capture output snapshots for complex legacy systems. ```python # Legacy report generator with complex formatting def generate_monthly_report(start_date, end_date): """Generates complex text report""" report = [] report.append(f"Report Period: {start_date} to {end_date}") # ... 500 lines of complex logic return "\n".join(report) # Golden master test import hashlib import os from pathlib import Path class TestMonthlyReportGoldenMaster: def test_january_2024_report(self): """Compare against known-good output""" report = generate_monthly_report('2024-01-01', '2024-01-31') # First run: Save golden master golden_path = Path(__file__).parent / 'golden_masters' / 'jan_2024.txt' if not golden_path.exists(): golden_path.parent.mkdir(exist_ok=True) golden_path.write_text(report) pytest.skip("Golden master saved, run again to verify") # Subsequent runs: Compare expected = golden_path.read_text() assert report == expected, "Output differs from golden master" def test_report_hash_unchanged(self): """Faster comparison using hash""" report = generate_monthly_report('2024-01-01', '2024-01-31') report_hash = hashlib.sha256(report.encode()).hexdigest() # Known good hash expected_hash = "a3f5b2c8d9e1f4a7b6c5d8e9f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0" assert report_hash == expected_hash # Approval testing library from approvaltests import verify def test_monthly_report_approval(): """Uses approvaltests library for easy golden master testing""" report = generate_monthly_report('2024-01-01', '2024-01-31') verify(report) # Creates .approved file first run, compares after ``` ## Snapshot Testing for APIs ```python # Legacy API with complex responses @app.get("/api/dashboard") async def get_dashboard(): # Complex aggregation logic