Ruff Setup
Ruff is a Rust-based linter and formatter that replaces flake8, isort, pyupgrade, and dozens of plugins in a single fast tool. It is the default linter for Python 3.14 projects.
Recipe
uv add --group dev ruff[tool.ruff]
line-length = 88
target-version = "py314"
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM"]uv run ruff check .
uv run ruff format .When to reach for this:
- Replacing flake8 + isort + autoflake + pyupgrade
- CI lint steps that take more than a few seconds
- New Python projects needing a zero-config quality baseline
Working Example
# pyproject.toml
[tool.ruff]
line-length = 88
target-version = "py314"
src = ["src", "tests"]
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"F", # pyflakes
"I", # isort
"UP", # pyupgrade
"B", # bugbear
"SIM", # simplify
]
ignore = ["E501"] # line length handled by formatter
[tool.ruff.lint.isort]
known-first-party = ["myapp"]
[tool.ruff.format]
quote-style = "double"uv run ruff check . --fix
uv run ruff format .
uv run ruff check . # verify cleanWhat this demonstrates:
- Single config block replaces multiple legacy tools
selectenables rule categories;ignoresuppresses specific codes--fixauto-corrects safe violations (imports, pyupgrade, some bugbear)ruff formatprovides Black-compatible formatting
Deep Dive
How It Works
- Ruff parses Python source with a Rust parser - typically 10-100x faster than flake8
- Rule codes map to flake8 plugins (e.g.,
B= bugbear,I= isort) - The formatter is compatible with Black's style by default
- One binary handles lint, format, and import sorting
Essential Rule Sets
| Code | Source | Catches |
|---|---|---|
| E, W | pycodestyle | Style violations |
| F | pyflakes | Undefined names, unused imports |
| I | isort | Import order |
| UP | pyupgrade | Outdated syntax |
| B | bugbear | Common bugs |
| SIM | simplify | Overly complex code |
Gotchas
- Running ruff without config - defaults may not match team style. Fix: commit
[tool.ruff]inpyproject.toml. - E501 conflicts with formatter - double enforcement. Fix: ignore
E501; letruff formathandle line length. - Not setting
target-version- pyupgrade won't suggest 3.14 syntax. Fix: settarget-version = "py314". - Missing
srcpaths - first-party import detection fails. Fix: setsrc = ["src"]for src-layout projects. - Replacing formatters incrementally - Black and ruff format fight in CI. Fix: pick one formatter; remove the other from CI.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| flake8 + plugins | Legacy config investment | Starting fresh |
| pylint | Deep semantic analysis needed | Speed matters |
| Black only | Format-only, no lint | You want one tool |
FAQs
Does ruff replace Black?
ruff format is Black-compatible. Most teams use ruff for both lint and format.
How do I ignore a line?
# noqa: F401 on the line, or # ruff: noqa for all rules on that line.
Can ruff fix all violations?
Many rules support --fix. Some (logic errors) require manual fixes.
How do I add ruff to pre-commit?
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-formatWhat is the difference between check and format?
ruff check lints. ruff format formats. Run both in CI.
How do I see all enabled rules?
ruff linter lists rule codes. ruff rule E501 explains a specific rule.
Does ruff support monorepos?
Yes. Use src and per-directory pyproject.toml or extend config.
Can I use ruff with mypy?
Yes. Ruff catches style and obvious bugs; mypy catches type errors. Run both.
How fast is ruff vs flake8?
Typically 10-100x faster on medium-to-large codebases.
What version should I pin?
ruff>=0.9 per the project stack. Pin exact version in pre-commit rev.
Related
- Black / Ruff Format - formatting details
- Essential Lint Rules - rule set recommendations
- pre-commit Hooks - git hook integration
- Linting in CI/CD - pipeline enforcement
Stack versions: This page was written for Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.