Astral, the company behind the Ruff linter and uv package manager, just announced experimental code formatting support in uv. If you’ve been following the Python tooling evolution, this is another significant step in what’s becoming one of the most ambitious consolidation efforts in any language ecosystem.
For context: uv started as a pip replacement — a fast package installer written in Rust. Then it absorbed virtual environment management. Then project management. Then Python version management. Now it’s adding code formatting. At some point, you have to step back and ask: is this the right approach, or is one tool trying to do too much?
The Case for Consolidation#
Let me start with why this matters. The traditional Python development setup involves juggling multiple tools:
- pyenv or python-build for Python version management
- venv or virtualenv for virtual environments
- pip (and maybe pip-tools or pip-compile) for package installation
- poetry or pdm for project management and dependency resolution
- black or autopep8 for code formatting
- ruff or flake8 for linting
- mypy or pyright for type checking
That’s seven or more tools, each with its own configuration file, its own update cycle, and its own quirks. Compared to ecosystems like Rust (cargo does almost everything) or Go (the go tool handles building, testing, formatting, and dependency management), Python has felt fragmented.
uv’s approach is to collapse this stack. One tool, written in Rust for performance, handling everything from installing Python itself to formatting your code. The appeal is obvious: fewer tools to install, configure, and keep updated. One consistent CLI interface. One configuration file.
What the Formatting Integration Looks Like#
The experimental formatting support in uv leverages the same Rust codebase that powers Ruff’s formatter. This isn’t a separate tool bundled in — it’s deeply integrated. You run uv fmt and your code gets formatted according to your project configuration.
The integration means formatting is aware of your project context. It knows your Python version target, your dependency tree, and your project structure. This contextual awareness allows for smarter formatting decisions and better error messages when things go wrong.
Performance is, as expected, excellent. Formatting a large codebase takes milliseconds rather than seconds. When you’re running formatting on every save or in a pre-commit hook, that speed difference compounds into meaningful productivity gains.
The Concerns#
I’m broadly positive about uv, but I think the consolidation trajectory deserves some critical examination.
Single point of failure: When one tool handles everything, a bug in that tool can block your entire workflow. If uv has a packaging bug, you can’t install dependencies. If it has a formatting bug, your CI pipeline breaks. With separate tools, a bug in Black doesn’t affect your ability to install packages.
Vendor concentration: Astral is a VC-funded startup. The Python ecosystem’s core tooling becoming dependent on a single company’s product is a legitimate concern. What happens if Astral’s business model doesn’t work out? The code is open source, which provides some insurance, but maintaining a project this complex requires sustained investment.
The Bazaar vs. the Cathedral: Python’s tooling fragmentation isn’t just a historical accident — it reflects the language’s culture of diverse approaches and community-driven development. There’s value in having multiple tools competing and innovating. When one tool dominates, that competitive pressure diminishes.
Configuration migration: If you’ve invested time configuring Black, isort, flake8, and mypy separately, migrating to a unified uv configuration isn’t trivial. The tools have subtly different defaults and behaviors, and ensuring your formatting output doesn’t change during migration requires careful testing.
The Broader Trend#
What’s happening with uv reflects a broader trend across programming language ecosystems: developers want batteries-included toolchains. The success of cargo (Rust), go (Go), and even deno (JavaScript/TypeScript) demonstrates that having a single, opinionated tool that handles the common workflow is what most developers prefer.
Python has historically resisted this approach — “there should be one obvious way to do it” is a Python zen principle, but Python tooling has been the poster child for having seventeen ways to do everything. uv is the most credible attempt to change that.
This week also saw the Ghostty project requiring AI tooling disclosure for contributions, which is an interesting parallel. As our tools become more powerful and more integrated, the question of transparency about what tools we use — and how much we depend on them — becomes increasingly important.
My Recommendation#
If you’re starting a new Python project today, use uv. The speed improvements alone justify the switch, and the integrated workflow is genuinely pleasant. The formatting support is experimental, but Astral has a strong track record of shipping quality — Ruff went from unknown to industry standard in about a year.
If you have existing projects with established tooling, don’t rush to migrate. Wait for the formatting support to stabilize, evaluate the migration path carefully, and switch when the benefits clearly outweigh the transition costs.
And regardless of what tools you use, keep your configuration in version control, document your toolchain choices, and make sure your team can reproduce the development environment from scratch. Tool choices change; good engineering practices don’t.
My Take#
I’ve watched Python’s tooling story evolve from distutils to setuptools to pip to poetry to uv, and each step has been an improvement. uv adding formatting feels like we’re approaching the endgame — a point where a single uv command can bootstrap an entire Python development environment from nothing.
That’s remarkable for a language that’s thirty years old. Most ecosystems this mature are stuck with their legacy tooling decisions. Python is getting a genuine fresh start on tooling, and so far, the results are impressive.
Will I miss the days of carefully curating my pyproject.toml with separate Black, isort, and mypy sections? Not even slightly.
