Agent skill
modular-refactoring-pattern
Guides systematic file refactoring following @j0kz/mcp-agents modular architecture pattern to maintain files under 300 LOC through constants/, helpers/, utils/ extraction with proven results from 2...
Install this agent skill to your Project
npx add-skill https://github.com/j0KZ/mcp-agents/tree/main/starter-kit/template/.claude/skills/modular-refactoring-pattern
SKILL.md
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] && 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'],
// ...
};
2. 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 { /* ... */ }
3. 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[] { /* ... */ }
4. 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!
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
debug-detective
Systematic debugging approach for ANY codebase, ANY language, ANY bug type
tool-discovery
Guide for discovering and using @j0kz MCP tools efficiently through search, filtering, and progressive exploration. Use when exploring available capabilities, finding the right tool for a task, or ...
project-standardization
Guides correct usage of @j0kz/mcp-agents standardization and automation scripts including version.json single source of truth, test count automation, URL casing rules, and critical workflow pattern...
api-integration
Master third-party API integration in ANY language with best practices and patterns
documentation-generation
Documentation generation and maintenance patterns including README structure, CHANGELOG management, API documentation, wiki synchronization, and badge updates. Use when creating package docs, updat...
competitive-ads-extractor
Extracts and analyzes competitor ads from ad libraries (Facebook, LinkedIn, TikTok, Google). Use when researching competitor messaging, creative patterns, campaign strategies, or ad inspiration. Ch...
Didn't find tool you were looking for?