Skip to content
AB

uv, Ruff, and pytest: A Modern Python Backend Toolchain

Use a small, explicit toolchain for dependency management, formatting, linting, security checks, and behavioral tests.

Ashish BhakhandPublished

Python projects can accumulate overlapping tools for environments, dependency locking, imports, formatting, linting, security, and tests. I prefer a small set with explicit responsibilities.

uv owns the environment and lock#

uv installs the project from pyproject.toml, maintains a lockfile, and runs commands inside the environment:

Code
uv sync --frozen
uv run backend
uv run pytest -q

--frozen is valuable in CI because it fails when project metadata and the committed lock disagree instead of silently changing dependency resolution.

Ruff owns formatting and static rules#

Ruff can format code and enforce imports, common bugs, modern Python syntax, async pitfalls, naming, simplification, and selected security rules.

Code
[tool.ruff]
target-version = "py312"
line-length = 110

[tool.ruff.lint]
select = ["E", "W", "F", "I", "B", "UP", "ASYNC", "S"]

Do not enable rules merely to produce a larger configuration. Each exception should explain why the general rule does not fit. FastAPI's Depends(...) defaults, for example, can require a targeted exception to a rule designed for ordinary function defaults.

pytest owns behavior#

pytest provides readable fixtures, parameterization, monkeypatching, and failure output. pytest-asyncio or AnyIO support lets tests call asynchronous services naturally.

Tests should prove contracts and decisions rather than compensate for weak types or duplicate the linter.

Make the merge gate boring#

Code
uv run ruff format --check src tests
uv run ruff check src tests
uv run pytest -q

If the schema changes, also require an idempotent migration and relevant documentation updates. CI should use the same commands developers run locally.

Tooling is valuable when it removes repeated judgment and makes failures easy to reproduce. A short documented toolchain that everyone runs is stronger than a sophisticated collection that only CI understands.

← Back to all writing