skills/kaakati/rails-enterprise-dev/Technical Debt Patterns

Technical Debt Patterns

SKILL.md

Technical Debt Patterns

Expert patterns for detecting, categorizing, and prioritizing technical debt in Rails applications.

Decision Tree: Debt Category Identification

What type of issue is this?
├─ Code structure problem?
│   ├─ Method too long (>20 lines) → Code Smell: Long Method
│   ├─ Class too large (>150 lines) → Code Smell: Large Class
│   ├─ Excessive parameter passing → Code Smell: Data Clump
│   └─ Method uses another object's data → Code Smell: Feature Envy
├─ Complexity issue?
│   ├─ Flog score >60 → Complexity: High
│   ├─ Cyclomatic complexity >10 → Complexity: High
│   ├─ Deep nesting (>3 levels) → Complexity: Nesting
│   └─ Too many conditionals → Complexity: Conditional
├─ Security concern?
│   ├─ SQL injection risk → Security: SQL Injection
│   ├─ XSS vulnerability → Security: XSS
│   ├─ Mass assignment issue → Security: Mass Assignment
│   └─ Outdated gem with CVE → Security: Dependency
├─ Outdated code?
│   ├─ Rails deprecation warning → Deprecation: Rails
│   ├─ Ruby version warning → Deprecation: Ruby
│   └─ Deprecated gem API → Deprecation: Gem
├─ Performance problem?
│   ├─ N+1 query pattern → Performance: N+1
│   ├─ Missing database index → Performance: Index
│   ├─ Memory bloat → Performance: Memory
│   └─ Slow query → Performance: Query
└─ Architecture violation?
    ├─ Fat controller → Architecture: Controller
    ├─ God object/model → Architecture: God Object
    ├─ Circular dependency → Architecture: Circular
    └─ Layer violation → Architecture: Layering

NEVER Do These (Critical Anti-Patterns)

NEVER ignore security debt because "we'll fix it later":

# WRONG - Ignoring SQL injection
User.where("name = '#{params[:name]}'")  # Security debt accumulates risk

# RIGHT - Fix immediately or track with Critical severity
User.where(name: params[:name])

→ Security debt has exponential risk growth. Track as Critical, not backlog.

NEVER create technical debt to "fix" technical debt:

# WRONG - Adding wrapper to hide complexity
class PaymentWrapper
  def process
    @legacy_payment.complex_legacy_method  # Just hiding the problem
  end
end

# RIGHT - Either refactor properly or track explicitly
# If time-constrained, create beads issue with clear scope

→ Debt wrappers compound into "debt squared". Refactor or track, don't hide.

NEVER disable linters globally to silence debt warnings:

# WRONG - Global disable in .rubocop.yml
Metrics/MethodLength:
  Enabled: false  # Hides ALL long method debt

# RIGHT - Explicit inline disable with justification
# rubocop:disable Metrics/MethodLength -- Legacy payment processor, tracked in PROJ-123
def complex_legacy_method
  # ...
end
# rubocop:enable Metrics/MethodLength

→ Global disables hide debt accumulation. Use inline disables with tracking.

NEVER skip team discussion on Critical/High debt:

# WRONG - Solo decision on major debt
"I found a god object, I'll refactor it this sprint"

# RIGHT - Team alignment first
1. Document finding in debt report
2. Create beads issue with severity
3. Discuss in sprint planning
4. Get consensus on approach

→ Major refactoring affects the whole team. Collaborate before large changes.

NEVER estimate features without considering debt in affected areas:

# WRONG - Ignoring debt in estimates
"Add payment retry logic: 2 story points"

# RIGHT - Include debt impact
"Add payment retry logic: 5 story points
 - 2 points: feature implementation
 - 3 points: PaymentService complexity (Flog 127) requires refactoring first"

→ Debt adds hidden cost. Include remediation in feature estimates.


Code Smell Detection Patterns

Long Method (>20 lines)

Detection:

# Find methods longer than 20 lines
awk '
  /^[[:space:]]*def / { start = NR; name = $2 }
  /^[[:space:]]*end/ && start > 0 {
    len = NR - start
    if (len > 20) print FILENAME ":" name " (" len " lines)"
    start = 0
  }
' app/**/*.rb

Severity Thresholds:

Lines Severity
20-40 Medium
40-80 High
>80 Critical

Large Class (>150 lines)

Detection:

# Find classes larger than 150 lines
for file in app/models/*.rb app/services/*.rb; do
  lines=$(wc -l < "$file" 2>/dev/null)
  if [ "$lines" -gt 150 ]; then
    echo "$file: $lines lines"
  fi
done

Severity Thresholds:

Lines Severity
150-300 Medium
300-500 High
>500 Critical

Feature Envy

Method uses another object's data more than its own.

Detection Pattern:

# Smell: Method calls another object's methods repeatedly
def calculate_total(order)
  order.items.sum(&:price) +
    order.shipping_cost +
    order.tax_amount -
    order.discount_amount
end

# Fix: Move method to Order class
class Order
  def calculate_total
    items.sum(&:price) + shipping_cost + tax_amount - discount_amount
  end
end

Data Clump

Same group of parameters passed together repeatedly.

Detection Pattern:

# Smell: Repeated parameter group
def create_user(name, email, phone, address)
def update_user(name, email, phone, address)
def validate_user(name, email, phone, address)

# Fix: Extract to value object
class ContactInfo
  attr_reader :name, :email, :phone, :address
end

def create_user(contact_info)
def update_user(contact_info)

Complexity Metrics

Flog Score Thresholds

Score Rating Action
< 30 Low No action needed
30-60 Medium Consider refactoring
60-100 High Plan refactoring
> 100 Critical Immediate refactoring

Running Flog:

# Overall complexity score
flog -q -g app/

# Top 10 most complex methods
flog -q app/ | head -10

# Score for specific file
flog app/services/payment_service.rb

Cyclomatic Complexity

Measures independent execution paths through code.

Complexity Rating Risk
1-5 Low Easy to test
6-10 Medium Moderate risk
11-20 High Difficult to test
>20 Critical Very high risk

Severity Scoring Framework

Calculate overall severity using weighted factors:

Severity Score = (Blast × 0.30) + (Fix × 0.20) + (Risk × 0.30) + (Age × 0.10) + (Freq × 0.10)
Factor Weight 1 (Low) 3 (Medium) 5 (High)
Blast Radius 30% Single file Module System-wide
Fix Complexity 20% Trivial Moderate Major refactor
Risk Level 30% Cosmetic Functional Security/Data
Age 10% < 6 months 6mo-2yr > 2 years
Frequency 10% Rare path Normal Hot path

Severity Categories:

  • Critical: Score >= 4.0 (SLA: 1 sprint)
  • High: Score >= 3.0 (SLA: 2 sprints)
  • Medium: Score >= 2.0 (SLA: Quarterly)
  • Low: Score < 2.0 (Opportunistic)

Quick Reference Tables

Detection Tools

Tool Purpose Command
Flog Complexity scoring flog -q app/
Reek Code smell detection reek app/
Rubocop Style + metrics rubocop --format json
Brakeman Security vulnerabilities brakeman -q
bundler-audit Gem CVEs bundle-audit check
rails_best_practices Rails anti-patterns rails_best_practices

Effort Estimation

Category Typical Effort
Long Method refactor 2-4 hours
Large Class extraction 1-3 days
God Object decomposition 3-5 days
N+1 query fix 1-2 hours
Security vulnerability 2-8 hours
Deprecation update 2-4 hours
Circular dependency fix 2-5 days

Beads Integration

# Create debt issue
bd create --type task --priority 1 \
  --title "Tech Debt: [Description]" \
  --description "[Details]"

# Track debt item
bd update PROJ-123 --status in_progress

# Close after remediation
bd close PROJ-123 --reason "Refactored in commit abc123"

References

For detailed patterns in each category, see:

  • references/code-smells.md - Long method, large class, feature envy, data clump
  • references/complexity-metrics.md - Flog, cyclomatic, cognitive complexity
  • references/deprecation-tracking.md - Rails, Ruby, gem deprecations
  • references/security-debt.md - Brakeman categories, OWASP patterns
  • references/performance-debt.md - N+1 queries, indexes, memory
  • references/testing-debt.md - Coverage gaps, flaky tests
  • references/architecture-debt.md - God objects, circular deps, layers

Integration with Other Skills

Skill Integration Point
code-quality-gates Rubocop/Sorbet findings feed into debt report
refactoring-workflow Debt items become refactoring targets
rails-conventions Convention violations are architecture debt
codebase-inspection Inspector discovers debt during analysis
Weekly Installs
0
GitHub Stars
6
First Seen
Jan 1, 1970