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
- Ruff rule reference - all available rules
- Configuring Ruff - pyproject.toml options