The Python tooling landscape for working with a project is confusing and unnecessary complex. There are tons of tools for every step in the project workflow like package management, Python version management, linting and formatting, etc.
In this post, I show you 3 tools I successfully use on every project which I can’t live without.
1. Package, Project and Python Version Management
For managing your packages, project structure, and Python versions, I use uv. It’s one of the best tools I’ve come across in the Python ecosystem because it seamlessly bundles together many functionalities that were previously provided by separate tools. (see image below).

Let’s see how we can use the basic commands:
Installation
# On macOS and Linux.
curl -LsSf https://astral.sh/uv/install.sh | sh
# On Windows.
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
List available Python versions (system-managed and uv-managed)
uv python list
Install Python 3.11.9
uv python install 3.11.9
Initialize project with Python 3.11.9
uv init my-project --package --python 3.11.9
The package
option is to create a packaged application like CLI apps installable through a package installer.
Result
Add/remove package
uv add requests
uv remove requests
Run Script
uv run hello.py
Adding package to a dependency group
uv add --group dev pytest
Installing project dependencies in a new project
uv sync
Ommitting dev dependencies
uv sync --no-group dev
Just installing dev dependencies
uv sync --only-group dev
2. Linting and Formatting with Ruff
Ruff is an ultra-fast linter and code formatter written in Rust. It’s designed to replace several tools at once integrating features from Flake8, Black, isort, and more while running tens to hundreds of times faster than traditional Python-based alternatives. With support for over 800 rules, Ruff is an obvious choice for maintaining code quality in your projects.
Installing:
uv add ruff
Run linter over whole project
uv run ruff check .
If Ruff finds any fixable errors, patch them automatically by adding the --fix
flag:
ruff check . --fix
Run formatter over whole project
ruff format .
You can also integrate Ruff into your project’s configuration via a pyproject.toml
(or ruff.toml
) file:
[tool.ruff.lint]
select = ["E", "F", "UP", "B", "SIM", "I"] # Rules can be looked up here https://docs.astral.sh/ruff/rules/
[tool.ruff.format]
quote-style = "double"
line-ending = "auto"
docstring-code-format = true
3. Pre-commit Hooks with pre-commit
Automating checks before every commit is a powerful way to maintain code quality. The pre-commit framework allows you to integrate tools like Ruff into your Git workflow so that static checks run automatically before your code is committed. This minimizes the chance of low-quality code making its way into your codebase.
Installing pre-commit
uv add pre-commit
Set up pre-commit
uv run pre-commit install
Create a .pre-commit-config.yaml
file where you define which hooks run when committing something
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.9.6
hooks:
# Run the linter.
- id: ruff
# Run the formatter.
- id: ruff-format
Now, when you run git add . && git commit -m “commit“,
pre-commit will run your defined hooks.
Conclusion
In this post, we explored the three essential tools that I use on every project: uv for project and Python version management, Ruff for fast linting and formatting, and pre-commit to automate quality checks before code is committed. In my experience, these tools are indispensable and I haven’t encountered any alternatives that match their combined efficiency and seamless integration.
Great summary, nicely structured also! Thanks for sharing