Skip to main content

Add Ruff to Automate Python Code Quality

UV Series · Productivity Tools

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

Goal

Add Ruff to the project and use it as part of your normal workflow.

Main idea

Install Ruff as a project development dependency so the project pins the version.

Outcome

You will know when to use uv add --dev ruff, uv run ruff, uvx ruff, and uv tool install ruff.

Why Ruff belongs in a beginner workflow Beginner code improves faster when feedback is immediate. Ruff can point out avoidable issues, keep formatting consistent, and reduce the amount of style discussion you have to hold in your head while learning Python itself.
Install Ruff in the project

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.

Use Ruff

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.

A small example

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.

Configuration

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.

Three ways to use Ruff

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.

A realistic habit

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.

FAQ

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.

Next in the series

Add ty for Type Checking and Better Feedback

Raell Dottin

Comments