modular-refactoring-pattern

Modular Refactoring Pattern for @j0kz/mcp-agents

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "modular-refactoring-pattern" with this command: npx skills add j0kz/mcp-agents/j0kz-mcp-agents-modular-refactoring-pattern

Modular Refactoring Pattern for @j0kz/mcp-agents

Systematic approach to keeping files under 300 LOC through strategic extraction.

Philosophy: Modular Architecture

Core Principle: Keep files focused and maintainable through consistent extraction patterns.

Established 2025: 4 packages successfully refactored with proven results.

Quality Targets

File Size

  • Target: <300 LOC per file

  • Maximum: 500 LOC (hard limit)

  • Action: Extract when approaching 300 LOC

Complexity

  • Target: <50 per file

  • Warning: >70 per file

  • Action: Simplify or extract complex functions

Duplication

  • Target: 0% duplicate code blocks

  • Action: Extract shared code to helpers/utils

When to Refactor (Triggers)

Trigger 1: File Size Exceeds 300 LOC

Check file sizes

wc -l packages//src/**/.ts

Example output:

456 packages/refactor-assistant/src/mcp-server.ts 395 packages/security-scanner/src/scanner.ts 411 packages/db-schema/src/designer.ts

Action: Extract to modular structure

Trigger 2: Magic Numbers (5+)

// ❌ Magic numbers scattered throughout if (complexity > 70) { /* ... / } if (lines > 1000) { / ... / } if (imports > 50) { / ... */ } const threshold = 0.8; const maxRetries = 3;

Action: Extract to constants/

Trigger 3: Complex Functions (30+ LOC)

// ❌ Complex calculation embedded function analyzeCode(file) { // 45 lines of complex logic // Multiple nested loops // Complex conditionals // Mathematical calculations }

Action: Extract to helpers/

Trigger 4: Duplicate Code Blocks

// ❌ Same pattern repeated 3+ times function processFile1() { validateInput(); parseContent(); transformData(); }

function processFile2() { validateInput(); // Duplicate! parseContent(); // Duplicate! transformData(); // Duplicate! }

Action: Extract to utils.ts

Trigger 5: Complexity Score >70

// ❌ High cyclomatic complexity function handleRequest(request) { if (condition1) { if (condition2) { if (condition3) { // Many nested branches // Multiple return paths // Complex error handling } } } // Complexity: 89 }

Action: Simplify or extract sub-functions

Extraction Decision Tree

Is it a configuration value/threshold/pattern? YES → Extract to constants/

Is it a complex calculation (30+ LOC)? YES → Extract to helpers/

Is it used by multiple modules? YES → Extract to utils.ts

Is it cross-cutting concern (error handling, validation)? YES → Extract to utils.ts OR @j0kz/shared

Is it still >30 LOC after extraction? YES → Break into smaller functions

Standard Refactoring Pattern

Step 1: Create Modular Structure

Inside package directory

mkdir -p src/{constants,helpers}

Verify structure

tree src/ src/ ├── mcp-server.ts ├── main-logic.ts ├── types.ts ├── constants/ ├── helpers/ └── utils.ts

Step 2: Extract Constants

What to Extract:

  • Magic numbers

  • Thresholds and limits

  • Regex patterns

  • Configuration values

  • Error messages

  • Default options

Example: constants/analysis-thresholds.ts

/**

  • Complexity analysis thresholds */ export const COMPLEXITY_THRESHOLDS = { LOW: 20, MODERATE: 50, HIGH: 70, CRITICAL: 100, } as const;

/**

  • File size limits (lines of code) */ export const SIZE_LIMITS = { TARGET: 300, WARNING: 400, MAXIMUM: 500, } as const;

/**

  • Quality score weights */ export const QUALITY_WEIGHTS = { COMPLEXITY: 0.3, MAINTAINABILITY: 0.3, COVERAGE: 0.2, DUPLICATION: 0.2, } as const;

Before (in main file):

if (complexity > 70) { /* ... / } if (lines > 300) { / ... */ } const score = complexity * 0.3 + maintainability * 0.3;

After:

import { COMPLEXITY_THRESHOLDS, SIZE_LIMITS, QUALITY_WEIGHTS } from './constants/analysis-thresholds.js';

if (complexity > COMPLEXITY_THRESHOLDS.HIGH) { /* ... / } if (lines > SIZE_LIMITS.TARGET) { / ... */ } const score = complexity * QUALITY_WEIGHTS.COMPLEXITY + maintainability * QUALITY_WEIGHTS.MAINTAINABILITY;

Benefits:

  • Single source of truth

  • Easy to tune thresholds

  • Self-documenting

  • Prevents inconsistencies

Step 3: Extract Helpers

What to Extract:

  • Complex calculations (30+ LOC)

  • Reusable business logic

  • Data transformations

  • Algorithm implementations

Example: helpers/complexity-calculator.ts

import { COMPLEXITY_THRESHOLDS } from '../constants/analysis-thresholds.js';

/**

  • Calculate cyclomatic complexity for a function
  • @param ast - Abstract syntax tree node
  • @returns Complexity score */ export function calculateComplexity(ast: any): number { let complexity = 1; // Base complexity

// Count decision points const decisionNodes = [ 'IfStatement', 'SwitchCase', 'ConditionalExpression', 'LogicalExpression', 'ForStatement', 'WhileStatement', 'CatchClause', ];

function traverse(node: any) { if (!node) return;

if (decisionNodes.includes(node.type)) {
  complexity++;
}

// Recursive traversal
for (const key in node) {
  if (node[key] &#x26;&#x26; typeof node[key] === 'object') {
    traverse(node[key]);
  }
}

}

traverse(ast); return complexity; }

/**

  • Categorize complexity level
  • @param complexity - Complexity score
  • @returns Level category */ export function categorizeComplexity(complexity: number): string { if (complexity <= COMPLEXITY_THRESHOLDS.LOW) return 'low'; if (complexity <= COMPLEXITY_THRESHOLDS.MODERATE) return 'moderate'; if (complexity <= COMPLEXITY_THRESHOLDS.HIGH) return 'high'; return 'critical'; }

Before (in main file - 45 LOC):

function analyzeFunction(ast) { // 45 lines of complexity calculation // Nested traversal logic // Multiple conditionals // Returns complexity score }

After (in main file - 3 LOC):

import { calculateComplexity, categorizeComplexity } from './helpers/complexity-calculator.js';

function analyzeFunction(ast) { const complexity = calculateComplexity(ast); return categorizeComplexity(complexity); }

Benefits:

  • Independently testable

  • Reusable across analyzers

  • Clear single responsibility

  • Easier to optimize

Step 4: Extract Utilities

What to Extract:

  • Cross-cutting concerns

  • Used by multiple modules

  • Error handling patterns

  • Validation logic

  • Shared transformations

Example: utils.ts

/**

  • Validate file path for security
  • @param filePath - Path to validate
  • @throws Error if path is invalid or unsafe */ export function validateFilePath(filePath: string): void { if (!filePath) { throw new Error('File path is required'); }

// Prevent path traversal if (filePath.includes('..')) { throw new Error('Path traversal detected'); }

// Ensure supported extension const ext = filePath.split('.').pop(); const supported = ['ts', 'js', 'tsx', 'jsx']; if (!ext || !supported.includes(ext)) { throw new Error(Unsupported file type: ${ext}); } }

/**

  • Format error message with context
  • @param operation - Operation that failed
  • @param error - Original error
  • @returns Formatted error message */ export function formatError(operation: string, error: Error): string { return Failed to ${operation}: ${error.message}; }

/**

  • Truncate long strings for logging
  • @param str - String to truncate
  • @param maxLength - Maximum length
  • @returns Truncated string */ export function truncate(str: string, maxLength: number = 100): string { if (str.length <= maxLength) return str; return str.substring(0, maxLength - 3) + '...'; }

Usage:

import { validateFilePath, formatError } from './utils.js';

try { validateFilePath(args.filePath); // Process file... } catch (error) { return { success: false, errors: [formatError('process file', error)] }; }

Real-World Example: security-scanner

For a comprehensive 371-line example showing the complete refactoring of the security-scanner package from 395 LOC down to modular components:

cat .claude/skills/modular-refactoring-pattern/references/security-scanner-example.md

This example demonstrates:

  • Before: Monolithic 395 LOC file with complexity 57

  • After: Modular structure with no file >120 LOC

  • Extraction of constants, helpers, and utilities

  • Proven results with metrics improvements

Proven Results (2025 Refactoring)

4 Packages Successfully Refactored:

Package Before LOC After LOC Reduction Complexity

security-scanner 395 209 (main) -47% 57 → 28

db-schema 411 262 (main) -36% 63 → 35

architecture-analyzer 382 287 (main) -25% 48 → 31

refactor-assistant 456 407 (main) -11% 72 → 44

Average Improvements:

  • -36% file size reduction

  • -42% complexity reduction

  • +122% maintainability improvement

  • -52% duplicate code

Refactoring Workflow

Complete Workflow

1. Analyze current state

npx @j0kz/smart-reviewer@latest review src/*.ts --metrics

2. Identify refactoring targets

wc -l src/*.ts | sort -rn

3. Apply refactoring pattern

npx @j0kz/refactor-assistant@latest refactor src/scanner.ts
--extract-constants
--extract-helpers
--target-size=300

4. Verify improvements

npx @j0kz/smart-reviewer@latest review src/*.ts --metrics

5. Run tests

npm test

6. Commit changes

git add -A git commit -m "refactor: apply modular pattern to scanner

  • Extract constants to constants/
  • Extract helpers to helpers/
  • Reduce main file from 395 to 209 LOC
  • Improve complexity from 57 to 28"

Common Patterns to Extract

  1. Configuration Objects

// Extract to constants/config.ts export const DEFAULT_CONFIG = { maxFileSize: 100000, excludePatterns: ['node_modules', 'dist'], supportedExtensions: ['.ts', '.js', '.tsx', '.jsx'], // ... };

  1. Validation Functions

// Extract to helpers/validators.ts export function isValidPath(path: string): boolean { /* ... / } export function isSupported(file: string): boolean { / ... / } export function validateConfig(config: Config): void { / ... */ }

  1. Business Logic

// Extract to helpers/[domain].ts export function calculateScore(metrics: Metrics): number { /* ... / } export function generateReport(results: Results): Report { / ... / } export function analyzePatterns(code: string): Pattern[] { / ... */ }

  1. Error Handling

// Extract to utils.ts export function wrapError(error: Error, context: string): Error { /* ... / } export function isRecoverableError(error: Error): boolean { / ... / } export function formatErrorResponse(error: Error): Response { / ... */ }

Best Practices

  • Extract early: Don't wait until 400+ LOC

  • Keep related code together: Group by domain

  • Use barrel exports: Re-export from index.ts

  • Document extractions: Add JSDoc comments

  • Test extracted modules: Unit test helpers independently

  • Avoid circular dependencies: Use dependency injection

  • Follow naming conventions: constants/, helpers/, utils.ts

  • Consider shared package: Move to @j0kz/shared if reusable

  • Maintain backwards compatibility: Keep public APIs stable

  • Review after refactoring: Use smart-reviewer to verify

Anti-Patterns to Avoid

❌ Over-extraction: Don't create 10 LOC files ❌ Premature extraction: Wait for actual need ❌ Deep nesting: Avoid src/helpers/utils/common/shared/ ❌ Generic names: Use domain-specific names ❌ Mixed concerns: Keep single responsibility ❌ Circular imports: Plan module dependencies ❌ Lost context: Keep related logic nearby

Verification Checklist

  • All files <300 LOC (max 500)

  • Complexity <50 per file (max 70)

  • No duplicate code blocks

  • Constants extracted and documented

  • Complex functions extracted to helpers

  • Shared utilities in utils.ts

  • Tests still passing

  • Import paths correct (.js extension)

  • No circular dependencies

  • Improved metrics from smart-reviewer

Success Metric: If smart-reviewer scores improve and tests pass, refactoring succeeded!

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

api-integration

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

dependency-doctor

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

brand-guidelines

No summary provided by upstream source.

Repository SourceNeeds Review