Clean Code β Code Hygiene Gate β
Code that works is not enough. Code must be CLEAN. Inspired by Clean Code (Robert C. Martin) + Refactoring (Martin Fowler) + TRIZ.
When to Use β
ALWAYS when:
- After completing a feature (mandatory hygiene pass before PR)
- After
cm-reactormigration (cleanup dead code from migration) - Before code review (
cm-code-review) β clean FIRST, review AFTER - During technical debt sprints
- When code smells are detected (see Detection section)
- After AI-generated code sessions (AI tends to leave mess)
- When file grows beyond 300 lines
Run automatically after:
cm-executioncompletes a task batchcm-reactorPhase 5 (post-migration cleanup)cm-tddRefactor phase (Red β Green β Refactor)
Skip when:
- Quick hotfix (patch first, clean later β but schedule the cleanup!)
- Prototype/spike code (will be thrown away)
TRIZ Principles Applied β
| # | Principle | How Applied |
|---|---|---|
| #1 | Segmentation | Break large files/functions into focused units |
| #10 | Prior Action | Clean BEFORE it rots β don't wait for tech debt sprint |
| #6 | Universality | One function should serve one purpose (SRP) |
| #27 | Cheap Short-living | Quick small cleanups > expensive large refactors |
| #2 | Taking Out | Extract what doesn't belong β separate concerns |
The 7-Point Hygiene Checklist β
Run this checklist on every file touched. Each point has auto-detect criteria:
βββββ¬βββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββ
β # β Check β Auto-Detect β Action β
βββββΌβββββββββββββββββββββββΌβββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββ€
β 1 β Dead Code β Unused exports, unreachable β DELETE β don't comment β
β β β branches, commented-out code β out, DELETE β
βββββΌβββββββββββββββββββββββΌβββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββ€
β 2 β Unused Imports β Import not used in file β REMOVE import line β
βββββΌβββββββββββββββββββββββΌβββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββ€
β 3 β Magic Numbers β Literal numbers in logic β EXTRACT to named const β
β β & Strings β Repeated string literals β β
βββββΌβββββββββββββββββββββββΌβββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββ€
β 4 β Naming β Single-letter vars (not i,j) β RENAME to describe β
β β β Abbreviations, inconsistent β intent clearly β
βββββΌβββββββββββββββββββββββΌβββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββ€
β 5 β Single Responsibilityβ Function does 2+ things β EXTRACT into separate β
β β (SRP) β Class has 5+ public methods β focused units β
βββββΌβββββββββββββββββββββββΌβββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββ€
β 6 β DRY (Don't Repeat) β Similar code blocks in 2+ β EXTRACT shared logic β
β β β places β into reusable function β
βββββΌβββββββββββββββββββββββΌβββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββ€
β 7 β Nesting Depth β if/for/while nested > 3 β EXTRACT, early return, β
β β β levels deep β guard clauses β
βββββ΄βββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββThe Process β
Phase 1: SCAN β Detect Code Smells β
Goal: Find what's dirty before cleaning.
Automated scan (file-by-file):
For each file modified in current task:
1. SIZE CHECK:
IF lines > 300 β FLAG "Large file β consider splitting"
IF any function > 50 lines β FLAG "Long function β extract methods"
2. IMPORT CHECK:
Scan imports β cross-reference with usage in file body
Unused import β FLAG for removal
3. DEAD CODE CHECK:
Commented-out code blocks β FLAG for deletion
Functions not called anywhere β FLAG (verify with codeintell)
Unreachable code after return/throw β FLAG
4. DUPLICATION CHECK:
Similar code blocks (>5 lines identical/near-identical) β FLAG
Copy-paste patterns β FLAG
5. NAMING CHECK:
Single-char variables (except loop vars i,j,k) β FLAG
Inconsistent casing (camelCase vs snake_case in same file) β FLAG
Generic names (data, result, temp, item, value, obj) β FLAG
6. COMPLEXITY CHECK:
Nesting > 3 levels β FLAG
Function with > 4 parameters β FLAG
Cyclomatic complexity > 10 β FLAGOutput: Smell Report
markdown
## Clean Code Scan: [filename]
| # | Smell | Line | Severity | Auto-fix? |
|---|-------|------|----------|-----------|
| 1 | Unused import: lodash | 3 | Low | β
Yes |
| 2 | Magic number: 86400 | 47 | Medium | β
Yes |
| 3 | Long function: processData (78 lines) | 23-101 | High | π§ Manual |
| 4 | Dead code: commented block | 112-125 | Low | β
Yes |
**Total smells: 4 | Auto-fixable: 3 | Manual: 1**Phase 2: CLEAN β Apply Fixes β
Goal: Fix each smell, one at a time, with tests passing between each fix.
Rules (from refactoring.guru + Martin Fowler):
- Tests first: Ensure tests exist and pass BEFORE cleaning
- One change at a time: Fix one smell β run tests β commit β next smell
- Behavior preservation: Clean code MUST NOT change functionality
- No feature additions: Cleaning and feature work are SEPARATE commits
Fix patterns:
Fix 1: Dead Code & Unused Imports β
Action: DELETE (not comment out)
Rationale: Version control is your backup, not comments
Commit: "clean: remove dead code in [file]"Fix 2: Magic Numbers β Named Constants β
Before: if (retryCount > 3) { ... }
setTimeout(fn, 86400000)
After: const MAX_RETRIES = 3;
const ONE_DAY_MS = 86_400_000;
if (retryCount > MAX_RETRIES) { ... }
setTimeout(fn, ONE_DAY_MS)
Commit: "clean: extract magic numbers in [file]"Fix 3: Extract Method (TRIZ #2 Taking Out) β
Before: 50+ line function doing validation + calculation + persistence
After: function processOrder(order) {
validateOrder(order);
const total = calculateTotal(order);
return persistOrder(order, total);
}
Commit: "clean: extract methods from [function] in [file]"Fix 4: Reduce Nesting (Guard Clauses) β
Before: function foo(x) {
if (x) {
if (x.valid) {
if (x.items.length > 0) {
// actual logic buried 3 levels deep
}
}
}
}
After: function foo(x) {
if (!x) return;
if (!x.valid) return;
if (x.items.length === 0) return;
// actual logic at top level
}
Commit: "clean: reduce nesting with guard clauses in [file]"Fix 5: DRY β Extract Shared Logic β
Before: // In file_a.ts
const formatted = `${date.getFullYear()}-${date.getMonth()+1}-${date.getDate()}`
// In file_b.ts (same pattern)
const formatted = `${d.getFullYear()}-${d.getMonth()+1}-${d.getDate()}`
After: // In utils/date.ts
export function formatDate(date: Date): string { ... }
// Both files import formatDate()
Commit: "clean: extract shared date formatting to utils"Fix 6: Improve Naming β
Before: const d = getData();
const r = process(d);
const x = r.filter(i => i.v > 0);
After: const orders = fetchPendingOrders();
const processedOrders = applyDiscounts(orders);
const validOrders = processedOrders.filter(order => order.total > 0);
Commit: "clean: improve naming in [file]"Phase 3: VERIFY β Confirm Cleanliness β
Goal: Ensure cleanup didn't break anything and meets standards.
Verification Checklist:
β‘ All tests pass (exact same test results as before)
β‘ No functionality changed (behavior preservation)
β‘ 7-Point Checklist passes on all modified files
β‘ No new code smells introduced
β‘ Commit history is clean (one commit per fix type)Re-run Phase 1 scan on cleaned files:
- If smells remain β iterate Phase 2-3
- If clean β mark task as done
SOLID Quick Reference β
Apply SOLID during Phase 2 when restructuring:
| Principle | Meaning | Quick Test |
|---|---|---|
| S β Single Responsibility | One class/function = one reason to change | "Can you describe what it does in one sentence without 'and'?" |
| O β Open/Closed | Open for extension, closed for modification | "Can you add behavior without changing existing code?" |
| L β Liskov Substitution | Subtypes must be substitutable | "Can you swap implementations without breaking callers?" |
| I β Interface Segregation | No client forced to depend on unused methods | "Does every consumer use all methods of this interface?" |
| D β Dependency Inversion | Depend on abstractions, not concretions | "Do high-level modules import from low-level modules?" |
Red Flags β STOP β
| Thought | Reality |
|---|---|
| "It works, don't touch it" | Working β maintainable. Clean it now or pay 10x later |
| "I'll clean it up later" | Later never comes. Clean after each feature |
| "Cleaning wastes time" | 10 min cleaning saves 2 hours debugging later |
| "It's just one small hack" | Hacks compound. Today's hack is tomorrow's tech debt |
| "AI generated it, must be clean" | AI creates functional code, rarely clean code |
| "Let me refactor AND add features" | Separate commits. Never mix. |
| "Comments explain the complex code" | If code needs comments, simplify the CODE |
Commit Convention β
clean: remove dead code in [file/module]
clean: extract magic numbers in [file]
clean: improve naming in [file]
clean: reduce nesting in [function]
clean: extract [method] from [function]
clean: remove unused imports in [file]
clean: apply DRY β extract shared [logic] to [util]Integration β
| Skill | Relationship |
|---|---|
cm-tdd | Tests MUST pass before and after cleaning (Red β Green β Refactor) |
cm-reactor | Reactor triggers cm-clean-code in Phase 5 (post-migration cleanup) |
cm-execution | After task batch β run hygiene pass |
cm-code-review | Clean FIRST β review AFTER (reviewers see clean code) |
cm-quality-gate | Cleanliness as a quality dimension |
cm-debugging | After bug fix β clean the surrounding code |
cm-continuity | Record cleanup decisions and patterns learned |
cm-codeintell | Verify dead code with call graph before deleting |
Lifecycle Position β
cm-execution β cm-clean-code β cm-code-review β cm-quality-gate β cm-safe-deploy
(build) (hygiene) (review) (verify) (ship)The Bottom Line β
Clean code = investment. Dirty code = debt. Pay as you go, or pay with interest later.