Flake8 vs Ruff: The Python Linting Shift
Ruff reimplements most Flake8 checks in Rust and runs vastly faster; Flake8 keeps a long tail of plugins and familiarity.
Flake8 has been the default Python linter, gluing pyflakes, pycodestyle, and a plugin ecosystem together. Ruff covers many of those same rules at much higher speed with a single binary and one config.
| Flake8 | Ruff | |
|---|---|---|
| Language | Python | Rust |
| Speed | Slow on large repos | Very fast |
| Plugins | Large ecosystem | Many rules built in, fewer external plugins |
| Config | setup.cfg / .flake8 / tox.ini | pyproject.toml [tool.ruff] |
| Autofix | No | Yes for many rules |
Where Ruff wins
Ruff is far faster, autofixes many issues, and consolidates several tools behind one config. It maps Flake8 rule codes, so migration is often a matter of enabling the equivalent rule sets. For most codebases it covers the checks you actually use.
Where Flake8 wins
Flake8 has a large, mature plugin ecosystem. If you depend on a niche Flake8 plugin that Ruff has not yet reimplemented, Flake8 still has the broader long tail. Some teams also value its long, stable track record.
Migration
Move Flake8 config into [tool.ruff], enable the matching rule families, and compare output on your codebase before switching the CI gate. Keep a specific Flake8 plugin only if Ruff lacks an equivalent.
The verdict
For most projects Ruff is the better default now: faster, autofixing, single config. Stay on Flake8 (or run it alongside) only for plugins Ruff has not yet covered.