mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-24 05:31:09 -05:00
- 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.
357 lines
7.3 KiB
Markdown
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]
|
|
```
|