I switched to Ruff because it’s ridiculously fast. On a ~50k line Python codebase, Flake8 + isort + pylint took about 45 seconds. Ruff does it in under 2 seconds. It’s written in Rust and it shows.

Basic usage Link to heading

ruff check .

Fix issues automatically:

ruff check --fix .

Run specific rules only:

ruff check --select RUF001  # unicode issues
ruff check --select F403    # star imports

I wanted to see what errors I had the most of:

ruff check --output-format=concise | \
    rg -o '([A-Z]+[0-9]+)' -r '$1' | \
    sort | uniq -c | sort -nr | head -10

Formatting Link to heading

Ruff also does formatting now:

ruff format .

It’s compatible with Black’s style but faster. I’ve fully replaced Black with ruff format in all my projects.

Rules I disable Link to heading

My pyproject.toml typically ignores a few rules:

[tool.ruff.lint]
ignore = [
    "E501",   # line too long - handled by formatter
    "D100",   # missing docstring in public module
    "D104",   # missing docstring in public package
]

I find the docstring requirements too noisy for internal code. For libraries with public APIs, I’d enable them.

Has it replaced everything? Link to heading

For me, yes. My pre-commit config used to have Flake8, isort, Black, and a handful of plugins. Now it’s just Ruff. The migration was painless - Ruff implements most of the common rules from Flake8, pylint, pyupgrade, isort, and others.

The only thing I still run separately is mypy for type checking. Ruff doesn’t do that (yet).

Combine with pytest for a fast development workflow.

Further reading Link to heading