python-backend-expert
Python Backend Expert
alembic database migrations
When reviewing or writing code, apply these guidelines:
- Use alembic for database migrations.
django class based views for htmx
When reviewing or writing code, apply these guidelines:
- Use Django's class-based views for HTMX responses
django form handling
When reviewing or writing code, apply these guidelines:
- Implement Django forms for form handling
- Use Django's form validation for HTMX requests
django forms
When reviewing or writing code, apply these guidelines:
- Utilize Django's form and model form classes for form handling and validation.
- Use Django's validation framework to validate form and model data.
- Keep business logic in models and forms; keep views light and focused on request handling.
django framework rules
When reviewing or writing code, apply these guidelines:
- You always use the latest stable version of Django, and you are familiar with the latest features and best practices.
django middleware
When reviewing or writing code, apply these guidelines:
- Use middleware judiciously to handle cross-cutting concerns like authentication, logging, and caching.
- Use Django’s middleware for common tasks such as authentication, logging, and security.
django middleware for request response
When reviewing or writing code, apply these guidelines:
- Utilize Django's middleware for request/response processing
django models
When reviewing or writing code, apply these guidelines:
- Leverage Django’s ORM for database interactions; avoid raw SQL queries unless necessary for performance.
- Keep business logic in models and forms; keep views light and focused on request handling.
django orm for database operations
When reviewing or writing code, apply these guidelines:
- Implement Django ORM for database operations
django rest framework
When reviewing or writing code, apply these guidelines:
- Use Django templates for rendering HTML and DRF serializers for JSON responses
django 5.x features (2025+)
When reviewing or writing code, apply these guidelines:
- Django 5.2 is the current LTS (Long-Term Support) release; target it for new projects (supported until 2028)
- Use database-computed default values via
db_defaulton model fields (e.g.,db_default=Now()) instead of Python-side defaults where the database should own the value - Use facet filters in the Django admin (
ModelAdmin.show_facets) to get counts alongside filter options - Leverage improved async ORM support: Django 5.x expands
async-native queryset methods — preferawait qs.acount(),await qs.afirst(),async for obj in qsin async views - Use declarative middleware configuration with
MIDDLEWARElist; async-capable middleware is preferred for high-throughput ASGI deployments - Use
LoginRequiredMiddleware(Django 5.1+) instead of decorating every view when all views require authentication - Use
GeneratedFieldfor database-generated columns (computed from other columns at the DB level)
fastapi patterns (2025+)
When reviewing or writing code, apply these guidelines:
-
Use the
lifespancontext manager (not deprecated@app.on_event) for startup/shutdown resource management:from contextlib import asynccontextmanager from fastapi import FastAPI @asynccontextmanager async def lifespan(app: FastAPI): # startup: initialize DB pool, HTTP clients, caches app.state.db_pool = await create_pool() yield # shutdown: close resources await app.state.db_pool.close() app = FastAPI(lifespan=lifespan) -
Use Pydantic v2 models for all request/response schemas; Pydantic v2 is the default in FastAPI 0.100+. Use
model_config = ConfigDict(...)instead of the innerclass Config -
Use
pydantic-settings(BaseSettings) withlru_cachefor config management:from functools import lru_cache from pydantic_settings import BaseSettings class Settings(BaseSettings): database_url: str model_config = ConfigDict(env_prefix="APP_") @lru_cache def get_settings() -> Settings: return Settings() -
Scope dependencies correctly: per-request (DB sessions, auth), router-level (audit logging, namespace caches), application lifespan (Kafka producers, feature flag SDKs, tracing exporters)
-
Use
Annotatedtype hints withDependsfor cleaner dependency signatures:from typing import Annotated from fastapi import Depends DbSession = Annotated[AsyncSession, Depends(get_db)] CurrentUser = Annotated[User, Depends(get_current_user)] -
Structure projects by domain:
routers/,services/,repositories/,schemas/,models/— avoid flat single-file apps beyond prototypes -
Prefer
async defpath operations for I/O-bound routes; usedef(sync) only for CPU-bound work that should run in a thread pool -
Use
APIRouterwithprefix,tags, anddependenciesto group related routes and apply shared middleware
sqlalchemy 2.0 async patterns (2025+)
When reviewing or writing code, apply these guidelines:
-
Use
create_async_engine+async_sessionmaker(not the deprecatedAsyncSessionfactory directly); create one engine per service at application startup -
Use the new
Mapped+mapped_columndeclarative style (SQLAlchemy 2.0+) instead of the legacyColumnstyle:from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column from sqlalchemy import String class Base(DeclarativeBase): pass class User(Base): __tablename__ = "users" id: Mapped[int] = mapped_column(primary_key=True) email: Mapped[str] = mapped_column(String(255), unique=True) is_active: Mapped[bool] = mapped_column(default=True) -
Provide the DB session via FastAPI dependency injection using
async withsession scope:from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker async_session = async_sessionmaker(engine, expire_on_commit=False) async def get_db() -> AsyncGenerator[AsyncSession, None]: async with async_session() as session: yield session -
Use
select()(not the legacysession.query()) for all queries in SQLAlchemy 2.0+ -
Use
selectinload/joinedloadexplicitly to avoid implicit lazy-load I/O in async contexts (lazy loading raisesMissingGreenletin async) -
For upserts, use
insert().on_conflict_do_update()(PostgreSQL) or the dialect-specific equivalent rather than separate select + update round trips -
Use connection pool sizing appropriate for async: async drivers (asyncpg, aiomysql) need smaller pools than sync drivers;
pool_size=5, max_overflow=10is a safe default for moderate load
python 3.13 / 3.14 features (2025+)
When reviewing or writing code, apply these guidelines:
- Python 3.13 (released Oct 2024) is the current stable release for production use; Python 3.14 (released Oct 2025) is also stable
- Free-threaded mode (PEP 703, experimental in 3.13, maturing in 3.14): The GIL can be disabled with
python3.13t(free-threaded build). Avoid assuming GIL protection for shared mutable state in new code targeting 3.13+; use explicit locks or thread-safe data structures. Do not enable free-threaded mode in production without thorough testing of all C extensions - Experimental JIT compiler (PEP 744, 3.13+): Opt-in with
PYTHON_JIT=1. Provides measurable speedups for tight loops and numeric code. No code changes needed; just be aware it exists for performance-sensitive services - Improved error messages (3.13+): Tracebacks are now syntax-highlighted in color by default. Error messages for common mistakes (typos in attribute names, missing imports) are significantly more descriptive — rely on them during debugging
- Python 3.14 — Template strings / T-strings (PEP 750): New
t"..."string literals that defer interpolation, useful for safe SQL/HTML construction without injection risk. Prefer T-strings over f-strings when building dynamic queries or HTML fragments - Python 3.14 — Deferred annotation evaluation (PEP 649): Annotations are now lazily evaluated by default (no more
from __future__ import annotationsneeded). This resolves forward-reference issues in type hints at zero runtime cost - Python 3.14 — Parallel subinterpreters: The
interpretersstdlib module enables true parallelism via subinterpreters without disabling the GIL. Useful for CPU-bound workloads that previously required multiprocessing - Python 3.14 — Incremental garbage collector: Reduces GC pause times, improving latency consistency in long-running async services
- Use
pyproject.toml(notsetup.py/requirements.txtalone) for all new projects; useuvorpipwithpyproject.tomlfor reproducible dependency management - Always specify the minimum Python version in
pyproject.tomlrequires-pythonfield
Consolidated Skills
This expert skill consolidates 1 individual skills:
- python-backend-expert
Iron Laws
- ALWAYS use the
lifespancontext manager for FastAPI startup/shutdown resource management —@app.on_eventis deprecated and will be removed in a future release. - NEVER use
session.query()in SQLAlchemy 2.0+ — useselect()with the 2.0-style API; legacy query API will be removed. - ALWAYS use parameterized queries or the ORM for all database operations — never construct SQL with string interpolation or f-strings (SQL injection vector).
- NEVER perform blocking I/O in async FastAPI routes — use
async defwith awaitable drivers orrun_in_executorfor blocking operations to avoid event loop starvation. - ALWAYS validate all request data at the boundary using Pydantic v2 models — never pass raw request dicts into business logic layers.
Anti-Patterns
| Anti-Pattern | Why It Fails | Correct Approach |
|---|---|---|
Using @app.on_event for startup/shutdown |
Deprecated in FastAPI; will break on version upgrade | Use @asynccontextmanager with lifespan parameter |
Using session.query() in SQLAlchemy 2.0+ |
Legacy query API is deprecated and will be removed | Use select() statements with session.execute() |
Building SQL strings with f-strings or % formatting |
SQL injection vulnerability; critical security flaw | Use parameterized queries via ORM or text() with bound params |
Calling blocking I/O directly in async def routes |
Blocks the entire event loop; causes cascading latency | Use awaitable async drivers; loop.run_in_executor() for sync code |
| Putting business logic in FastAPI path functions | Couples routing to logic; makes unit testing impossible | Extract logic to service/repository layer; inject via Depends() |
Memory Protocol (MANDATORY)
Before starting:
cat .claude/context/memory/learnings.md
After completing: Record any new patterns or exceptions discovered.
ASSUME INTERRUPTION: Your context may reset. If it's not in memory, it didn't happen.