Tools

Ruff: one Python linter and formatter replacing Flake8, isort and Black

TL;DR

Ruff combines a Python linter and formatter with 900+ rules from Flake8, isort, pyupgrade and bugbear. Run ruff check --fix and ruff format, configure [tool.ruff], add the I, B and UP sets, and review diffs for unsafe fixes.

A typical Python project uses Flake8 for linting plus a few plugins, isort to sort imports, Black to format, and pyupgrade to modernise syntax. Four tools, four config files, and on a large codebase a run can take a minute. Ruff from Astral, written in Rust, is both linter and formatter in one tool, supports over 900 rules drawn from those familiar tools, and according to its README is 10 to 100 times faster than existing linters.

GitHub page of astral-sh/ruff
GitHub page of astral-sh/ruff (screenshot, November 2026)

GitHub: https://github.com/astral-sh/ruff · about 50k stars · Rust · MIT

Installation

uv tool install ruff       # or: pipx install ruff / pip install ruff
uvx ruff check             # try it without installing
curl -LsSf https://astral.sh/ruff/install.sh | sh

The two main commands

ruff check .               # lint the whole project
ruff check --fix .         # automatically apply safe fixes
ruff format .              # format code (Black-compatible style)
ruff format --check .      # check only, for CI
ruff rule F401             # explain a rule

Configuration in pyproject.toml

[tool.ruff]
line-length = 100
target-version = "py312"

[tool.ruff.lint]
select = [
  "E", "F",   # pycodestyle, Pyflakes (the defaults)
  "I",        # isort: import sorting
  "B",        # flake8-bugbear: likely bugs
  "UP",       # pyupgrade: newer Python syntax
  "SIM",      # flake8-simplify
]
ignore = ["E501"]

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["B011"]

[tool.ruff.format]
quote-style = "double"

A standalone ruff.toml works too. By default Ruff only enables the basic E and F groups; you opt into more rule sets as needed.

Rule groups worth knowing

CodeOriginCatches
FPyflakesUnused variables/imports, undefined names
IisortImport order
Bflake8-bugbearMutable default arguments, bare excepts…
UPpyupgradeOld syntax that can be modernised
Sflake8-banditSecurity issues like eval, hard-coded passwords

Suppressions and unsafe fixes

  • Skip one line: add # noqa: F401 at the end.
  • Some fixes may change behaviour and are marked "unsafe"; apply them only deliberately with ruff check --fix --unsafe-fixes, and review the diff.
  • Migrating a large codebase: enable rule groups gradually and use --add-noqa to mark existing violations, then fix them over time.

Integrations

  • Editors: the Ruff extension for VS Code, and language-server support for other editors: lint and format on save.
  • pre-commit: the astral-sh/ruff-pre-commit repo with ruff-check and ruff-format hooks.
  • CI: ruff check --output-format=github . so errors appear inline on the PR.

How to apply it

  • Dev: Enable format-on-save in your editor; no more formatting debates in review.
  • Tech Lead: Replace Flake8 + isort + Black with Ruff, consolidate config in pyproject.toml, and enable B and UP to catch real bugs, not just style.
  • PM: One tool instead of four, faster CI, less configuration to maintain.

Sources & references

  1. astral-sh/ruff — astral-sh (GitHub)
  2. Ruff documentation — astral-sh