ruff review
Ruff 0.16.4 is a compiled command-line linter and formatter for Python files and notebooks. Its built-in rule catalog reimplements checks associated with Pyflakes, pycodestyle, isort, pyupgrade, flake8-bugbear, Bandit, Pylint, and many other tools; `ruff format` handles Black-compatible formatting as a separate command. Configuration can cascade through a monorepo, the linter caches unchanged files, fixes are labeled safe or unsafe, and editor integrations use the Ruff server. Version 0.16.4 fixes crashes on Windows CPUs without POPCNT, reports duplicate keyword arguments and parameters declared `nonlocal`, improves notebook pull diagnostics, and aligns several syntax and rule cases with current Python behavior.
Ruff 0.16.4 installed as 1 package using 1 MB in 0.5 seconds, imported in 0.04 seconds, and produced 0 audit findings in our sandbox. Pin it for linting, import rules, and formatting when its catalog covers the project, but keep a type checker and any unsupported plugin beside it.
We installed it
| Install | ✓ · 0.5s | 1 package on disk · 1 MB |
| Import | ✓ | import ruff in 0.04s · compiled extensions · requires Python >=3.7 |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does ruff install cleanly?
Yes. In a fresh container with an empty cache, pip install ruff finished in 0.5s, leaving 1 package and 1 MB on disk. pip-audit reported no known vulnerabilities.
What does ruff need to run?
Python >=3.7, and a platform wheel with compiled extensions. In our run import ruff succeeded in 0.04s.
ruff or flake8: which should you use?
flake8: Keep it when a required third-party plugin has no corresponding Ruff rule family. Ruff 0.16.4 installed as 1 package using 1 MB in 0.5 seconds, imported in 0.04 seconds, and produced 0 audit findings in our sandbox.
When should you not use ruff?
An essential Flake8 plugin has no Ruff rule family. Ruff implements rules internally and does not load arbitrary Flake8 plugins.
Discussed on
Use it if
- One pinned executable should cover common lint rules, import sorting, code upgrades, and formatting in local hooks and CI.
- Existing lint latency is high enough that developers avoid running checks before a push.
- A monorepo needs shared defaults with narrower configuration in individual Python projects.
- Automatic changes must distinguish generally safe fixes from behavior-changing unsafe fixes.
- An essential Flake8 plugin has no Ruff rule family. Ruff implements rules internally and does not load arbitrary Flake8 plugins.
- The missing tool is a type checker. Ruff does not replace mypy, Pyright, or ty for cross-module type analysis.
- Policy forbids pre-1.0 tooling in a required CI gate. Ruff is still 0.16.4, and minor releases can alter rules, fixes, and formatting details.
- You expect formatting to sort imports. `ruff format` leaves import ordering to the linter's `I` rules.
- The environment cannot run the supplied native build. Our PyPI install contained compiled extensions rather than a pure-Python implementation.
Setup reality
We installed Ruff 0.16.4 in a fresh Python 3.12 Bookworm container in 0.5 seconds. The environment contained 1 package using 1 MB, and Ruff declares 0 direct dependencies with a Python 3.7 minimum. The wheel ships compiled .so files and does not include py.typed. pip-audit found 0 known vulnerabilities. import ruff completed in 0.04 seconds in our sandbox, though the supported product surface is the ruff executable rather than a documented Python library.
Ruff reads pyproject.toml, ruff.toml, or .ruff.toml. In pyproject, top-level settings live under [tool.ruff], linter settings under [tool.ruff.lint], and formatter settings under [tool.ruff.format]. Config discovery walks ancestors and can extend another file. A plain ruff check enables the documented default set, not every available rule. Inventory current Flake8 plugins and options before removing them, since unsupported plugins cannot be imported into Ruff.
Linting and formatting are two commands. ruff check --fix applies fixes classified as safe; --unsafe-fixes can change runtime meaning and needs a source review. Import sorting is ruff check --select I --fix, not part of ruff format. Pin 0.16.4 in project dependencies, pre-commit, editor settings, and CI together. Preview mode opts into unfinished rules and formatting behavior, so it should not arrive accidentally through one developer's editor.
Ruff writes .ruff_cache by default and respects normal cache controls, which matters in read-only or disposable CI workspaces. Begin a migration with the existing behavior and add rule families in reviewable batches. --add-noqa edits the tree and can hide too much if used as an automatic baseline. For machine consumption, choose an output format and use exit codes; importing the ruff package is not a substitute for its CLI contract.
Patterns
Pin Ruff and run both modes pin-and-run
uv add --dev ruff==0.16.4
uv run ruff check .
uv run ruff format .Linting and formatting are separate commands. Pin the same 0.16.4 release in local, editor, hook, and CI environments.
Set target Python and lint families configure-project
[tool.ruff]
target-version = "py312"
line-length = 100
[tool.ruff.lint]
select = ["E4", "E7", "E9", "F", "I", "B", "UP"]In `pyproject.toml`, lint settings belong under `[tool.ruff.lint]`. A `ruff.toml` file omits the `tool.ruff` prefix.
Show lint changes before applying them check-without-writing
ruff check --diff .
ruff check --fix .The normal `--fix` path applies fixes Ruff classifies as safe. Review `--unsafe-fixes` separately because behavior can change.
Fail CI on formatter differences verify-formatting
ruff format --check --diff .This checks formatting only. Run `ruff check` as another CI step for lint diagnostics and import order.
Apply import sorting rules sort-imports
ruff check --select I --fix .
# pyproject.toml
[tool.ruff.lint.isort]
known-first-party = ["myapp"]`ruff format` does not reorder imports. Ruff's isort-compatible behavior lives under the linter's `I` rule family.
Scope rule exceptions by path ignore-test-files
[tool.ruff.lint.per-file-ignores]
"tests/**" = ["S101"]
"**/__init__.py" = ["F401"]Per-file ignores preserve the rule elsewhere. Keep both the glob and code list narrower than a project-wide ignore.
Suppress one named diagnostic suppress-one-line
import plugin_registry # noqa: F401
# Find suppressions that no longer hide a diagnostic
# ruff check --select RUF100 .A bare `noqa` suppresses every finding on the line. Naming `F401` leaves unrelated diagnostics active.
Add generated paths to exclusions exclude-generated-code
[tool.ruff]
extend-exclude = [
"migrations",
"generated",
"*_pb2.py",
]`extend-exclude` adds to Ruff's defaults. Setting `exclude` replaces the existing exclusion list.
Run lint fixes before formatting configure-pre-commit
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.16.4
hooks:
- id: ruff-check
args: [--fix]
- id: ruff-formatPut the lint-fix hook first because its changes may need a formatter pass. Pin the hook revision to the project version.
Pin the CI action's Ruff version configure-github-action
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v3
with:
version: "0.16.4"
args: "check --output-format=github"Pinning `version` prevents CI from adopting a new Ruff minor before local dependencies and configuration are updated.
Read the installed explanation for a rule inspect-rule
ruff rule F401
ruff rule B008`ruff rule` prints the explanation, examples, origin, and fix details that belong to the installed Ruff release.
Produce machine-readable findings emit-json-diagnostics
ruff check . --output-format json > ruff-findings.jsonJSON output is for reporting and integrations. The command's nonzero status still indicates that lint findings were present.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| flake8 | PyPI | Keep it when a required third-party plugin has no corresponding Ruff rule family. |
| pylint | PyPI | Use it when its deeper inference and project-specific diagnostics matter more than lint speed. |
| black | PyPI | Use it when formatting is the only requirement and exact Black ownership is part of the policy. |
| isort | PyPI | Keep it when an established import-sorting configuration uses behavior Ruff cannot reproduce. |
More cli & tooling guides
commander · chalk · typescript · esbuild · yargs · click · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

