Files
gh-buzzdan-ai-coding-rules-…/skills/linter-driven-development/reference.md
2025-11-29 18:02:45 +08:00

13 KiB

Linter-Driven Development Reference (TypeScript + React)

Overview

The linter-driven development workflow ensures code quality through automated tooling and design validation. This orchestrator manages the complete lifecycle from design to commit-ready code.

Quality Tool Stack

1. TypeScript Compiler (tsc)

Command: npm run typecheck Purpose: Type safety validation Can auto-fix: No Failure resolution: Manual type fixes or refactoring

2. ESLint

Command:

  • Check: npm run lintcheck
  • Fix: npm run lint

Purpose: Code quality, style, and complexity analysis Plugins used:

  • eslint-plugin-sonarjs - Complexity metrics (THE KEY PLUGIN)
  • typescript-eslint - TypeScript-aware linting
  • eslint-plugin-react - React best practices
  • eslint-plugin-react-hooks - Hooks rules enforcement
  • eslint-plugin-jsx-a11y - Accessibility rules
  • eslint-plugin-import - Import/export management
  • eslint-plugin-unused-imports - Remove dead code
  • eslint-plugin-simple-import-sort - Auto-sort imports
  • eslint-plugin-promise - Async/await best practices
  • eslint-plugin-security - Security vulnerabilities

Can auto-fix: Many rules (formatting, imports, simple violations) Requires refactoring: Complexity rules, design issues

3. Prettier

Command:

  • Check: npm run formatcheck
  • Fix: npm run format

Purpose: Code formatting consistency Can auto-fix: Always (100% auto-fixable)

4. Stylelint

Command:

  • Check: npm run stylecheck
  • Fix: npm run stylefix

Purpose: SCSS/CSS linting Can auto-fix: Most rules

Workflow Phases in Detail

Phase 1: Design

Trigger: New components, custom hooks, major architectural changes

Actions:

  1. Invoke @component-designing skill
  2. Answer design questions:
    • Component composition strategy?
    • State management approach?
    • Custom hooks needed?
    • Type definitions required?
  3. Receive design plan with:
    • Component structure
    • Props interfaces
    • Custom hooks
    • Type definitions
    • File organization

Output: Design document ready for implementation

Phase 2: Implementation + Testing

Testing principles (from @testing skill):

  • Write tests for public API only
  • Use React Testing Library patterns
  • Test user behavior, not implementation
  • Use MSW for API mocking (real HTTP)
  • Avoid waitFor with arbitrary delays
  • Achieve 100% coverage on leaf components/hooks (no dependencies on other types)
  • Integration tests for orchestrating components (test interactions and composition)

Implementation approach:

  • Parallel development (test + code together)
  • Focus on behavior validation
  • Use real implementations over mocks
  • Follow component composition patterns
  • Push business logic into leaf types for better testability

Phase 3: Linter Loop

This is the core quality gate with multiple sub-checks:

Step 1: Type Checking

npm run typecheck

Checks: TypeScript compilation, type safety Failures: Type errors, missing types, type mismatches Resolution:

  • Fix types manually
  • Add type assertions where needed
  • Use type guards for narrowing
  • Cannot proceed if failing

Step 2: ESLint Check

npm run lintcheck

Checks: Code quality, complexity, React rules, hooks rules, a11y Failures: See "ESLint Failure Categories" below Resolution:

  • Auto-fix: npm run lint
  • If auto-fix insufficient → manual fixes or invoke @refactoring

Step 3: Prettier Check

npm run formatcheck

Checks: Code formatting consistency Failures: Inconsistent formatting Resolution: Always auto-fix with npm run format

Step 4: Stylelint Check

npm run stylecheck

Checks: SCSS/CSS quality and consistency Failures: Style violations, naming issues Resolution: Auto-fix with npm run stylefix, some manual fixes

Loop Behavior

Run all checks → Any fail? → Run auto-fixes → Re-run checks
                       ↓
                    Still failing?
                       ↓
              Complexity/design issues?
                       ↓
              Invoke @refactoring
                       ↓
                  Re-run checks
                       ↓
                Loop until pass

Phase 4: Pre-Commit Review (Advisory)

Always invoked, even if linter passes.

Purpose: Validate design principles that linters cannot enforce

Scope:

  • Primary: All changed code in current commit
  • Secondary: Broader file context (flags patterns for future refactoring)

Categories:

  • 🔴 Design Debt: Will cause pain when extending code

    • Primitive obsession (string IDs, unvalidated inputs)
    • Prop drilling (state passed through 3+ levels)
    • Tight coupling
    • Missing error boundaries
    • Non-self-validating types
  • 🟡 Readability Debt: Hard to understand and work with

    • Mixed abstraction levels
    • Complex nested logic
    • Inline styles or logic
    • Poor naming
    • Missing component extraction
  • 🟢 Polish Opportunities: Minor improvements

    • Missing JSDoc
    • Accessibility enhancements
    • Type refinements
    • Better naming

Output: Advisory report with specific line references and fix suggestions

User Decision Points:

  1. Commit as-is (accept debt)
  2. Fix design debt (🔴) - recommended
  3. Fix design + readability (🔴 + 🟡)
  4. Fix all (🔴 🟡 🟢)
  5. Expand scope (refactor related code)

Phase 5: Commit Ready

Checklist:

  • Type checking passes
  • ESLint passes
  • Prettier passes
  • Stylelint passes
  • Tests pass (100% coverage on leaf types, integration tests on orchestrating components)
  • Design review complete (advisory)

Output:

  • Commit readiness summary
  • Suggested commit message
  • List of modified/added files
  • Coverage report
  • Design review findings
  • User decision prompt

Coverage Targets

Follow @testing skill principles for coverage strategy:

  • Leaf types (pure logic, no external dependencies): 100% unit test coverage
  • Orchestrating types (coordinate pieces, call external systems): Integration tests

See testing/reference.md for:

  • Detailed coverage targets explanation
  • Examples of leaf vs orchestrating types
  • Testing approach for each type
  • Architectural benefits

ESLint Failure Categories

Category 1: Auto-Fixable (npm run lint)

These are fixed automatically:

  • Unused imports (unused-imports/no-unused-imports)
  • Import sorting (simple-import-sort/imports)
  • Missing semicolons, quotes, spacing (handled by Prettier)
  • Simple style violations
  • Arrow function simplification (arrow-body-style)

Action: Run npm run lint → Re-run checks → Continue

Category 2: Requires Manual Fix

These need developer intervention but are straightforward:

  • @typescript-eslint/no-explicit-any - Replace any with proper types
  • no-console - Remove or replace with proper logging
  • react/jsx-key - Add key props to list items
  • react-hooks/exhaustive-deps - Fix hook dependencies
  • Type-related issues

Action: Fix issues manually → Re-run checks → Continue

Category 3: Requires Refactoring (invoke @refactoring)

These indicate design or complexity problems:

  • sonarjs/cognitive-complexity (max: 15)
  • sonarjs/cyclomatic-complexity (max: 10)
  • sonarjs/expression-complexity (max: 5)
  • sonarjs/max-lines-per-function (max: 200)
  • sonarjs/max-lines (max: 600)
  • sonarjs/nested-control-flow (max: 4 levels)
  • react/no-unstable-nested-components - Extract components
  • react/no-multi-comp - Split into multiple files

Action: Invoke @refactoring skill → Apply patterns (storifying, extract hooks/functions, early returns, simplify conditionals) → Re-run checks → Continue

Complexity Thresholds Explained

Cognitive Complexity (max: 15)

What it measures: How difficult is it to understand the code? Increments for: Nested structures, breaks in linear flow, recursion Why it matters: High cognitive load → more bugs, harder maintenance

How to fix: Invoke @refactoring skill which applies storifying - making code read like a story at single abstraction level. See refactoring/reference.md for detailed techniques and examples.

Example violation:

// Cognitive complexity: 18 (too high!)
function validateUser(user: User): ValidationResult {
  if (user) {  // +1
    if (user.email) {  // +2 (nested)
      if (isValidEmail(user.email)) {  // +3 (nested)
        if (user.age >= 18) {  // +4 (nested)
          if (user.country === 'US') {  // +5 (nested)
            return { valid: true }
          } else {  // +1
            return { valid: false, reason: 'Not in US' }
          }
        }
      } else {  // +1
        return { valid: false, reason: 'Invalid email' }
      }
    }
  }
  return { valid: false, reason: 'Missing user' }
}

How to fix: Invoke @refactoring skill to storify with early returns (see refactoring/reference.md)

Cyclomatic Complexity (max: 10)

What it measures: Number of independent paths through code Increments for: if, else, case, &&, ||, while, for, catch Why it matters: More paths → more test cases needed, higher bug risk

Fix strategies: Extract functions, use polymorphism, simplify conditionals

Expression Complexity (max: 5)

What it measures: Number of operators in a single expression Increments for: &&, ||, ternary operators Why it matters: Hard to read, error-prone

Example violation:

// Expression complexity: 6 (too high!)
const isValid = user && user.email && isValidEmail(user.email) && user.age >= 18 && user.country === 'US' && !user.banned

Fix: Extract to variables or validation function

Storifying Pattern

When cognitive complexity is high, invoke @refactoring skill which applies storifying patterns (making code read like a story at single abstraction level).

See refactoring/reference.md for:

  • Detailed storifying explanation with examples
  • TypeScript and React examples
  • When and how to apply storifying
  • Step-by-step storifying process

Integration with Other Skills

@component-designing

When invoked: Phase 1, if new components/major changes needed Input: Feature requirements Output: Component structure, props, hooks, types Next step: Proceed to Phase 2 implementation

@testing

When applied: Phase 2, during implementation Input: Component/hook to test Output: Test files with React Testing Library Principles: User-centric testing, real implementations

@refactoring

When invoked: Phase 3, when linter fails on complexity Input: Failing component/function + linter error Output: Refactored code that passes linter Patterns: Storifying, extract hooks/functions, simplify logic, early returns, single abstraction levels

@pre-commit-review

When invoked: Phase 4, always (advisory) Input: All changed code + file context Output: Design review findings (🔴 🟡 🟢) Decision: User chooses whether to apply fixes

@documentation

When invoked: After commit (feature complete) Input: Implemented feature Output: Storybook stories, JSDoc, feature docs Purpose: Documentation for humans and AI

Command Alternatives

Different projects may use different naming conventions:

Option 1: Separate check/fix commands

# Checks (validation only)
npm run typecheck
npm run lintcheck
npm run formatcheck
npm run stylecheck

# Fixes (auto-fix where possible)
npm run lint
npm run format
npm run stylefix

Option 2: Combined commands

# Check all quality gates
npm run checkall  # or npm run check

# Fix all auto-fixable issues
npm run fix

Option 3: Task runner (if available)

# Using task runner (e.g., make, task)
task lint
task fix

Plugin should detect which commands are available from package.json scripts.

Best Practices

1. Fail Fast, Fix Fast

  • Run linter frequently during development
  • Don't accumulate linter errors
  • Fix auto-fixable issues immediately

2. Trust Complexity Metrics

  • SonarJS complexity rules are calibrated well
  • If it flags complexity → there's real complexity
  • Refactor rather than disable

3. Respect Advisory Review

  • Design debt compounds over time
  • Fix 🔴 before committing when possible
  • Track accepted debt in tickets

4. Test After Refactoring

  • Complexity fixes can introduce bugs
  • Re-run tests after @refactoring
  • Verify behavior unchanged

5. Commit Granularly

  • Small, focused commits
  • Each commit passes all gates
  • Easy to review and revert

Troubleshooting

"Type errors won't go away"

  • TypeScript errors require manual fixes
  • Consider if types are correct (not the code)
  • Use type guards for narrowing
  • Add type assertions as last resort

"ESLint keeps failing after auto-fix"

  • Auto-fix only handles simple rules
  • Complexity rules need refactoring
  • Invoke @refactoring skill
  • May need architectural changes

"Linter passes but review finds issues"

  • Expected! Linters can't enforce design principles
  • Review catches: primitive obsession, coupling, architecture
  • User decides whether to fix

"Too many findings in review"

  • Common for legacy code
  • Fix incrementally (design debt first)
  • Consider broader refactor ticket
  • Don't let perfect be enemy of good
  • For design patterns: See @component-designing/reference.md
  • For testing strategies: See @testing/reference.md
  • For refactoring patterns: See @refactoring/reference.md
  • For review principles: See @pre-commit-review/reference.md