Python Code Refactoring Analyser
When the user wants to refactor code, improve code quality, or check for code smells:
1. Introduction
This skill provides comprehensive refactoring guidance for Python code, focusing on:
- Functional programming patterns - Detect non-functional code (mutations, side effects, classes vs functions)
- Code quality checks - Automated analysis using pylint, black, and mypy
- Manual refactoring guidance - Identify code smells and suggest improvements
- Configuration extraction - Find hardcoded values that should be extracted to constants or config files
The skill combines automated tool-based analysis with manual pattern detection to provide actionable refactoring suggestions.
2. When to Use
Activate this skill when the user:
- Requests code review or refactoring
- Wants to improve code quality before committing
- Needs to convert object-oriented code to functional style
- Asks to check for code smells or antipatterns
- Wants periodic code quality checks
- Is preparing code for testing (making it more testable)
3. Quick Start
Execute the Python script located in the same directory as this skill:
Auto-detect mode (analyses all Python files in current directory):
python3 skills/refactor/refactor.py
Specific file mode:
python3 skills/refactor/refactor.py path/to/file.py
Full project scan (analyses all .py files recursively):
python3 skills/refactor/refactor.py --all
The script works immediately with no dependencies (uses Python stdlib). Optional tools (pylint, black, mypy) provide enhanced analysis if installed.
4. What It Checks
Functional Programming Patterns (AST-based)
- Mutations: Detects
list.append(),dict[key] = value,items[0] = x - Class usage: Flags classes when functions + dataclasses would suffice
- Global state: Identifies global variable modifications
- Side effects: Finds
print(), file I/O, network calls in business logic functions - Impure functions: Detects functions that modify state or have side effects
Code Quality (via pylint)
- PEP 8 style compliance
- Naming conventions
- Code complexity metrics
- Unused imports and variables
- Missing docstrings
- Potential bugs
Code Formatting (via black)
- Line length consistency
- Indentation style
- Quote style
- Whitespace formatting
Type Safety (via mypy)
- Type hint coverage
- Type correctness
- Return type consistency
- Argument type validation
Manual Code Smell Detection
- Functions longer than 50 lines
- Nesting depth greater than 3 levels
- Magic numbers and strings (hardcoded values)
- Missing or inadequate docstrings
- Repeated code patterns (DRY violations)
Configuration Extraction
- Hardcoded file paths, URLs, API keys
- Environment-specific values
- Magic constants that should be named
- Format strings and patterns
5. Output Interpretation
The script produces a structured report with issues grouped by severity and category:
Severity Levels
- CRITICAL: Must fix - violates core principles or will cause bugs
- WARNING: Should fix - reduces code quality or maintainability
- INFO: Consider fixing - minor improvements or suggestions
Example Output
Refactoring Analysis: skills/anonymise/anonymise.py
================================================================
SUMMARY: 5 issues found
Critical: 0
Warning: 3
Info: 2
FUNCTIONAL PATTERNS (2 warnings)
Line 52: Side effect detected in function 'anonymise_csv'
Suggestion: Extract file I/O to separate function, keep core logic pure
Line 70: Mutation detected - modifying list in place
Suggestion: Use list comprehension or functional transformation
CODE QUALITY (1 warning)
[pylint] Line 54: Consider using 'with' statement for resource cleanup
Score: 9.2/10
INFO (2)
[config] Line 50: Hardcoded datetime format string
Suggestion: Extract to constant TIMESTAMP_FORMAT = "%Y%m%d_%H%M%S"
[code smell] Function 'main' is 45 lines - approaching complexity threshold
Suggestion: Consider extracting argument parsing to separate function
Checks run: AST analysis, pylint, black, mypy
Tools available: pylint ✓, black ✓, mypy ✓
6. Tool Integration
First-Run Setup
The script automatically checks for optional analysis tools. If tools are not installed:
Optional tools not found:
- pylint: pip install pylint
- black: pip install black
- mypy: pip install mypy
Install all: pip install pylint black mypy
Continuing with stdlib-only analysis...
Note: Core functional pattern analysis and code smell detection work without any external dependencies.
Configuration Files
On first run, the script can generate configuration files if they don't exist:
pyproject.toml - Configures black, mypy, and pylint:
- Line length: 88 characters (black default)
- Python version: 3.8+
- Type checking: Strict mode
- Functional-programming-friendly linter rules
You can customise these configs for project-specific requirements.
Tool Execution
- Graceful degradation: Skips unavailable tools without errors
- Unified output: Parses all tool outputs into consistent Issue format
- Performance: Caches results during analysis for speed
7. Manual Refactoring Guidance
Extracting Configuration
Before (hardcoded values):
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = f"{timestamp}.csv"
After (extracted constants):
TIMESTAMP_FORMAT = "%Y%m%d_%H%M%S"
CSV_EXTENSION = ".csv"
timestamp = datetime.now().strftime(TIMESTAMP_FORMAT)
output_file = f"{timestamp}{CSV_EXTENSION}"
Converting to Functional Style
Before (mutation-based):
def process_items(items):
results = []
for item in items:
if item > 0:
results.append(item * 2)
return results
After (functional):
def process_items(items: list[int]) -> list[int]:
return [item * 2 for item in items if item > 0]
Extracting Pure Functions
Before (side effects mixed with logic):
def anonymise_csv(input_file: str) -> str:
with open(input_file, 'r') as f:
rows = list(csv.reader(f))
anonymised = [row[1:] for row in rows]
with open(output_file, 'w') as f:
csv.writer(f).writerows(anonymised)
return output_file
After (pure core, impure shell):
def read_csv(file_path: str) -> list[list[str]]:
"""Read CSV file (impure I/O)."""
with open(file_path, 'r') as f:
return list(csv.reader(f))
def anonymise_rows(rows: list[list[str]]) -> list[list[str]]:
"""Remove first column from all rows (pure function)."""
return [row[1:] for row in rows]
def write_csv(file_path: str, rows: list[list[str]]) -> None:
"""Write CSV file (impure I/O)."""
with open(file_path, 'w') as f:
csv.writer(f).writerows(rows)
def anonymise_csv(input_file: str, output_file: str) -> str:
"""Orchestrate anonymisation (impure shell)."""
rows = read_csv(input_file)
anonymised = anonymise_rows(rows)
write_csv(output_file, anonymised)
return output_file
Function Decomposition
When to break down functions:
- Function exceeds 50 lines
- Cyclomatic complexity > 10
- Multiple levels of nesting (> 3)
- Mixing different levels of abstraction
- Repeated code blocks
How to decompose:
- Identify distinct responsibilities
- Extract each responsibility to a named function
- Use descriptive function names
- Pass dependencies explicitly (no hidden state)
- Prefer pure functions where possible
8. Notes
- British English: All output uses British spelling (analyse, optimise, behaviour, etc.)
- Non-destructive: Analysis only - no automatic code modifications
- Platform-agnostic: Works with any AI coding assistant (Claude Code, Cursor, Aider, Codex CLI, etc.)
- Minimal dependencies: Core analysis uses stdlib only; external tools are optional enhancements
- Testability focus: Suggestions prioritise making code easier to test with pytest
- Functional-first: Recommendations align with project preference for functional programming idioms
- Standards compliance: Checks against AGENTS.md conventions (PEP 8, type hints, British English, functional style)