From Team Skills
Senior-level standard for building and extending Python backends. Enforces uv-only workflow, the latest stable Python installed on the machine, Pydantic v2 schemas + pydantic-settings config, a clean layered src/ layout, robust & optimized typed code, pytest testing, Ruff lint+format, and proper documentation. Use whenever creating a new Python project/service or adding backend code (APIs, services, data models, CLIs, workers).
How this skill is triggered — by the user, by Claude, or both
Slash command
/team-skills:backend-pythonThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The non-negotiable standard for any Python backend work. Treat this as a senior engineer's
The non-negotiable standard for any Python backend work. Treat this as a senior engineer's review bar: code does not ship unless it meets every section below.
Invoke this skill before writing code whenever you:
If the user types /backend-python, follow this skill from the top.
uv. Never use
raw pip, pip-tools, poetry, pipenv, conda, virtualenv, or a hand-rolled venv.pydantic-settings. No bare dicts crossing a boundary.ruff check and ruff format must pass clean before "done".pytest tests. No "I'll add tests later".src/ layout, clear layering, docstrings, README, and
design notes in Docs/..env (+ .env.example)
via pydantic-settings for local, secret manager for cloud. Never hardcode or print secrets.Run these at project start. Commands are identical across OSes (only .env loading differs).
# 1. Ensure uv exists
uv --version
# If missing (Windows): winget install astral-sh.uv | or: pip install uv (last resort)
# 2. See installed Pythons, pick the highest STABLE (ignore a*/rc/dev, pypy, graalpy, freethreaded)
uv python list --only-installed
# 3. If the latest stable isn't present, install it (example pins the line, not a patch)
uv python install 3.14
Pin the version so the project is reproducible:
requires-python = ">=3.14" in pyproject.toml..python-version file containing the chosen version (uv writes it on uv init --python).Rule of thumb for "latest stable": the highest
cpython-X.Y.Zwith noa/b/rc/+freethreadedsuffix fromuv python list. Today that is the 3.14 line.
# Application (service/API/worker). Creates pyproject.toml, .python-version, src/ layout.
uv init <project-name> --package --python 3.14
# Reusable library instead: uv init <name> --lib --python 3.14
Then shape it into this layered src/ layout (FastAPI shown as the default web framework;
keep the layering for any backend):
<project>/
├── pyproject.toml # single source of config: deps, ruff, pytest, coverage, type-checker
├── uv.lock # committed — reproducible installs
├── .python-version # pinned interpreter
├── .env.example # documented, committed (NO real values)
├── .gitignore # ignores .env, .venv, __pycache__, .ruff_cache, .pytest_cache, etc.
├── README.md # run/dev/test instructions
├── Docs/ # architecture & design notes (per global rule: all .md live here)
├── scripts/ # one-off utilities & migration scripts (per global rule)
├── src/
│ └── <package>/
│ ├── __init__.py
│ ├── main.py # app/entrypoint wiring only
│ ├── core/
│ │ ├── config.py # pydantic-settings Settings
│ │ ├── logging.py # structured logging setup
│ │ └── exceptions.py # domain exception hierarchy
│ ├── api/ # transport layer (routers/controllers); thin
│ │ ├── deps.py
│ │ └── v1/
│ │ ├── router.py
│ │ └── routes/
│ ├── schemas/ # Pydantic v2 I/O models (DTOs) — never leak ORM models
│ ├── models/ # ORM / persistence models
│ ├── services/ # business logic (pure, testable, framework-agnostic)
│ ├── repositories/ # data access; isolates DB from services
│ └── db/
│ └── session.py
└── tests/
├── conftest.py
├── unit/ # fast, no I/O — mirrors src/ structure
└── integration/ # DB/HTTP/external boundaries
Layering rule (dependency direction): api → services → repositories → db. Services hold
business logic and never import the web framework. Schemas (Pydantic) are the boundary between
layers — ORM models never cross into the API response directly.
# Runtime deps
uv add fastapi "uvicorn[standard]" pydantic pydantic-settings
# Dev deps (grouped — not shipped to prod)
uv add --dev pytest pytest-cov pytest-asyncio ruff ty pre-commit
# Run anything inside the project env (NEVER activate the venv manually)
uv run <cmd> # e.g. uv run pytest, uv run ruff check, uv run python -m <package>
# Sync / lock
uv sync # install from uv.lock
uv lock --upgrade # refresh the lockfile deliberately
Swap FastAPI for the framework the project needs, but keep
pydantic+pydantic-settingsand the dev toolchain.tyis Astral's type checker; usepyrightormypyif preferred.
src/<package>/core/config.py:
from functools import lru_cache
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
"""Application configuration, loaded from environment / .env."""
model_config = SettingsConfigDict(
env_file=".env", env_file_encoding="utf-8", extra="ignore", frozen=True
)
app_name: str = "service"
debug: bool = False
database_url: str = Field(..., description="DB DSN; injected via env/secret manager")
@lru_cache
def get_settings() -> Settings:
return Settings() # type: ignore[call-arg]
.env (+ a committed .env.example with placeholders like <DB_URL>).Typing & models
Any.UserCreate, UserRead, User).
Use model_config = ConfigDict(from_attributes=True) to build read-models from ORM objects.@field_validator / @model_validator); keep the
core logic working on trusted, typed objects.Structure & robustness
core/exceptions.py); translate to HTTP/transport
errors in one place (exception handlers / middleware). Never except Exception: pass.with / async with for all resources (DB sessions, files, clients). No leaks.Performance & async
async def for I/O-bound paths end-to-end; never block the event loop with sync I/O.Logging
core/logging.py; log decisions and errors with context, not secrets.print() in library/app code.tests/, mirroring src/; unit/ is fast and I/O-free, integration/ covers boundaries.conftest.py; parametrize edge cases; assert on behavior, not internals.pytest-asyncio. Aim for meaningful coverage (target ≥ 85% on core logic).uv run pytest # run all
uv run pytest --cov=src --cov-report=term-missing
uv run pytest tests/unit -q # fast loop
Both must pass clean before any task is "done":
uv run ruff format . # format (also: --check in CI)
uv run ruff check . --fix # lint + autofix
Config in pyproject.toml (see §9 template). Treat Ruff lint failures as build failures.
uv sync, how to run, test, and lint..md go here
per the global file-placement rule).Consolidate tool config in pyproject.toml:
[project]
name = "<package>"
requires-python = ">=3.14"
dependencies = [
"fastapi",
"uvicorn[standard]",
"pydantic",
"pydantic-settings",
]
[dependency-groups]
dev = ["pytest", "pytest-cov", "pytest-asyncio", "ruff", "ty", "pre-commit"]
[tool.ruff]
target-version = "py314"
line-length = 100
src = ["src", "tests"]
[tool.ruff.lint]
# sensible strong default; expand as needed
select = ["E", "F", "I", "UP", "B", "SIM", "C4", "ASYNC", "RUF"]
[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101"] # asserts allowed in tests
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-ra --strict-markers"
asyncio_mode = "auto"
[tool.coverage.run]
source = ["src"]
branch = true
.pre-commit-config.yaml (run uv run pre-commit install once):
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.14
hooks:
- id: ruff-check
args: [--fix]
- id: ruff-format
CI (and local "done" check) should run, in order:
uv sync → uv run ruff format --check . → uv run ruff check . → uv run ty check (or pyright/mypy) → uv run pytest --cov=src.
.python-version pins latest stable Python; uv.lock committed.src/ layered layout; transport thin; services framework-agnostic; ORM models don't leak into responses..env/secret manager (never hardcoded/printed).Docs/ notes updated in the same change.Guides collaborative design exploration before implementation: explores context, asks clarifying questions, proposes approaches, and writes a design doc for user approval.
Creates structured, bite-sized implementation plans from specs or requirements before writing code. Useful for breaking down multi-step tasks into testable steps with file structure and task boundaries.
Resolves in-progress git merge or rebase conflicts by analyzing history, understanding intent, and preserving both changes where possible. Runs automated checks after resolution.
npx claudepluginhub karthikrommula/team-toolkit --plugin team-skills