uv and Ruff: The Rust-Powered Python Toolchain in Production
Two years ago, my Python projects needed pip, pip-tools, pyenv, virtualenv, Black, isort, and flake8 just to get from a clean checkout to a passing CI run. Today they need two binaries: uv and ruff. Both are written in Rust by Astral, both are really fast, and both could become the default choice for new Python projects.
This post is for anyone who writes Python day-to-day and wants a straight answer to “should I actually switch, and what does it look like once I have?” I will walk through setting up a real project with uv, linting and formatting it with ruff, wiring both into CI, and the one configuration mistake that causes almost every “it works locally but fails in CI” report I have seen.
Which Python Tools uv and Ruff Replace
uv is a single Rust binary that manages Python interpreters, virtual environments, dependencies, lockfiles, and command-line tools — standing in for pip, pip-tools, pyenv, virtualenv, and pipx. Ruff is a single Rust binary that lints and formats Python, standing in for flake8, Black, isort, and pyupgrade, implementing over 800 lint rules and running one to two orders of magnitude faster than the tools it replaces.
| Old tool |
Replaced by |
What the single binary now owns |
| pip, pip-tools |
uv add, uv lock, uv sync |
Resolution and a cross-platform uv.lock |
| pyenv |
uv python pin, uv python install |
Interpreter download and per-project pinning |
| virtualenv, venv |
uv venv (implicit in uv run) |
Environment creation |
| pipx |
uv tool install, uvx |
Isolated CLI tools |
| flake8 |
ruff check |
Lint rules, including the plugin families |
| Black |
ruff format |
Formatting |
| isort |
ruff check rule family I |
Import sorting |
| pyupgrade |
ruff check rule family UP |
Syntax modernisation |
Both come from the same company, Astral. On 19 March 2026, OpenAI announced an agreement to acquire Astral, with the team joining the Codex group and closing subject to the usual regulatory conditions. OpenAI’s announcement states its intention to keep supporting Astral’s open source products — uv, ruff, and the newer ty type checker — after the deal closes. OpenAI’s own figures put uv at more than 126 million downloads in the month before the announcement, which tells you why a model company bought a package manager. It was not for fun.
Worth being honest about what that guarantee is: a stated intention, not a licence change. uv and ruff are MIT-licensed and the history is public, so the downside scenario is a fork, not a lockout. I would still keep the version pinned in uv.lock rather than tracking latest blindly, but that was already true before March.
Setting up a project with uv, linting and formatting with Ruff, running both in CI, and a migration checklist follow below.
Project Setup: Initialising and Locking Dependencies with uv
🔒 Subscribe to keep reading.
Linting and Formatting Python with Ruff: check, format and fix safety
🔒 Subscribe to keep reading.
Running uv and Ruff in GitHub Actions CI
🔒 Subscribe to keep reading.
Mental Model: uv as Dependency Transport, Ruff as Code Inspection
🔒 Subscribe to keep reading.
Migration Checklist: Moving a Python Project to uv and Ruff
🔒 Subscribe to keep reading.
Final Thoughts: Is the uv and Ruff Migration Worth It?
🔒 Subscribe to keep reading.
References
🔒 Subscribe to keep reading.
You've hit a Deep Dive tutorial.
I spend dozens of hours researching, coding, and breaking things to write these guides. This content is free, but reserved for my subscriber community. Drop your email below to unlock this guide (and all past/future deep dives):
Full content temporarily unavailable — refresh in a moment
Already a subscriber? Use the magic link from your last newsletter, or reset your password.
Log in to unlock
New subscribers get an inbox mail: Set a password to unlock articles. The form does not log you in — use the same email afterwards.