# ADR-002: Dependency Management — uv

## Status

Accepted

## Context

Python dependency management has historically been fragmented: `pip` + `requirements.txt`, `poetry`, `pipenv`, `conda`, and `hatch` each solve different subsets of the problem with varying performance and UX. The Keelson service requires:

- Reproducible installs across developer machines and CI
- Fast installs in Docker build layers
- Support for optional dependency groups (dev, test, adapters)
- PEP 517/518 compliance (no custom build backends)
- A single lockfile that pins transitive dependencies

Options considered:

1. **uv** — Rust-based fast resolver and installer by Astral; supports `pyproject.toml` and `uv.lock`
2. **Poetry** — established dependency manager with its own lockfile (`poetry.lock`) and build backend
3. **pip + pip-tools** — standard tooling with `pip-compile` for lockfiles; universally available

## Decision

We use **uv** for dependency management and virtual environment creation.

Key factors:

- **Speed** — uv resolves and installs dependencies 10–100× faster than pip or poetry; this materially reduces Docker build times and CI feedback loops.
- **`pyproject.toml` native** — uv reads standard `[project]` and `[project.optional-dependencies]` tables; no proprietary `[tool.poetry.dependencies]` syntax.
- **`uv.lock`** — cross-platform lockfile with hash verification; semantically equivalent to `poetry.lock` but generated by a faster resolver.
- **Drop-in install UX** — `uv sync` replaces `pip install -e ".[dev]"` with a single command that also creates the virtual environment.
- **Active development** — Astral maintains uv with rapid iteration; it has become the de facto standard in new Python projects as of 2025.

Poetry was ruled out because its custom build backend adds friction when integrating with `hatchling` (which the project already uses) and its resolver is significantly slower. `pip-tools` was ruled out because it does not manage virtual environments, requiring a separate `venv` step in every environment.

## Consequences

**Positive:**
- `uv sync` in Docker and CI installs all dependencies including the project itself in seconds.
- `uv add <package>` updates both `pyproject.toml` and `uv.lock` atomically.
- `uv run pytest` executes commands inside the managed virtualenv without `source .venv/bin/activate`.
- Optional groups (`uv sync --extra dev`) map directly to `[project.optional-dependencies]` in `pyproject.toml`.

**Negative / trade-offs:**
- `uv` must be installed in CI and Docker images; it is not part of the Python standard library. The install is a single `curl | sh` or `pip install uv` step, but it is an extra dependency.
- Developers on Windows need PowerShell or WSL2 for the `curl`-based installer; alternatively `pip install uv` works universally.
- `uv.lock` is not compatible with `poetry.lock` or `pip-tools` `.txt` format; switching away from uv in the future requires regenerating lockfiles.

**Neutral:**
- The `Makefile` uses `uv run` for all developer commands (`make test`, `make lint`, `make typecheck`).
- The `Dockerfile` installs uv via its official installer script before running `uv sync --no-dev`.
- `Dockerfile.dev` runs `uv sync --extra dev` to include test and linting tools.
