Files
thrillwiki_django_no_react/backend/docs/code_standards.md
pacnpal edcd8f2076 Add secret management guide, client-side performance monitoring, and search accessibility enhancements
- Introduced a comprehensive Secret Management Guide detailing best practices, secret classification, development setup, production management, rotation procedures, and emergency protocols.
- Implemented a client-side performance monitoring script to track various metrics including page load performance, paint metrics, layout shifts, and memory usage.
- Enhanced search accessibility with keyboard navigation support for search results, ensuring compliance with WCAG standards and improving user experience.
2025-12-23 16:41:42 -05:00

357 lines
7.3 KiB
Markdown

# Code Standards
This document defines the code quality standards for the ThrillWiki backend.
## Formatting & Style
### PEP 8 Compliance
All Python code must comply with PEP 8, verified using:
- **black**: Code formatting (line length: 88)
- **flake8**: Style checking (max-line-length: 88, max-complexity: 10)
- **ruff**: Fast linting and import sorting
### Running Formatters
```bash
# Format code
uv run black backend/
# Check style
uv run flake8 backend/ --max-line-length=88 --max-complexity=10
# Lint and fix
uv run ruff check backend/ --fix
```
## Docstring Requirements
### Coverage
- 100% coverage for public classes and methods
- 100% coverage for all functions
- Optional for private methods (but encouraged)
### Style
Follow Google-style docstrings:
```python
def function_name(arg1: Type1, arg2: Type2) -> ReturnType:
"""
Brief description of what this function does.
Longer description if needed, explaining the purpose,
behavior, and any important details.
Args:
arg1: Description of arg1
arg2: Description of arg2
Returns:
Description of return value
Raises:
ExceptionType: When this exception is raised
Example:
>>> function_name("value1", "value2")
"result"
"""
```
### Class Docstrings
```python
class ClassName:
"""
Brief description of what this class does.
Longer description if needed.
Attributes:
attr1: Description of attr1
attr2: Description of attr2
Example:
instance = ClassName()
instance.method()
"""
```
### View Docstrings
Views should include URL patterns and permissions:
```python
class MyView(DetailView):
"""
Brief description of what this view does.
View Type: CBV (DetailView)
URL Pattern: /resource/<slug>/
Template: app/resource_detail.html
Permissions: LoginRequired
"""
```
## Complexity Guidelines
### Limits
- **Maximum McCabe complexity**: 10
- **Maximum method length**: 50 lines
- **Maximum nesting depth**: 3 levels
### Checking Complexity
```bash
# Check McCabe complexity
uv run flake8 backend/ --max-complexity=10 --select=C901
# Get complexity metrics
uv run radon cc backend/apps/ -a
```
### Refactoring Strategies
1. **Extract helper methods** for distinct responsibilities:
```python
# Before
def process_data(self, data):
# Validate data (10 lines)
# Transform data (10 lines)
# Save data (10 lines)
# Send notifications (10 lines)
pass
# After
def process_data(self, data):
self._validate_data(data)
transformed = self._transform_data(data)
result = self._save_data(transformed)
self._send_notifications(result)
return result
```
2. **Use early returns** to reduce nesting:
```python
# Before
def process(self, data):
if data:
if data.get('field1'):
if data.get('field2'):
return result
return None
# After
def process(self, data):
if not data:
return None
if not data.get('field1'):
return None
if not data.get('field2'):
return None
return result
```
3. **Move complex logic to service layer**
## Service Layer Patterns
### Service Method Signature
Always use keyword-only arguments for service methods:
```python
class MyService:
@staticmethod
def create_entity(
*, # Force keyword-only arguments
name: str,
description: str = "",
created_by: Optional[User] = None,
) -> Entity:
"""Create a new entity."""
pass
```
### Validation Pattern
Always call `full_clean()` before save:
```python
@staticmethod
def create_park(*, name: str, ...) -> Park:
with transaction.atomic():
park = Park(name=name, ...)
park.full_clean() # Validate before save
park.save()
return park
```
## Import Organization
Imports should be organized in this order:
1. Standard library
2. Third-party packages
3. Django imports
4. Local app imports
```python
import logging
from typing import Any, Dict, Optional
from django.contrib.auth import get_user_model
from django.db import transaction
from rest_framework import status
from apps.core.exceptions import ServiceError
from .models import MyModel
```
## Type Hints
Use type hints for all function signatures:
```python
def process_data(
data: Dict[str, Any],
user: Optional[User] = None,
) -> ProcessResult:
"""Process data and return result."""
pass
```
## Testing Requirements
- Maintain or improve test coverage with changes
- Add tests for new service methods
- Add tests for new mixins and base classes
- Run tests before committing:
```bash
pytest backend/tests/ --cov=backend/apps --cov-report=html
```
## Logging Standards
### Logger Initialization
Every view and middleware file should initialize a logger:
```python
import logging
logger = logging.getLogger(__name__)
```
### Centralized Logging Utilities
Use the centralized logging utilities from `apps.core.logging` for structured logging:
```python
from apps.core.logging import log_exception, log_business_event, log_security_event
```
### When to Use Each Log Level
- **`logger.debug()`**: Detailed diagnostic information (disabled in production)
- **`logger.info()`**: General operational events (search queries, user actions)
- **`logger.warning()`**: Unexpected conditions that don't prevent operation
- **`logger.error()`**: Error conditions that require attention
- **`log_exception()`**: Exception handling with full stack trace
### Exception Logging
Use `log_exception` for all exception handlers:
```python
try:
# operation
except Exception as e:
log_exception(
logger,
e,
context={"operation": "get_filtered_queryset", "filters": filter_params},
request=self.request,
)
messages.error(self.request, f"Error: {str(e)}")
```
### Business Event Logging
Use `log_business_event` for significant business operations:
```python
log_business_event(
logger,
event_type="fsm_transition",
message=f"Park approved: {park.name}",
context={
"model": "Park",
"object_id": park.id,
"old_state": old_status,
"new_state": park.status,
},
request=request,
)
```
### Security Event Logging
Use `log_security_event` for authentication and security-related events:
```python
log_security_event(
logger,
event_type="user_login",
message=f"User {user.username} logged in successfully",
severity="low", # low, medium, high, critical
context={"user_id": user.id, "username": user.username},
request=request,
)
```
### What NOT to Log
Never log:
- Passwords or password hashes
- API tokens or secrets
- Session IDs
- Full credit card numbers
- Other sensitive PII
### Log Message Guidelines
- Use clear, concise messages
- Include relevant context (IDs, usernames, operation names)
- Use consistent naming conventions
- Avoid logging large data structures
## Pre-commit Configuration
The following pre-commit hooks are configured:
```yaml
repos:
- repo: https://github.com/psf/black
rev: 24.1.0
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: 7.1.1
hooks:
- id: flake8
args: [--max-line-length=88, --max-complexity=10]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.10
hooks:
- id: ruff
args: [--fix]
```