Claude
Skills
Sign in
Back

tech-debt-report

Included with Lifetime
$97 forever

Quantifies technical debt across the codebase by scanning for code smells, outdated dependencies, complexity hotspots, missing tests, TODOs, dead code, and architectural issues. Produces a prioritized report with effort estimates and business impact. Saves output to project-decisions/ folder. Use when the user says "tech debt report", "how much tech debt", "technical debt", "code health", "codebase health check", "what needs cleanup", "debt audit", "code quality report", "health check", or "what's the state of our codebase?".

Security

What this skill does


# Tech Debt Report Skill

When generating a tech debt report, follow this structured process. The goal is to turn the vague feeling of "our code is messy" into a quantified, prioritized list that leadership and engineering can use to make informed decisions about debt paydown.

**IMPORTANT**: Always save the output as a markdown file in the `project-decisions/` directory at the project root. Create the directory if it doesn't exist.

## 0. Output Setup
```bash
# Create project-decisions directory if it doesn't exist
mkdir -p project-decisions

# File will be saved as:
# project-decisions/YYYY-MM-DD-tech-debt-report.md
```

## 1. Codebase Overview

### Project Stats
```bash
# Total lines of code (excluding deps and build)
find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.py" -o -name "*.go" -o -name "*.java" -o -name "*.rb" -o -name "*.php" -o -name "*.rs" \) ! -path '*/node_modules/*' ! -path '*/vendor/*' ! -path '*/dist/*' ! -path '*/build/*' ! -path '*/.git/*' -exec cat {} + 2>/dev/null | wc -l

# File count by language
find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.py" -o -name "*.go" -o -name "*.java" -o -name "*.rb" -o -name "*.php" \) ! -path '*/node_modules/*' ! -path '*/vendor/*' ! -path '*/dist/*' ! -path '*/build/*' | sed 's/.*\.//' | sort | uniq -c | sort -rn

# Total source files
find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.py" -o -name "*.go" \) ! -path '*/node_modules/*' ! -path '*/dist/*' ! -path '*/build/*' | wc -l

# Total test files
find . -type f \( -name "*.test.*" -o -name "*.spec.*" -o -name "test_*" -o -name "*_test.*" \) ! -path '*/node_modules/*' | wc -l

# Test to source ratio
echo "Source files: $(find . -type f \( -name '*.ts' -o -name '*.js' -o -name '*.py' \) ! -path '*/node_modules/*' ! -path '*/dist/*' ! -name '*.test.*' ! -name '*.spec.*' ! -name 'test_*' | wc -l)"
echo "Test files: $(find . -type f \( -name '*.test.*' -o -name '*.spec.*' -o -name 'test_*' \) ! -path '*/node_modules/*' | wc -l)"

# Age of the codebase
echo "First commit: $(git log --reverse --format='%ai' | head -1)"
echo "Latest commit: $(git log --format='%ai' -1)"
echo "Total commits: $(git rev-list --count HEAD)"
echo "Contributors: $(git log --format='%aN' | sort -u | wc -l)"

# Directory structure
find . -maxdepth 2 -type d ! -path '*/node_modules/*' ! -path '*/.git/*' ! -path '*/dist/*' ! -path '*/build/*' ! -path '*/vendor/*' | sort
```

## 2. Debt Category Scans

### 2a. Code Complexity Debt

#### Large Files
```bash
# Files over 500 lines (likely need splitting)
find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.py" -o -name "*.go" -o -name "*.java" -o -name "*.rb" -o -name "*.php" \) ! -path '*/node_modules/*' ! -path '*/dist/*' ! -path '*/build/*' -exec wc -l {} + 2>/dev/null | sort -rn | head -20

# Files over 300 lines (getting unwieldy)
find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.py" \) ! -path '*/node_modules/*' ! -path '*/dist/*' -exec sh -c 'lines=$(wc -l < "$1"); if [ "$lines" -gt 300 ]; then echo "$lines $1"; fi' _ {} \; 2>/dev/null | sort -rn

# Count of oversized files
echo "Files > 500 lines: $(find . -type f \( -name '*.ts' -o -name '*.js' -o -name '*.py' \) ! -path '*/node_modules/*' ! -path '*/dist/*' -exec sh -c 'lines=$(wc -l < "$1"); if [ "$lines" -gt 500 ]; then echo "$1"; fi' _ {} \; 2>/dev/null | wc -l)"
echo "Files > 300 lines: $(find . -type f \( -name '*.ts' -o -name '*.js' -o -name '*.py' \) ! -path '*/node_modules/*' ! -path '*/dist/*' -exec sh -c 'lines=$(wc -l < "$1"); if [ "$lines" -gt 300 ]; then echo "$1"; fi' _ {} \; 2>/dev/null | wc -l)"
```

#### Long Functions
```bash
# Find long functions (rough heuristic for JS/TS)
grep -rn "^\s*\(export \)\?\(async \)\?\(function\|const.*=>.*{\)" --include="*.ts" --include="*.js" src/ 2>/dev/null | head -30

# For Python
grep -rn "^\s*def \|^\s*async def " --include="*.py" src/ app/ 2>/dev/null | head -30

# Deep nesting (indentation > 4 levels)
find . -type f \( -name "*.ts" -o -name "*.js" -o -name "*.py" \) ! -path '*/node_modules/*' ! -path '*/dist/*' -exec sh -c '
  deep=$(awk "{ match(\$0, /^[[:space:]]*/); if (RLENGTH > 16) print FILENAME\":\"NR\": \"RLENGTH/2\" levels - \"\$0 }" "$1" | head -5)
  if [ -n "$deep" ]; then echo "$deep"; fi
' _ {} \; 2>/dev/null | head -20
```

#### Code Duplication
```bash
# Find duplicate lines (rough detection)
find . -type f \( -name "*.ts" -o -name "*.js" -o -name "*.py" \) ! -path '*/node_modules/*' ! -path '*/dist/*' ! -path '*/test*' -exec cat {} + 2>/dev/null | sort | uniq -c | sort -rn | grep -v "^\s*[0-9]* \s*$\|^\s*[0-9]* import\|^\s*[0-9]* //\|^\s*[0-9]* #\|^\s*[0-9]* }\|^\s*[0-9]* {" | head -20

# Find similarly named functions across files (possible duplication)
grep -rn "function \|const.*= \(.*\) =>\|def \|func " --include="*.ts" --include="*.js" --include="*.py" --include="*.go" src/ 2>/dev/null | awk -F'[( ]' '{print $NF}' | sort | uniq -c | sort -rn | awk '$1 > 2' | head -20
```

#### Any Type Abuse (TypeScript)
```bash
# Count `any` usage
echo "Total 'any' types: $(grep -rn ": any\|<any>\|as any\| any;" --include="*.ts" --include="*.tsx" src/ 2>/dev/null | grep -v "node_modules\|test\|spec\|\.d\.ts" | wc -l)"

# List files with most `any` usage
grep -rln ": any\|<any>\|as any" --include="*.ts" --include="*.tsx" src/ 2>/dev/null | grep -v "node_modules\|test\|spec\|\.d\.ts" | xargs -I{} sh -c 'echo "$(grep -c ": any\|<any>\|as any" "$1") $1"' _ {} 2>/dev/null | sort -rn | head -15

# @ts-ignore / @ts-expect-error
echo "Total @ts-ignore: $(grep -rn "@ts-ignore\|@ts-expect-error\|@ts-nocheck" --include="*.ts" --include="*.tsx" src/ 2>/dev/null | grep -v "node_modules" | wc -l)"
```

### 2b. TODO / FIXME / HACK Debt
```bash
# Count all debt markers
echo "=== Debt Markers ==="
echo "TODO: $(grep -rn "TODO" --include="*.ts" --include="*.js" --include="*.py" --include="*.go" --include="*.java" --include="*.rb" --include="*.php" src/ app/ 2>/dev/null | grep -v "node_modules" | wc -l)"
echo "FIXME: $(grep -rn "FIXME" --include="*.ts" --include="*.js" --include="*.py" --include="*.go" src/ app/ 2>/dev/null | grep -v "node_modules" | wc -l)"
echo "HACK: $(grep -rn "HACK" --include="*.ts" --include="*.js" --include="*.py" --include="*.go" src/ app/ 2>/dev/null | grep -v "node_modules" | wc -l)"
echo "XXX: $(grep -rn "XXX" --include="*.ts" --include="*.js" --include="*.py" --include="*.go" src/ app/ 2>/dev/null | grep -v "node_modules" | wc -l)"
echo "WORKAROUND: $(grep -rn "WORKAROUND\|workaround" --include="*.ts" --include="*.js" --include="*.py" src/ app/ 2>/dev/null | grep -v "node_modules" | wc -l)"
echo "TEMPORARY: $(grep -rn "TEMPORARY\|temporary\|TEMP " --include="*.ts" --include="*.js" --include="*.py" src/ app/ 2>/dev/null | grep -v "node_modules" | wc -l)"
echo "DEPRECATED: $(grep -rn "@deprecated\|DEPRECATED" --include="*.ts" --include="*.js" --include="*.py" src/ app/ 2>/dev/null | grep -v "node_modules" | wc -l)"

# List all with context
grep -rn "TODO\|FIXME\|HACK\|XXX\|WORKAROUND" --include="*.ts" --include="*.js" --include="*.py" --include="*.go" src/ app/ 2>/dev/null | grep -v "node_modules"

# TODOs without ticket numbers (untracked debt)
grep -rn "TODO" --include="*.ts" --include="*.js" --include="*.py" src/ app/ 2>/dev/null | grep -v "node_modules" | grep -v "#[0-9]\|JIRA-\|[A-Z]\+-[0-9]\+" | head -20

# Age of TODOs (when were they added?)
for todo in $(grep -rln "TODO\|FIXME\|HACK" --include="*.ts" --include="*.js" --include="*.py" src/ 2>/dev/null | grep -v "node_modules" | head -10); do
  echo "=== $todo ==="
  git log -1 --format="%ai %s" -- "$todo" 2>/dev/null
done
```

### 2c. Dependency Debt
```bash
# Outdated packages
npm outdated 2>/dev/null | head -30
pip list --outdated 2>/dev/null | head -30

# Security vulnerabilities
npm audit --json 2>/dev/null | python3 -c "
im

Related in Security