Skip to content

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-reactor migration (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-execution completes a task batch
  • cm-reactor Phase 5 (post-migration cleanup)
  • cm-tdd Refactor 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 ​

#PrincipleHow Applied
#1SegmentationBreak large files/functions into focused units
#10Prior ActionClean BEFORE it rots β€” don't wait for tech debt sprint
#6UniversalityOne function should serve one purpose (SRP)
#27Cheap Short-livingQuick small cleanups > expensive large refactors
#2Taking OutExtract 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 β†’ FLAG

Output: 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):

  1. Tests first: Ensure tests exist and pass BEFORE cleaning
  2. One change at a time: Fix one smell β†’ run tests β†’ commit β†’ next smell
  3. Behavior preservation: Clean code MUST NOT change functionality
  4. 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:

PrincipleMeaningQuick Test
S β€” Single ResponsibilityOne class/function = one reason to change"Can you describe what it does in one sentence without 'and'?"
O β€” Open/ClosedOpen for extension, closed for modification"Can you add behavior without changing existing code?"
L β€” Liskov SubstitutionSubtypes must be substitutable"Can you swap implementations without breaking callers?"
I β€” Interface SegregationNo client forced to depend on unused methods"Does every consumer use all methods of this interface?"
D β€” Dependency InversionDepend on abstractions, not concretions"Do high-level modules import from low-level modules?"

Red Flags β€” STOP ​

ThoughtReality
"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 ​

SkillRelationship
cm-tddTests MUST pass before and after cleaning (Red β†’ Green β†’ Refactor)
cm-reactorReactor triggers cm-clean-code in Phase 5 (post-migration cleanup)
cm-executionAfter task batch β†’ run hygiene pass
cm-code-reviewClean FIRST β†’ review AFTER (reviewers see clean code)
cm-quality-gateCleanliness as a quality dimension
cm-debuggingAfter bug fix β†’ clean the surrounding code
cm-continuityRecord cleanup decisions and patterns learned
cm-codeintellVerify 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.

Open Source AI Agent Skills Framework