Add Ruff to Automate Python Code Quality
The first productivity tool most Python beginners should add is not a testing framework or a deployment system. It is a tool that keeps the code readable and catches obvious problems early. Ruff gives you fast linting and formatting, and uv makes it easy to add Ruff to the project in a way the whole team can share.
UV Series
Add Ruff to the project and use it as part of your normal workflow.
Install Ruff as a project development dependency so the project pins the version.
You will know when to use uv add --dev ruff, uv run ruff, uvx ruff, and uv tool install ruff.
Add Ruff as a Development Dependency
From your project root, run:
uv add --dev ruff
The --dev flag matters. It tells uv that Ruff is a development tool for working on the project, not a runtime dependency that your application needs in production.
This is the best default setup for a shared project because the project now records which version of Ruff everyone should use.
Run the Linter and Formatter Through uv
Once Ruff is in the project, run it through the project environment:
uv run ruff check .
uv run ruff format .
The first command looks for code issues. The second formats the codebase. Many teams use both on every project because one checks and one rewrites.
If Ruff can fix something automatically, you can often use:
uv run ruff check . --fix
That makes Ruff feel immediately useful instead of purely judgmental.
See the Workflow on a Deliberately Messy File
Suppose main.py looks like this:
import requests,sys
def main( ):
print( requests.__version__ )
unused = 1
if __name__ == "__main__":
main()
Now run:
uv run ruff check .
uv run ruff format .
Ruff will complain about issues like unused imports or unused variables, and the formatter will normalize spacing and structure. That means you can spend more energy learning Python logic instead of inventing code style from scratch.
Add a Minimal Ruff Configuration Only When You Need It
Ruff works well with defaults, so do not rush into over-configuration. When you do want a project-specific setting, add it to pyproject.toml:
[tool.ruff]
line-length = 88
That keeps your project settings near the rest of the project metadata instead of scattering configuration into unrelated files.
Know the Difference Between uv run, uvx, and uv tool install
| Command style | Best use | Example |
|---|---|---|
| Project-pinned | Use when Ruff is part of the project workflow | uv add --dev ruff then uv run ruff check . |
| One-off temporary run | Use when you just want to try Ruff quickly | uvx ruff check . |
| Persistent global install | Use when you want Ruff available everywhere on your machine | uv tool install ruff@latest |
For beginners learning project discipline, the project-pinned approach is usually the right default.
How Ruff Fits Into Everyday Work
uv run ruff check .
uv run ruff format .
uv run main.py
A small loop like that keeps the project clean without adding much cognitive load. It is one of the easiest upgrades you can make to a beginner workflow.
Frequently Asked Questions
These are the practical questions beginners usually ask at this stage of the uv workflow.
Why add Ruff with --dev instead of as a normal dependency?
Because Ruff helps you work on the project. It is not usually something your application needs when users run the finished program.
When should I use uvx ruff?
Use it for a quick temporary run when you do not want Ruff permanently attached to the project or installed globally.
Why not always install Ruff globally?
A global install is convenient, but a project-pinned version is more reproducible. Everyone on the project gets the same behavior.
Does Ruff replace learning Python style?
No. It supports it. Ruff automates consistency and catches easy mistakes so you can focus more on structure and readability.
Should I run both ruff check and ruff format?
Yes, that is a strong default. Checking finds issues, and formatting enforces a consistent look.
Conclusion
Ruff is where a beginner project starts to feel like a real development environment instead of a loose folder of scripts.
The next step is different but equally valuable: adding type checking so the project can catch a different class of mistakes before you run the code.
Raell Dottin
Comments