--- name: code-review-framework description: Automatically applies when reviewing code. Ensures structured review checklist covering correctness, security, performance, maintainability, testing, and documentation. category: python --- # Code Review Framework When reviewing code, follow this structured framework for comprehensive, consistent reviews. **Trigger Keywords**: code review, PR review, pull request, review checklist, code quality, review comments, review feedback **Agent Integration**: Used by `code-reviewer`, `backend-architect`, `security-engineer` ## ✅ Correct Pattern: Review Checklist ```python """ Code Review Checklist =================== Use this checklist for every code review. """ from typing import List, Dict from dataclasses import dataclass from enum import Enum class ReviewCategory(str, Enum): """Review categories.""" CORRECTNESS = "correctness" SECURITY = "security" PERFORMANCE = "performance" MAINTAINABILITY = "maintainability" TESTING = "testing" DOCUMENTATION = "documentation" class ReviewSeverity(str, Enum): """Issue severity levels.""" BLOCKING = "blocking" # Must fix before merge MAJOR = "major" # Should fix MINOR = "minor" # Nice to have NITPICK = "nitpick" # Style/preference @dataclass class ReviewComment: """Single review comment.""" category: ReviewCategory severity: ReviewSeverity file: str line: int message: str suggestion: str = "" class CodeReview: """Structured code review.""" def __init__(self): self.comments: List[ReviewComment] = [] def add_comment( self, category: ReviewCategory, severity: ReviewSeverity, file: str, line: int, message: str, suggestion: str = "" ): """Add review comment.""" self.comments.append(ReviewComment( category=category, severity=severity, file=file, line=line, message=message, suggestion=suggestion )) def check_correctness(self, code: str): """Check correctness issues.""" checks = [ self._check_error_handling, self._check_edge_cases, self._check_logic_errors, self._check_type_safety, ] for check in checks: check(code) def check_security(self, code: str): """Check security issues.""" checks = [ self._check_input_validation, self._check_sql_injection, self._check_secret_exposure, self._check_authentication, ] for check in checks: check(code) def check_performance(self, code: str): """Check performance issues.""" checks = [ self._check_n_plus_one, self._check_inefficient_loops, self._check_memory_leaks, self._check_async_usage, ] for check in checks: check(code) def get_blocking_issues(self) -> List[ReviewComment]: """Get blocking issues that prevent merge.""" return [ c for c in self.comments if c.severity == ReviewSeverity.BLOCKING ] def generate_summary(self) -> Dict[str, int]: """Generate review summary.""" summary = { "total_comments": len(self.comments), "blocking": 0, "major": 0, "minor": 0, "nitpick": 0, } for comment in self.comments: summary[comment.severity.value] += 1 return summary ``` ## Correctness Review ```python """ Correctness Review Checklist =========================== 1. Error Handling """ # ❌ Missing error handling async def fetch_user(user_id: int): response = await http_client.get(f"/users/{user_id}") return response.json() # What if request fails? # ✅ Proper error handling async def fetch_user(user_id: int) -> Dict: """ Fetch user by ID. Args: user_id: User ID Returns: User data dict Raises: UserNotFoundError: If user doesn't exist APIError: If API request fails """ try: response = await http_client.get(f"/users/{user_id}") response.raise_for_status() return response.json() except httpx.HTTPStatusError as e: if e.response.status_code == 404: raise UserNotFoundError(f"User {user_id} not found") raise APIError(f"API request failed: {e}") except httpx.RequestError as e: raise APIError(f"Network error: {e}") """ 2. Edge Cases """ # ❌ No edge case handling def divide(a: float, b: float) -> float: return a / b # What if b is 0? # ✅ Edge cases handled def divide(a: float, b: float) -> float: """ Divide a by b. Args: a: Numerator b: Denominator Returns: Result of division Raises: ValueError: If b is zero """ if b == 0: raise ValueError("Cannot divide by zero") return a / b """ 3. Logic Errors """ # ❌ Logic error def calculate_discount(price: float, discount_percent: float) -> float: return price - price * discount_percent # Should divide by 100! # ✅ Correct logic def calculate_discount(price: float, discount_percent: float) -> float: """Calculate price after discount.""" if not 0 <= discount_percent <= 100: raise ValueError("Discount must be between 0 and 100") return price - (price * discount_percent / 100) """ 4. Type Safety """ # ❌ No type hints def process_data(data): # What type is data? return data.upper() # ✅ Type hints def process_data(data: str) -> str: """Process string data.""" return data.upper() ``` ## Security Review ```python """ Security Review Checklist ======================== 1. Input Validation """ # ❌ No input validation @app.post("/users") async def create_user(email: str, password: str): user = User(email=email, password=password) db.add(user) return user # ✅ Input validation with Pydantic class UserCreate(BaseModel): email: EmailStr # Validates email format password: str = Field(min_length=8, max_length=100) @validator("password") def validate_password(cls, v): if not any(c.isupper() for c in v): raise ValueError("Password must contain uppercase") if not any(c.isdigit() for c in v): raise ValueError("Password must contain digit") return v @app.post("/users") async def create_user(user: UserCreate): # Input is validated hashed_password = hash_password(user.password) db_user = User(email=user.email, password=hashed_password) db.add(db_user) return db_user """ 2. SQL Injection Prevention """ # ❌ SQL injection vulnerability def get_user(email: str): query = f"SELECT * FROM users WHERE email = '{email}'" return db.execute(query) # Vulnerable! # ✅ Parameterized queries def get_user(email: str): query = text("SELECT * FROM users WHERE email = :email") return db.execute(query, {"email": email}) """ 3. Secret Exposure """ # ❌ Hardcoded secrets API_KEY = "sk-1234567890abcdef" # Exposed! DATABASE_URL = "postgresql://user:password@localhost/db" # ✅ Environment variables from pydantic_settings import BaseSettings class Settings(BaseSettings): api_key: str = Field(alias="API_KEY") database_url: str = Field(alias="DATABASE_URL") class Config: env_file = ".env" env_file_encoding = "utf-8" """ 4. Authentication & Authorization """ # ❌ No authentication @app.delete("/users/{user_id}") async def delete_user(user_id: int): db.delete(User, user_id) # Anyone can delete! # ✅ Proper authentication and authorization @app.delete("/users/{user_id}") async def delete_user( user_id: int, current_user: User = Depends(get_current_user) ): # Check authorization if not current_user.is_admin and current_user.id != user_id: raise HTTPException(403, "Not authorized") db.delete(User, user_id) ``` ## Performance Review ```python """ Performance Review Checklist =========================== 1. N+1 Queries """ # ❌ N+1 query problem async def get_orders(): orders = db.query(Order).all() for order in orders: print(order.user.email) # N queries! # ✅ Eager loading async def get_orders(): orders = db.query(Order).options(joinedload(Order.user)).all() for order in orders: print(order.user.email) # Single query """ 2. Inefficient Loops """ # ❌ Inefficient loop def find_duplicates(items: List[str]) -> List[str]: duplicates = [] for i, item in enumerate(items): for j, other in enumerate(items): if i != j and item == other: duplicates.append(item) # O(n²) return duplicates # ✅ Efficient with set def find_duplicates(items: List[str]) -> List[str]: seen = set() duplicates = set() for item in items: if item in seen: duplicates.add(item) seen.add(item) return list(duplicates) # O(n) """ 3. Async Usage """ # ❌ Blocking I/O in async async def fetch_data(): response = requests.get("https://api.example.com") # Blocks! return response.json() # ✅ Async I/O async def fetch_data(): async with httpx.AsyncClient() as client: response = await client.get("https://api.example.com") return response.json() """ 4. Memory Usage """ # ❌ Loading entire file def process_file(filepath: str): with open(filepath) as f: content = f.read() # All in memory! for line in content.split('\n'): process_line(line) # ✅ Streaming def process_file(filepath: str): with open(filepath) as f: for line in f: # One line at a time process_line(line) ``` ## Review Comment Template ```markdown ## Review Summary ### Overview - **Files Changed**: 5 - **Lines Changed**: +150, -30 - **Blocking Issues**: 0 - **Major Issues**: 2 - **Minor Issues**: 3 ### Blocking Issues None ### Major Issues 1. **File: `api/users.py`, Line 45** (Security) ```python # Current query = f"SELECT * FROM users WHERE id = {user_id}" # Suggestion query = text("SELECT * FROM users WHERE id = :id") result = db.execute(query, {"id": user_id}) ``` **Reason**: SQL injection vulnerability. Always use parameterized queries. 2. **File: `services/orders.py`, Line 78** (Performance) ```python # Current orders = db.query(Order).all() for order in orders: print(order.user.email) # N+1 query # Suggestion orders = db.query(Order).options(joinedload(Order.user)).all() ``` **Reason**: N+1 query problem. Use eager loading. ### Minor Issues 1. **File: `models/user.py`, Line 12** (Type Safety) - Missing return type hint on `get_full_name` method - Add: `-> str` 2. **File: `tests/test_users.py`, Line 34** (Testing) - Missing test for edge case: empty email - Add test case 3. **File: `utils/helpers.py`, Line 56** (Documentation) - Missing docstring for `format_date` function - Add Google-style docstring ### Positive Feedback - ✅ Good use of Pydantic models for validation - ✅ Comprehensive error handling - ✅ Well-structured code - ✅ Good test coverage (85%) ### Next Steps 1. Address major issues (SQL injection, N+1) 2. Consider minor improvements 3. Update tests 4. Update documentation **Overall Assessment**: Approve after addressing major issues. ``` ## ❌ Anti-Patterns in Reviews ```python # ❌ Vague comments "This doesn't look right" # ✅ Better: Specific with suggestion "SQL injection vulnerability on line 45. Use parameterized queries: query = text('SELECT * FROM users WHERE id = :id')" # ❌ No severity indication "You should add error handling" # ✅ Better: Clear severity "[BLOCKING] Missing error handling. API calls can fail and crash the app." # ❌ Only pointing out negatives "You forgot type hints, missing tests, bad variable names" # ✅ Better: Balance with positives "Good use of Pydantic! One suggestion: add type hints to helper functions" # ❌ Style preferences as blocking "[BLOCKING] Use single quotes instead of double quotes" # ✅ Better: Appropriate severity "[NITPICK] Consider single quotes for consistency" ``` ## Best Practices Checklist ### Reviewer - ✅ Use structured review checklist - ✅ Categorize comments (correctness, security, etc.) - ✅ Indicate severity (blocking, major, minor) - ✅ Provide specific suggestions with code examples - ✅ Balance criticism with positive feedback - ✅ Focus on important issues first - ✅ Be respectful and constructive - ✅ Test the code if possible - ✅ Check for security vulnerabilities - ✅ Review tests and documentation ### Author - ✅ Self-review before requesting review - ✅ Provide context in PR description - ✅ Keep PRs focused and small (<400 lines) - ✅ Respond to all comments - ✅ Don't take feedback personally - ✅ Ask questions if unclear - ✅ Mark resolved comments - ✅ Update tests and docs - ✅ Verify all blocking issues fixed - ✅ Request re-review after changes ## Auto-Apply When reviewing code: 1. Use structured checklist (correctness, security, performance) 2. Categorize and prioritize issues 3. Provide specific suggestions with code 4. Mark severity (blocking, major, minor, nitpick) 5. Include positive feedback 6. Focus on impact, not style 7. Be respectful and constructive ## Related Skills - `type-safety` - For type checking - `async-await-checker` - For async patterns - `structured-errors` - For error handling - `pytest-patterns` - For test review - `fastapi-patterns` - For API review - `pydantic-models` - For validation review