Skip to content

0003. Use uv, not pip or Poetry

Status: Accepted Date: 2026-08-28

Context

Both the template and every service it generates need one answer to dependency resolution, locking, virtual environments and running project commands, decided once before anything else in the toolchain — the justfile, the pre-commit hooks, the CI workflows — is written against it.

Decision

We use uv for everything Python: resolving and locking dependencies, creating and managing virtual environments, running commands and pinned tools, and pinning the Python interpreter itself. uv.lock is committed at the root of the template and inside examples/reference-service. There is no requirements.txt anywhere in the repository, and pip is never invoked directly.

Alternatives considered

  • pip with pip-tools. Rejected: it is separate tools for resolving, locking and running, stitched together by hand, and it has no dependency-group concept. Splitting the service's runtime dependencies from the documentation toolchain's would be a manual convention rather than something the tool itself understands.
  • Poetry. Rejected on two counts: slower dependency resolution, and its own non-standard sections in pyproject.toml. uv instead writes PEP 621 metadata — the standard [project] table other tools already know how to read — so nothing generated here locks a reader into a Poetry-specific reading of the manifest.

Consequences

One tool covers locking, syncing, running and tool execution, and its resolution is fast enough that uv sync is affordable in every CI job rather than something to cache around. A contributor learns one command surface instead of three.

The cost is a young tool sitting at the centre of the build: uv is newer than pip or Poetry, with a shorter track record and a faster release cadence, and every just recipe and CI job depends on it working correctly. The one place a pip-named tool still appears is pip-audit (spec D2), which runs through uvx pip-audit rather than a project dependency — it audits the uv-resolved environment without reintroducing pip as a package manager.

Full reasoning: spec section 2.