| name | codebase-drag-audit |
| description | Audit a codebase for "codebase drag" — the hidden overhead that makes every task take longer than it should. Scores five signals (Apology Estimate, Deploy Fear, Don't-Touch-That File, Coverage Lie, Time to First Commit) on a 0-10 scale using parallel sub-agents. Use this skill whenever the user asks about codebase health, engineering velocity, technical debt assessment, why the team is slow, code quality audit, or wants to measure how much friction their codebase creates. Also trigger when users mention "drag audit", "codebase drag", or reference the Piechowski article. A follow-up deepening scan turns high-scoring signals into concrete refactor candidates — trigger it directly when the user asks where the drag comes from, for "deepening opportunities", or to find shallow modules.
|
Codebase Drag Audit
Codebase drag is distinct from technical debt. Technical debt is what's wrong with code; drag is what it costs to work in the code. A codebase can have known debt that's well-contained and causes little drag, or it can have no obvious debt yet impose massive friction on every change.
This audit measures five signals of codebase drag, each scored 0-2, for a total of 0-10. The signals come from Ally Piechowski's engineering velocity diagnostic framework.
How to Run the Audit
Launch five sub-agents in parallel using the Agent tool — one per signal below. Each agent independently investigates its signal and returns a score with evidence. This parallelization is important because each investigation involves significant git/file analysis.
After all five agents complete, aggregate results into the final report.
Sub-Agent Prompt Template
For each signal, spawn a sub-agent with this structure (filling in the signal-specific instructions from the sections below):
You are auditing a codebase for a specific signal of "codebase drag."
Working directory: {cwd}
## Your Signal: {signal_name}
{signal_investigation_instructions}
## Output Format
After your investigation, respond with EXACTLY this format:
SIGNAL: {signal_name}
SCORE: {0, 1, or 2}
EVIDENCE:
- {bullet point findings}
- {bullet point findings}
RECOMMENDATION: {one-sentence actionable fix, or "No action needed" for score 0}
Signal 1: The Apology Estimate (Complexity & Coupling)
What it measures: Whether the codebase has accumulated complexity that forces engineers to estimate tasks at 2-3x what they "should" take — not because they're padding, but because understanding and safely changing the code demands it.
Sub-agent investigation instructions:
Investigate code complexity and coupling that would make simple changes expensive.
1. Find the 10 largest source files (by line count). Exclude vendored code, generated
files, lock files, migrations, and test fixtures. Report files over 500 lines.
2. Check for deeply coupled modules: pick 3-5 core source files and count how many
other files import from them (fan-out). Anything imported by >15 files is a coupling
hotspot.
3. Look for "god files" — files that mix multiple responsibilities (e.g., a single file
containing models, business logic, and HTTP handlers).
4. Check directory nesting depth — are there files buried 6+ levels deep?
Scoring:
- 0: No files over 500 lines, fan-out is reasonable (<10), clean module boundaries
- 1: A few large files (3-5 over 500 lines) or moderate coupling hotspots
- 2: Many large files (5+), high fan-out (>15), or god files present
Signal 2: Deploy Fear
What it measures: Whether the team has low confidence in their ability to ship and roll back safely. Teams with deploy fear batch changes, avoid certain days, or have unwritten deployment rules.
Sub-agent investigation instructions:
Investigate deployment confidence signals in the codebase.
1. Check CI/CD configuration: look for files like .github/workflows/*, .gitlab-ci.yml,
Jenkinsfile, .circleci/*, Dockerfile, docker-compose*, fly.toml, vercel.json,
netlify.toml, etc. Assess:
- Are there automated tests in CI?
- Is there a deploy step?
- Is there a rollback mechanism or blue/green setup?
2. Check deployment frequency via git tags:
Run: git tag --sort=-creatordate | head -20
And: git log --oneline --since="6 months ago" --grep="deploy\|release\|v[0-9]" | head -20
How often are releases cut? Weekly = healthy, monthly+ = potential fear.
3. Look for deployment scripts or runbooks (deploy.sh, Makefile deploy targets,
scripts/ directory). Are they maintained or rotting?
4. Check for feature flags infrastructure (LaunchDarkly config, feature flag files,
environment-based toggles). Their presence suggests the team needs safety nets
for shipping.
Scoring:
- 0: CI runs tests + deploys automatically, releases are frequent (weekly+), rollback exists
- 1: CI exists but deploy is manual, or releases are infrequent (monthly), or no rollback
- 2: No CI, no deploy automation, very infrequent releases, or no evidence of deployment process
Signal 3: The "Don't Touch That" File
What it measures: Whether certain files have become untouchable dead zones that the codebase grows around rather than through. These files accumulate small patches but never get the structural work they need.
Sub-agent investigation instructions:
Find files that engineers avoid touching — the "lava layer" of the codebase.
1. Find high-churn files with only small patches:
Run: git log --pretty=format: --name-only --since="2 years ago" | sort | uniq -c | sort -rn | head -20
For the top files, check if changes are structural or just patch-ups:
Run (for each): git log --oneline --since="2 years ago" -- <file> | head -10
Files with many commits but only small fixes (typos, one-line patches, config tweaks)
are "don't touch" candidates.
2. Find stale critical files — important-looking files (config, core logic, main entry
points) that haven't been meaningfully changed in 1+ year:
Run: git log --oneline -1 -- <file>
3. Look for "wrapper" patterns where new code wraps around old code rather than
modifying it — adapter classes, compatibility shims, files named *_legacy*,
*_old*, *_v2*.
4. Check for TODO/FIXME/HACK/XXX density in core files:
Search for these markers and note which files accumulate them.
Scoring:
- 0: No stale critical files, changes are structural, few HACK/TODO markers
- 1: 1-2 files showing avoidance patterns, or moderate TODO/HACK accumulation
- 2: Clear dead zones (3+ files with patch-only history), wrapper patterns, or high HACK density
Signal 4: The Coverage Lie
What it measures: Whether test coverage numbers mask a reality where tests don't catch regressions. High coverage with low confidence means the test suite exists to satisfy metrics, not to protect the codebase.
Sub-agent investigation instructions:
Assess whether the test suite provides real confidence or just coverage theater.
1. Calculate the test-to-source ratio: count test files vs source files.
A healthy ratio is roughly 1:1 to 1:2. Below 1:3 is a yellow flag.
2. Check for mock-heavy tests: search for mock/patch/stub usage in test files.
Count files where mocks outnumber real assertions. When mocks dominate, tests
verify the mock, not the system.
3. Look for assertion quality: search test files for assertion patterns.
- Are there tests with zero assertions (test_* functions that just call code)?
- Are assertions meaningful (checking values) or trivial (assertTrue(True))?
- Count test files vs files with actual assertions.
4. Check for integration/E2E tests: do tests exist that exercise real paths
(database, API calls, full request cycles), or is everything unit-mocked?
5. Look for coverage configuration: .coveragerc, jest.config coverage settings,
nyc config. Is there a minimum threshold? Is it enforced in CI?
Scoring:
- 0: Good test ratio (>1:3), integration tests exist, assertions are substantive, coverage enforced
- 1: Tests exist but are mock-heavy, or ratio is low, or no integration tests
- 2: Very few tests, or tests are mostly smoke/trivial, or clear coverage theater (config with no enforcement)
Signal 5: Time to First Commit (Onboarding Friction)
What it measures: How much accumulated knowledge a new contributor needs before they can make a meaningful change. Healthy codebases get new people productive in days; dragging ones take weeks.
Sub-agent investigation instructions:
Assess how hard it would be for a new developer to get started and make a change.
1. Check README quality:
- Does it exist and is it current?
- Does it have setup instructions?
- Are the setup steps specific (exact commands) or vague ("install dependencies")?
- Does it explain the project structure?
2. Check for setup automation:
- Look for: Makefile, setup.sh, bin/setup, docker-compose for dev, devcontainer.json,
.tool-versions, mise.toml, .nvmrc, .python-version, flake.nix
- Try to assess if scripts would actually work (do referenced files exist?)
3. Check environment variable documentation:
- Is there a .env.example or .env.template?
- Are required env vars documented somewhere?
- Count env var references in code vs documentation coverage
4. Check for contributing guidelines:
- CONTRIBUTING.md or contributing section in README
- Code style documentation or automated formatting config
- PR template (.github/pull_request_template.md)
5. Check dependency management clarity:
- Is the dependency manager obvious (package.json, pyproject.toml, go.mod, Cargo.toml)?
- Are there multiple conflicting dependency files?
- Is the lock file committed?
Scoring:
- 0: Clear README with working setup steps, .env.example, automated setup, contributing guide
- 1: README exists but setup is incomplete, or missing .env.example, or no contributing guide
- 2: No README, broken/missing setup, undocumented env vars, unclear how to even start
Aggregation and Final Report
After all five sub-agents return, build the final report. Use this template:
# Codebase Drag Audit
## Scores
| Signal | Score | Rating |
|--------|-------|--------|
| Apology Estimate (Complexity) | X/2 | {rating} |
| Deploy Fear | X/2 | {rating} |
| Don't-Touch-That File | X/2 | {rating} |
| Coverage Lie | X/2 | {rating} |
| Time to First Commit | X/2 | {rating} |
| **Total** | **X/10** | **{overall}** |
{Use these rating labels: 0 = "Clean", 1 = "Moderate", 2 = "Severe"}
## Diagnosis
{Based on total score:}
{0-3: "**Normal friction.** This codebase has typical levels of overhead. No systemic drag detected."}
{4-6: "**Drag is real.** The team is slower than they should be. The signals above point to specific areas where targeted investment would pay off."}
{7-10: "**The codebase is the bottleneck.** Process changes, reorgs, and hiring won't help — the code itself demands overhead that compounds invisibly. Direct investment in the highest-scoring signals is needed."}
## Evidence & Recommendations
{For each signal, list the key evidence bullets and the recommendation from the sub-agent.
Order by score descending — worst signals first — so the most impactful fixes lead.}
## Suggested Next Steps
{Based on the highest-scoring signals, recommend 1-2 concrete two-week sprints.
Be specific: name the files, the pattern, the fix. Not "reduce complexity" but
"split user_controller.py (1200 lines) into auth, profile, and admin modules."}
Report this to the user in full. The value is in the specificity — vague advice like "improve test coverage" is worthless. Name the files, the patterns, and the fixes.
After the report, offer the deepening scan below when any signal scored 2, or when the evidence names specific files. The audit measures what the drag costs; the scan finds the structures causing it.
Deepening Scan — from scores to refactor candidates
Runs after the audit (seeded by its evidence) or standalone when the user asks where the friction comes from. It proposes deepening opportunities — refactors that turn shallow modules into deep ones — using the codebase-design skill's vocabulary and principles (module, interface, depth, seam, adapter, leverage, locality, the deletion test). Load that skill first and use its terms exactly; don't drift into "component," "service," or "boundary."
1. Scope — YAGNI first
Deepening a module pays off by making future changes cheaper, so weight the parts of the codebase that actually change. Decide where to look before looking:
- If the user named a direction — a module, a subsystem, a pain point — take it and skip the inference below.
- If the audit just ran, start from its evidence: the god files from Signal 1, the lava-layer files from Signal 3, the untestable paths from Signal 4.
- Otherwise, walk back a good stretch of
git log --oneline --name-only to find the hot spots — files and areas that keep coming up — and let those paths pull attention first. Scattered changes with no hot spot → widen the net.
2. Explore — friction, not heuristics
Use the Agent tool (subagent_type: Explore) to walk the scoped area. Don't follow rigid checklists — explore organically and note where you experience friction:
- Where does understanding one concept require bouncing between many small modules?
- Where are modules shallow — interface nearly as complex as the implementation?
- Where have pure functions been extracted just for testability, while the real bugs hide in how they're called (no locality)?
- Where do tightly-coupled modules leak across their seams?
- Which parts are untested, or hard to test through their current interface?
Apply the deletion test to anything you suspect is shallow: would deleting it make the complexity vanish (pass-through) or scatter across N callers (earning its keep)? "Vanishes" is the signal you want.
Respect recorded decisions: check meme recall (and ~/.claude/skills/meme/bin/meme search "<module>") for decision facts covering a candidate's area. If a candidate contradicts one, surface it only when the friction is real enough to warrant revisiting the decision, and mark the contradiction on the card — don't relitigate silently, and don't list every refactor a recorded decision forbids.
3. Present candidates as an Artifact
Render the candidates as an Artifact page (load the artifact-design skill first). The page must be fully self-contained — inline CSS and hand-drawn HTML/SVG diagrams; no CDN scripts, so no Tailwind/Mermaid includes.
For each candidate, render a card with:
- Files — which files/modules are involved
- Problem — why the current structure causes friction, tied to the audit signal it feeds when the audit ran
- Solution — plain-English description of what would change; NO interface designs yet
- Benefits — explained as leverage and locality gains, and how the test surface improves
- Before / After diagram — side by side, showing the shallowness and the deepening
- Recommendation strength — a
Strong / Worth exploring / Speculative badge
End with a Top recommendation section: which candidate to tackle first and why. Then ask which candidate the user wants to explore.
4. Hand off to /brainstorm
The chosen candidate goes to /brainstorm — constraints, dependency categories (see the codebase-design skill's DEEPENING.md), the shape of the deepened module, what sits behind the seam, which tests survive. Interface design happens there under its closure discipline, not in the scan. If the user rejects a candidate with a load-bearing reason, draft the rejection with /meme:draft as a decision so a future scan doesn't re-suggest it.