| name | lava-flow-legacy-detection |
| description | Checklist of "lava flow" signs in legacy/brownfield code — dead code surrounding live paths, unremoved feature flags, fossilized workarounds, duplicates left over from migrations. Use during current-state analysis of a repo, before refactoring, and when reviewing PRs in a mature codebase. Activate on mentions of "legacy", "technical debt", "brownfield", "why is this code here". |
| type | reference |
| domain | development |
| owners | ["architect","reviewer","senior_full_stack"] |
| gates | ["ARCH","DEV","REV"] |
| tech | [] |
| topic | ["architecture","refactoring","legacy"] |
| triggers | ["legacy code","dead code","lava flow","unremoved feature flag","brownfield refactor","fossilized workaround","unused code"] |
| related | ["current-state-analysis","architecture-compliance-review","architecture-doc-reference","design-patterns-reference","adr-log","adr-log-reference","threat-model-baseline","security-baseline-dev","code-review-checklist","system-design-checklist"] |
| budget_lines | 250 |
| schema_version | 1 |
Skill: Lava Flow Legacy Detection
The "lava flow" anti-pattern (Brown et al., AntiPatterns, 1998) — a hardened mass of code that flowed unchecked into production. Nobody remembers why it's there, deleting feels risky (no tests), rewriting is expensive. Every release thickens the layer.
When to apply: ARCH (current-state analysis), DEV (before touching an old module), REV (PR review in a mature codebase). Reference skill — activation via frontmatter triggers; see § MCP integration below for cross-gate consumption.
Sections:
- Signs of lava flow
- Detection checklist
- What to do with a finding
- What NOT to do
- Report template
1. Signs
1.1 Structural
| # | Sign | How to detect |
|---|
| LF-01 | Commented-out blocks in production files | grep for // / /* blocks >5 lines |
| LF-02 | Code branches that never execute | coverage report shows 0% on the branch |
| LF-03 | Functions/classes with no inbound references | static analysis (ts-prune, knip, unimport) |
| LF-04 | Parallel "versions" of the same logic (v1/v2/legacy/new) | grep by name suffixes |
| LF-05 | Duplicates left over from unfinished migrations | diff old/, legacy/, deprecated/ dirs |
| LF-06 | Files untouched longer than half the repo's age | git log -1 --format=%cd -- <file> or find <dir> -name '*.ts' -mtime +<days> |
1.2 Configurational
| # | Sign | How to detect |
|---|
| LF-07 | Feature flags permanently false (or permanently true) | grep flag.*= false, verify usage in code |
| LF-08 | Config keys nobody reads | grep key value across codebase |
| LF-09 | Env vars not present in .env.example | diff process.env.X against .env.example |
| LF-10 | Hardcoded mock/stub data in production paths | grep MOCK_, STUB_, fake_, dummy_ |
1.3 Behavioural
| # | Sign | How to detect |
|---|
| LF-11 | Workarounds that became permanent | grep TODO, FIXME, HACK, XXX older than 6 mo |
| LF-12 | Hardcoded version/environment checks against a dead target | grep if.*IE, if.*v1, if.*LEGACY where the right side no longer exists |
| LF-13 | Inactive cron jobs / scheduled tasks | crontab -l + grep logs over past 3 mo; systemctl list-timers --all; kubectl get cronjob -o wide (lastScheduleTime) |
| LF-14 | Endpoints with no callers from frontend/other services | grep route paths across the monorepo |
| LF-15 | Router routes leading to 404s or empty components | unit test walks all routes |
1.4 Code examples
Lava — commented-out fallback to a removed system:
const session = await auth0.login(creds);
The legacyAuth module was removed 18 months ago. The comment is fossil; the history already lives in git. Decision: delete.
Not lava — comment explains the non-obvious (Chesterton's fence):
const month = date.getMonth() + 1;
The comment carries meaning and protects the code from a falsely-obvious "fix". Decision: keep.
2. Checklist
Apply before refactoring a module or when first onboarding into a legacy repo.
3. What to do
Principle: removing lava flow is its own task — never part of a feature PR. Before any deletion, git blame the block. If the author is still on the team — ask for context (it routinely saves hours and prevents the deletion of something quietly important). If the author left or the code is older than ~24 months, git blame is just archaeology — proceed down the checklist.
- Report BEFORE changes. List findings in a dedicated doc (
docs/reports/architect/lava-flow-<module>.md). Do not delete without architect sign-off.
- Test coverage BEFORE deletion. If the branch lacks tests — write characterization tests first (M. Feathers, Working Effectively with Legacy Code), then delete.
- Small PRs. One PR = one finding. Don't "delete all of deprecated/". Atomicity gives a clean rollback point.
- Feature flag → delete the "off" side. If a flag has been 100% in one position for more than a release, delete the opposing branch. BUT: if the flag has been toggled at least once in the last release window, it's live config, not lava; leave it alone (Chesterton's fence — someone is actively using it).
- ADR when in doubt. If you can't tell why the code exists — open
$adr-log with options "delete / keep / rewrite", escalate to Architect.
4. Pitfalls
- ❌ "While I'm in here, I'll clean this up" — lava flow is never removed ad-hoc inside a feature PR. It pollutes the diff and breaks review.
- ❌ Mass deletion without tests — characterization tests are mandatory for uncovered branches.
- ❌ Renaming instead of deleting —
_old, _deprecated_v2 create a new lava layer.
- ❌ A "we'll deal with it later" comment — that's lava in seed form. Decide now: delete or ADR.
- ❌ Keeping it "just in case" — git history is the archive.
5. Report
# Lava Flow Report — <module-name>
**Date:** <YYYY-MM-DD>
**Author:** <agent>
**Context:** <why we looked — refactor / current-state / review>
## Findings
| # | ID | File:line | Sign | Decision |
|---|----|-----------|------|----------|
| 1 | LF-01 | src/foo.ts:120-180 | 60-line commented block | DELETE (PR #N) |
| 2 | LF-07 | src/config/flags.ts:42 | Flag `useNewAuth` = false for 8 mo | DELETE old branch (PR #N+1) |
| 3 | LF-04 | src/api/users-v1.ts | Duplicate of users-v2.ts, no callers | DELETE (ADR-DEV-XXX) |
## Escalations
- <cases that need Architect/user sign-off>
## Close-out plan
- <PR breakdown with order>
Related skills
$current-state-analysis — repo-wide audit; lava flow is one of its outputs
$architecture-compliance-review — verify architecture compliance after deletion
$design-patterns-reference — what to use INSTEAD of the removed lava
$code-review-checklist — for reviewing the deletion PRs
$system-design-checklist — so the new code doesn't become next year's lava
MCP integration (cross-gate activation)
Reference skill with cross-gate consumption:
- ARCH (primary) —
architect uses it during current-state analysis before proposing refactoring
- DEV (secondary) —
senior_full_stack consults before touching an old module
- REV (secondary) —
reviewer applies during PR review in a mature codebase
Activation via frontmatter triggers (legacy code, dead code, lava flow, unremoved feature flag, brownfield refactoring, etc.). See $mcp-integration for general MCP flow, gate ritual and recording discipline.
Section IDs (LF-01..LF-25) are stable anchors: cite them inline in ARCH/REV artifacts instead of copying content. Keeps the audit trail compact and provides a backlink to the canonical source.