Initial commit
This commit is contained in:
37
skills/ln-341-code-quality-checker/SKILL.md
Normal file
37
skills/ln-341-code-quality-checker/SKILL.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: ln-341-code-quality-checker
|
||||
description: Worker that checks DRY/KISS/YAGNI/architecture/guide compliance for Done implementation tasks. Reports issues; does not change statuses or create tasks.
|
||||
---
|
||||
|
||||
# Code Quality Checker
|
||||
|
||||
Analyzes Done implementation tasks for code-quality issues and reports findings for the Story quality gate.
|
||||
|
||||
## Purpose & Scope
|
||||
- Load Story and Done implementation tasks (exclude test tasks).
|
||||
- Check for DRY/KISS/YAGNI violations, architecture boundary breaks, guide non-compliance, and obvious config/hardcode problems.
|
||||
- Produce a verdict and structured issue list; never edits Linear or kanban.
|
||||
|
||||
## Workflow (concise)
|
||||
1) Load Story (full) and Done implementation tasks (full descriptions) via Linear; skip tasks with label "tests".
|
||||
2) Collect affected files from tasks (Affected Components/Existing Code Impact) and recent commits/diffs if noted.
|
||||
3) Analyze code and docs: reuse existing components, avoid duplication, simplify over-engineering, keep layers clean, no hardcoded creds/URLs/magic numbers, follow referenced guides.
|
||||
4) Output verdict: PASS or ISSUES_FOUND with details (category, severity, file, recommendation). Add Linear comment with findings.
|
||||
|
||||
## Critical Rules
|
||||
- Read guides mentioned in Story/Tasks before judging compliance.
|
||||
- Language preservation in comments (EN/RU).
|
||||
- Do not create tasks or change statuses; caller decides next actions.
|
||||
|
||||
## Definition of Done
|
||||
- Story and Done implementation tasks loaded (test tasks excluded).
|
||||
- Guides reviewed; affected files inspected.
|
||||
- Verdict produced with structured issues (if any) and Linear comment posted.
|
||||
|
||||
## Reference Files
|
||||
- Guides: `docs/guides/`
|
||||
- Templates for context: `../ln-311-task-creator/references/task_template_implementation.md`
|
||||
|
||||
---
|
||||
Version: 4.0.0 (Condensed worker flow)
|
||||
Last Updated: 2025-11-26
|
||||
209
skills/ln-341-code-quality-checker/diagram.html
Normal file
209
skills/ln-341-code-quality-checker/diagram.html
Normal file
@@ -0,0 +1,209 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ln-341-code-quality-checker Workflow</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
|
||||
<link rel="stylesheet" href="../shared/css/diagram.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>🔎 ln-341-code-quality-checker Workflow</h1>
|
||||
<p class="subtitle">Linear Workflow - Worker v1.0.0</p>
|
||||
</header>
|
||||
<div class="info-box">
|
||||
<h3>Overview</h3>
|
||||
<p><strong>Purpose:</strong> Analyze code quality of Done implementation tasks for DRY/KISS/YAGNI/Architecture violations and guide compliance.</p>
|
||||
<p><strong>Type:</strong> Linear Workflow (5 sequential phases)</p>
|
||||
<p><strong>Single Responsibility:</strong> ONLY analyzes code quality and reports issues - does NOT create tasks or change statuses.</p>
|
||||
<p><strong>Fail Fast:</strong> Phase 4 Step 1 in ln-330-story-executor - checks quality BEFORE testing to catch refactoring needs early.</p>
|
||||
</div>
|
||||
|
||||
<div class="mermaid">
|
||||
graph TD
|
||||
Start([START]) --> Phase1[Phase 1: Discovery<br/>Load Story<br/>Discover guides<br/>Extract guide links]
|
||||
|
||||
Phase1 --> Phase2[Phase 2: Load Done Tasks<br/>Query implementation tasks<br/>Filter out test tasks<br/>Identify affected files]
|
||||
|
||||
Phase2 --> Phase3[Phase 3: Analyze Changes<br/>Extract git diffs<br/>Read file contents<br/>Parse AST]
|
||||
|
||||
Phase3 --> Phase4[Phase 4: Check Violations<br/>Check 1: DRY duplicates<br/>Check 2: KISS complexity<br/>Check 3: YAGNI unused<br/>Check 4: Architecture layers<br/>Check 5: Guide compliance]
|
||||
|
||||
Phase4 --> Phase5[Phase 5: Report Results<br/>Categorize by severity<br/>Linear comment<br/>Return JSON verdict]
|
||||
|
||||
Phase5 --> End([END:<br/>JSON verdict + issues])
|
||||
|
||||
classDef phase fill:#E3F2FD,stroke:#1976D2,stroke-width:2px
|
||||
classDef endpoint fill:#C8E6C9,stroke:#388E3C,stroke-width:2px
|
||||
|
||||
class Phase1,Phase2,Phase3,Phase4,Phase5 phase
|
||||
class Start,End endpoint
|
||||
</div>
|
||||
|
||||
<h2>Phase Descriptions</h2>
|
||||
|
||||
<div class="phase-description">
|
||||
<div class="phase-title">Phase 1: Discovery</div>
|
||||
<ul>
|
||||
<li>Load Story from Linear via MCP</li>
|
||||
<li>Extract Story.id (UUID) for parentId filter</li>
|
||||
<li>Discover available guides in docs/guides/</li>
|
||||
<li>Parse Story Technical Notes for guide links</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="phase-description">
|
||||
<div class="phase-title">Phase 2: Load Done Implementation Tasks</div>
|
||||
<ul>
|
||||
<li>Query Linear for Story's child tasks with state="Done"</li>
|
||||
<li>Filter OUT test tasks (label "tests" or title contains "test")</li>
|
||||
<li>Extract affected files from task "Affected Components" section</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="phase-description">
|
||||
<div class="phase-title">Phase 3: Analyze Code Changes</div>
|
||||
<ul>
|
||||
<li>Extract git diffs for each task (git diff main...[branch])</li>
|
||||
<li>Read file contents via Read tool</li>
|
||||
<li>Parse AST (Abstract Syntax Tree) for code structure</li>
|
||||
<li>Identify functions, classes, imports, complexity metrics</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="phase-description">
|
||||
<div class="phase-title">Phase 4: Check Violations (5 checks)</div>
|
||||
<ul>
|
||||
<li><strong>Check 1: DRY</strong> - Duplicate code blocks, functions, validation logic</li>
|
||||
<li><strong>Check 2: KISS</strong> - High cyclomatic complexity (>10), deep nesting, long functions</li>
|
||||
<li><strong>Check 3: YAGNI</strong> - Unused code, premature abstraction, over-engineering</li>
|
||||
<li><strong>Check 4: Architecture</strong> - Layer violations, circular dependencies, domain logic in wrong layer</li>
|
||||
<li><strong>Check 5: Guide Compliance</strong> - Code doesn't follow recommended patterns from guides</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="phase-description">
|
||||
<div class="phase-title">Phase 5: Report Results</div>
|
||||
<ul>
|
||||
<li>Categorize issues by severity (HIGH/MEDIUM/LOW)</li>
|
||||
<li>Determine verdict (PASS if no HIGH/MEDIUM issues)</li>
|
||||
<li>Format Linear comment with categorized issues</li>
|
||||
<li>Add comment to Story</li>
|
||||
<li>Return JSON verdict with detailed issue list</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2>Violation Types</h2>
|
||||
|
||||
<div class="violation-type">
|
||||
<div class="violation-title">1. DRY (Don't Repeat Yourself)</div>
|
||||
<p><strong>Detects:</strong> Duplicate functions, code blocks, validation logic, error handling patterns</p>
|
||||
<p><strong>Example:</strong> Same validation function in 3 different files</p>
|
||||
<p><strong>Fix:</strong> Extract to shared utility module</p>
|
||||
<p><strong>Severity:</strong> HIGH if >2 duplicates, MEDIUM if 2 duplicates</p>
|
||||
</div>
|
||||
|
||||
<div class="violation-type">
|
||||
<div class="violation-title">2. KISS (Keep It Simple)</div>
|
||||
<p><strong>Detects:</strong> High cyclomatic complexity (>10), deep nesting (>4 levels), long functions (>50 lines)</p>
|
||||
<p><strong>Example:</strong> Function with 5 nested if statements</p>
|
||||
<p><strong>Fix:</strong> Simplify with early returns, extract helper functions</p>
|
||||
<p><strong>Severity:</strong> HIGH if complexity >15, MEDIUM if >10</p>
|
||||
</div>
|
||||
|
||||
<div class="violation-type">
|
||||
<div class="violation-title">3. YAGNI (You Aren't Gonna Need It)</div>
|
||||
<p><strong>Detects:</strong> Unused functions, premature abstraction, over-engineering</p>
|
||||
<p><strong>Example:</strong> Interface with only one implementation</p>
|
||||
<p><strong>Fix:</strong> Remove unused code, simplify abstraction</p>
|
||||
<p><strong>Severity:</strong> MEDIUM (doesn't break functionality, adds maintenance burden)</p>
|
||||
</div>
|
||||
|
||||
<div class="violation-type">
|
||||
<div class="violation-title">4. Architecture Violations</div>
|
||||
<p><strong>Detects:</strong> Layer violations (controller → repository, skipping service), circular dependencies</p>
|
||||
<p><strong>Example:</strong> Controller imports Repository directly instead of Service</p>
|
||||
<p><strong>Fix:</strong> Follow layer hierarchy: Controller → Service → Repository</p>
|
||||
<p><strong>Severity:</strong> HIGH (breaks architecture, creates coupling)</p>
|
||||
</div>
|
||||
|
||||
<div class="violation-type">
|
||||
<div class="violation-title">5. Guide Compliance Violations</div>
|
||||
<p><strong>Detects:</strong> Code doesn't follow recommended patterns from project guides</p>
|
||||
<p><strong>Example:</strong> Custom JWT implementation instead of authlib (Guide 01 recommendation)</p>
|
||||
<p><strong>Fix:</strong> Use recommended library/pattern from guide</p>
|
||||
<p><strong>Severity:</strong> HIGH if security/critical, MEDIUM otherwise</p>
|
||||
</div>
|
||||
|
||||
<h2>Output Format</h2>
|
||||
<pre style="background: #F5F5F5; padding: 15px; border-radius: 4px; overflow-x: auto;">
|
||||
{
|
||||
"verdict": "PASS" | "ISSUES_FOUND",
|
||||
"story_id": "US001",
|
||||
"tasks_analyzed": 3,
|
||||
"issues": [
|
||||
{
|
||||
"type": "DRY",
|
||||
"severity": "HIGH",
|
||||
"file": "src/auth/service.py",
|
||||
"line": 42,
|
||||
"description": "Duplicate validation logic in 3 places",
|
||||
"suggestion": "Extract to shared validator function"
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"dry_violations": 2,
|
||||
"kiss_violations": 1,
|
||||
"yagni_violations": 0,
|
||||
"architecture_violations": 1,
|
||||
"guide_violations": 1,
|
||||
"total_issues": 5,
|
||||
"high_severity": 3,
|
||||
"medium_severity": 2,
|
||||
"low_severity": 0
|
||||
},
|
||||
"linear_comment_id": "abc123"
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h2>Key Characteristics</h2>
|
||||
<ul>
|
||||
<li><strong>Atomic Worker:</strong> Single responsibility - code analysis only</li>
|
||||
<li><strong>Fail Fast:</strong> Runs FIRST in Phase 4 (before testing) to catch refactoring needs early</li>
|
||||
<li><strong>AST-Based:</strong> Uses Abstract Syntax Tree parsing for accurate code analysis</li>
|
||||
<li><strong>Multi-Dimensional:</strong> Checks 5 violation types (DRY/KISS/YAGNI/Architecture/Guide Compliance)</li>
|
||||
<li><strong>Severity Categorization:</strong> Issues ranked HIGH/MEDIUM/LOW for prioritization</li>
|
||||
<li><strong>Guide Integration:</strong> Validates code follows project-specific patterns</li>
|
||||
</ul>
|
||||
|
||||
<h2>Complexity Metrics</h2>
|
||||
<p><strong>Cyclomatic Complexity Thresholds:</strong></p>
|
||||
<ul>
|
||||
<li>1-5: Simple, low risk</li>
|
||||
<li>6-10: Moderate complexity</li>
|
||||
<li>11-15: Complex, needs refactoring (MEDIUM severity)</li>
|
||||
<li>16+: Very complex, high risk (HIGH severity)</li>
|
||||
</ul>
|
||||
|
||||
<script>
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
theme: 'default',
|
||||
flowchart: {
|
||||
useMaxWidth: true,
|
||||
htmlLabels: true,
|
||||
curve: 'basis'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<footer>
|
||||
<p>ln-341-code-quality-checker v1.0.0 | Worker Pattern | Mermaid.js</p>
|
||||
</footer>
|
||||
</div>
|
||||
<script>
|
||||
mermaid.initialize({ startOnLoad: true, theme: 'default', flowchart: { useMaxWidth: true, htmlLabels: true, curve: 'basis' } });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,143 @@
|
||||
# Architecture Violations Reference
|
||||
|
||||
Clean Architecture principles: Separation of concerns, layer independence, dependency inversion.
|
||||
|
||||
## Layer Hierarchy
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Controllers │ (HTTP handlers, routes)
|
||||
│ (routes/) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
v
|
||||
┌─────────────────┐
|
||||
│ Services │ (Business logic)
|
||||
│ (services/) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
v
|
||||
┌─────────────────┐
|
||||
│ Repositories │ (Data access)
|
||||
│ (repositories/) │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
**Rule:** Only call downward. Controller → Service → Repository.
|
||||
|
||||
## Violation Patterns
|
||||
|
||||
### 1. Layer Skipping
|
||||
|
||||
**Violation:**
|
||||
```python
|
||||
# controllers/user_controller.py
|
||||
from repositories.user_repository import UserRepository # VIOLATION!
|
||||
|
||||
def get_user(user_id):
|
||||
user_repo = UserRepository() # Skips Service layer
|
||||
return user_repo.get(user_id)
|
||||
```
|
||||
|
||||
**Fix:**
|
||||
```python
|
||||
# controllers/user_controller.py
|
||||
from services.user_service import UserService
|
||||
|
||||
def get_user(user_id):
|
||||
user_service = UserService()
|
||||
return user_service.get_user(user_id)
|
||||
|
||||
# services/user_service.py
|
||||
from repositories.user_repository import UserRepository
|
||||
|
||||
class UserService:
|
||||
def __init__(self):
|
||||
self.user_repo = UserRepository()
|
||||
|
||||
def get_user(self, user_id):
|
||||
return self.user_repo.get(user_id)
|
||||
```
|
||||
|
||||
**Severity:** HIGH
|
||||
|
||||
---
|
||||
|
||||
### 2. Circular Dependencies
|
||||
|
||||
**Violation:**
|
||||
```python
|
||||
# module_a.py
|
||||
from module_b import function_b
|
||||
|
||||
def function_a():
|
||||
return function_b()
|
||||
|
||||
# module_b.py
|
||||
from module_a import function_a # CIRCULAR!
|
||||
|
||||
def function_b():
|
||||
return function_a()
|
||||
```
|
||||
|
||||
**Detection:** Build dependency graph, check for cycles
|
||||
|
||||
**Fix:** Extract shared code to third module or use dependency injection
|
||||
|
||||
**Severity:** HIGH
|
||||
|
||||
---
|
||||
|
||||
### 3. Business Logic in Wrong Layer
|
||||
|
||||
**Violation:**
|
||||
```python
|
||||
# controllers/user_controller.py
|
||||
def create_user(data):
|
||||
# Business logic in controller! VIOLATION!
|
||||
if not data.get('email') or '@' not in data['email']:
|
||||
return {"error": "Invalid email"}
|
||||
|
||||
if len(data.get('password', '')) < 8:
|
||||
return {"error": "Password too short"}
|
||||
|
||||
user = User(**data)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
return {"success": True}
|
||||
```
|
||||
|
||||
**Fix:** Move business logic to Service layer
|
||||
|
||||
---
|
||||
|
||||
## Detection
|
||||
|
||||
```python
|
||||
def detect_layer_violations(files):
|
||||
violations = []
|
||||
|
||||
for file in files:
|
||||
# Parse imports
|
||||
imports = extract_imports(file)
|
||||
|
||||
# Check layer
|
||||
if 'controllers' in file.path or 'routes' in file.path:
|
||||
# Controller layer
|
||||
for imp in imports:
|
||||
if 'repositories' in imp:
|
||||
violations.append({
|
||||
'type': 'Architecture',
|
||||
'severity': 'HIGH',
|
||||
'file': file.path,
|
||||
'description': 'Controller imports Repository (skips Service)',
|
||||
'suggestion': 'Import from services/ instead'
|
||||
})
|
||||
|
||||
return violations
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Last Updated:** 2025-11-13
|
||||
231
skills/ln-341-code-quality-checker/references/dry_violations.md
Normal file
231
skills/ln-341-code-quality-checker/references/dry_violations.md
Normal file
@@ -0,0 +1,231 @@
|
||||
# DRY Violations Reference
|
||||
|
||||
DRY (Don't Repeat Yourself) principle: Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
|
||||
|
||||
## Common Violation Patterns
|
||||
|
||||
### Pattern 1: Duplicate Function Definitions
|
||||
|
||||
**Violation:**
|
||||
```python
|
||||
# auth/service.py
|
||||
def validate_email(email):
|
||||
if not email or '@' not in email:
|
||||
raise ValueError("Invalid email")
|
||||
|
||||
# users/service.py
|
||||
def validate_email(email): # DUPLICATE!
|
||||
if not email or '@' not in email:
|
||||
raise ValueError("Invalid email")
|
||||
```
|
||||
|
||||
**Fix:**
|
||||
```python
|
||||
# utils/validators.py
|
||||
def validate_email(email):
|
||||
if not email or '@' not in email:
|
||||
raise ValueError("Invalid email")
|
||||
|
||||
# auth/service.py + users/service.py
|
||||
from utils.validators import validate_email
|
||||
```
|
||||
|
||||
**Severity:** HIGH if >2 duplicates, MEDIUM if 2 duplicates
|
||||
|
||||
---
|
||||
|
||||
### Pattern 2: Duplicate Validation Logic
|
||||
|
||||
**Violation:**
|
||||
```python
|
||||
# File 1
|
||||
if not user.email or '@' not in user.email:
|
||||
return {"error": "Invalid email"}
|
||||
|
||||
# File 2
|
||||
if not email or email.find('@') == -1: # Same logic, different syntax!
|
||||
raise ValueError("Invalid email")
|
||||
|
||||
# File 3
|
||||
email_valid = email and '@' in email # Same logic again!
|
||||
if not email_valid:
|
||||
return False
|
||||
```
|
||||
|
||||
**Fix:**
|
||||
```python
|
||||
# utils/validators.py
|
||||
def is_valid_email(email):
|
||||
return email and '@' in email
|
||||
|
||||
# All files use:
|
||||
if not is_valid_email(user.email):
|
||||
return {"error": "Invalid email"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Pattern 3: Duplicate Error Handling
|
||||
|
||||
**Violation:**
|
||||
```python
|
||||
# File 1
|
||||
try:
|
||||
result = api_call()
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"API call failed: {e}")
|
||||
return {"error": "Service unavailable"}
|
||||
|
||||
# File 2
|
||||
try:
|
||||
data = external_api_call()
|
||||
except requests.exceptions.RequestException as err:
|
||||
logger.error(f"API call failed: {err}") # DUPLICATE!
|
||||
return {"error": "Service unavailable"}
|
||||
```
|
||||
|
||||
**Fix:**
|
||||
```python
|
||||
# utils/decorators.py
|
||||
def handle_api_errors(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"API call failed: {e}")
|
||||
return {"error": "Service unavailable"}
|
||||
return wrapper
|
||||
|
||||
# Usage
|
||||
@handle_api_errors
|
||||
def api_call():
|
||||
return requests.get("https://api.example.com")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Pattern 4: Duplicate Database Queries
|
||||
|
||||
**Violation:**
|
||||
```python
|
||||
# users/repository.py
|
||||
def get_active_users():
|
||||
return db.query(User).filter(User.active == True, User.deleted_at == None).all()
|
||||
|
||||
# admin/repository.py
|
||||
def get_active_users(): # DUPLICATE!
|
||||
return db.query(User).filter(User.active == True, User.deleted_at == None).all()
|
||||
```
|
||||
|
||||
**Fix:**
|
||||
```python
|
||||
# users/repository.py (single source of truth)
|
||||
class UserRepository:
|
||||
@staticmethod
|
||||
def get_active_users():
|
||||
return db.query(User).filter(User.active == True, User.deleted_at == None).all()
|
||||
|
||||
# admin/repository.py imports from users/
|
||||
from users.repository import UserRepository
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Detection Algorithm
|
||||
|
||||
```python
|
||||
def detect_dry_violations(files):
|
||||
violations = []
|
||||
|
||||
# 1. Extract all functions from all files
|
||||
functions = {}
|
||||
for file in files:
|
||||
ast_tree = ast.parse(file.content)
|
||||
for node in ast.walk(ast_tree):
|
||||
if isinstance(node, ast.FunctionDef):
|
||||
functions[node.name] = functions.get(node.name, [])
|
||||
functions[node.name].append({
|
||||
'file': file.path,
|
||||
'line': node.lineno,
|
||||
'body': ast.unparse(node)
|
||||
})
|
||||
|
||||
# 2. Find duplicate function names
|
||||
for func_name, occurrences in functions.items():
|
||||
if len(occurrences) > 1:
|
||||
# 3. Check if function bodies are similar (>80% similarity)
|
||||
for i, func1 in enumerate(occurrences):
|
||||
for func2 in occurrences[i+1:]:
|
||||
similarity = calculate_similarity(func1['body'], func2['body'])
|
||||
if similarity > 0.8:
|
||||
violations.append({
|
||||
'type': 'DRY',
|
||||
'severity': 'HIGH' if len(occurrences) > 2 else 'MEDIUM',
|
||||
'file': func2['file'],
|
||||
'line': func2['line'],
|
||||
'description': f"Duplicate function '{func_name}' in {len(occurrences)} places",
|
||||
'suggestion': f"Extract to shared module"
|
||||
})
|
||||
|
||||
return violations
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Similarity Calculation
|
||||
|
||||
**Token-based Jaccard similarity:**
|
||||
|
||||
```python
|
||||
def calculate_similarity(code1, code2):
|
||||
# Tokenize code (remove whitespace, comments)
|
||||
tokens1 = set(tokenize(code1))
|
||||
tokens2 = set(tokenize(code2))
|
||||
|
||||
# Jaccard index
|
||||
intersection = tokens1 & tokens2
|
||||
union = tokens1 | tokens2
|
||||
|
||||
if not union:
|
||||
return 0.0
|
||||
|
||||
return len(intersection) / len(union)
|
||||
|
||||
def tokenize(code):
|
||||
# Remove whitespace and comments
|
||||
code = re.sub(r'#.*$', '', code, flags=re.MULTILINE)
|
||||
code = re.sub(r'\s+', ' ', code)
|
||||
|
||||
# Split into tokens
|
||||
tokens = re.findall(r'\w+|[^\w\s]', code)
|
||||
|
||||
return tokens
|
||||
```
|
||||
|
||||
**Threshold:** 80% similarity = duplicate
|
||||
|
||||
---
|
||||
|
||||
## Exceptions (When DRY Not Applicable)
|
||||
|
||||
1. **Different domains:** Same logic but different business context
|
||||
2. **Temporary code:** Prototypes or one-off scripts
|
||||
3. **Test fixtures:** Test data duplication acceptable for clarity
|
||||
4. **Configuration:** Similar config blocks OK (e.g., multiple API endpoints)
|
||||
|
||||
**Example (acceptable duplication):**
|
||||
```python
|
||||
# auth/config.py
|
||||
AUTH_API_URL = "https://api.example.com/auth"
|
||||
AUTH_API_TIMEOUT = 5
|
||||
|
||||
# users/config.py
|
||||
USERS_API_URL = "https://api.example.com/users" # Similar but different domain
|
||||
USERS_API_TIMEOUT = 5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Last Updated:** 2025-11-13
|
||||
@@ -0,0 +1,75 @@
|
||||
# KISS Violations Reference
|
||||
|
||||
KISS (Keep It Simple, Stupid) principle: Most systems work best if they are kept simple rather than made complicated.
|
||||
|
||||
## Violation Types
|
||||
|
||||
### 1. High Cyclomatic Complexity
|
||||
|
||||
**Threshold:** >10 = MEDIUM severity, >15 = HIGH severity
|
||||
|
||||
**Violation Example:**
|
||||
```python
|
||||
def process_user(user, action, options=None):
|
||||
if user:
|
||||
if user.active:
|
||||
if action == 'update':
|
||||
if options and 'email' in options:
|
||||
if validate_email(options['email']):
|
||||
if user.role == 'admin' or user.id == options.get('user_id'):
|
||||
# Complexity = 7 decision points!
|
||||
return update_email(user, options['email'])
|
||||
return None
|
||||
```
|
||||
|
||||
**Fix with Early Returns:**
|
||||
```python
|
||||
def process_user(user, action, options=None):
|
||||
if not user or not user.active:
|
||||
return None
|
||||
|
||||
if action != 'update' or not options or 'email' not in options:
|
||||
return None
|
||||
|
||||
if not validate_email(options['email']):
|
||||
return None
|
||||
|
||||
if user.role != 'admin' and user.id != options.get('user_id'):
|
||||
return None
|
||||
|
||||
return update_email(user, options['email'])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Deep Nesting
|
||||
|
||||
**Threshold:** >4 levels = violation
|
||||
|
||||
**Fix:** Extract nested logic into helper functions
|
||||
|
||||
---
|
||||
|
||||
### 3. Long Functions
|
||||
|
||||
**Threshold:** >50 lines = violation
|
||||
|
||||
**Fix:** Extract logical blocks into separate functions with descriptive names
|
||||
|
||||
---
|
||||
|
||||
## Detection
|
||||
|
||||
```python
|
||||
def calculate_complexity(func_node):
|
||||
complexity = 1 # Base complexity
|
||||
for node in ast.walk(func_node):
|
||||
if isinstance(node, (ast.If, ast.For, ast.While, ast.And, ast.Or, ast.ExceptHandler)):
|
||||
complexity += 1
|
||||
return complexity
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Last Updated:** 2025-11-13
|
||||
@@ -0,0 +1,72 @@
|
||||
# YAGNI Violations Reference
|
||||
|
||||
YAGNI (You Aren't Gonna Need It) principle: Don't add functionality until it's necessary.
|
||||
|
||||
## Violation Patterns
|
||||
|
||||
### 1. Unused Functions/Classes
|
||||
|
||||
**Violation:**
|
||||
```python
|
||||
def calculate_tax(amount): # Defined but never called!
|
||||
return amount * 0.2
|
||||
|
||||
def process_payment(amount):
|
||||
return charge_card(amount) # Doesn't use calculate_tax
|
||||
```
|
||||
|
||||
**Detection:** Search codebase for function calls; if function never called → YAGNI violation
|
||||
|
||||
---
|
||||
|
||||
### 2. Premature Abstraction
|
||||
|
||||
**Violation:**
|
||||
```python
|
||||
# Interface with only ONE implementation
|
||||
class UserRepositoryInterface:
|
||||
def get_user(self, id): pass
|
||||
def save_user(self, user): pass
|
||||
|
||||
class UserRepository(UserRepositoryInterface): # Only implementation!
|
||||
def get_user(self, id):
|
||||
return db.query(User).get(id)
|
||||
def save_user(self, user):
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
```
|
||||
|
||||
**Fix:** Remove interface until second implementation needed
|
||||
|
||||
---
|
||||
|
||||
### 3. Over-Engineering
|
||||
|
||||
**Violation:**
|
||||
```python
|
||||
# Complex pattern for simple problem
|
||||
class UserFactoryBuilder:
|
||||
def __init__(self):
|
||||
self.strategy = None
|
||||
|
||||
def set_strategy(self, strategy):
|
||||
self.strategy = strategy
|
||||
return self
|
||||
|
||||
def build(self):
|
||||
return User(strategy=self.strategy)
|
||||
|
||||
# Usage
|
||||
user = UserFactoryBuilder().set_strategy('default').build()
|
||||
```
|
||||
|
||||
**Fix:**
|
||||
```python
|
||||
# Simple solution
|
||||
user = User()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Last Updated:** 2025-11-13
|
||||
Reference in New Issue
Block a user