Ruff vs Black vs Flake8: Why Ruff Replaces All Three Tools
My Python CI/CD pipeline was taking 12 seconds just for linting and formatting. I was running Flake8, Black, isort, pyupgrade, and pydocstyle separately - each tool adding overhead and configuration complexity. Then I discovered Ruff, and everything changed.
The Problem
I had a multi-tool setup that looked like this:
$ flake8 src/$ black src/$ isort src/$ pyupgrade --py38-plus src/Each tool required:
- Separate installation and dependency management
- Individual configuration files
- Sequential execution (no parallelization possible)
- Different rule sets that sometimes conflicted
The real pain point? Running all these tools on the CPython codebase took over 12 seconds. For a team running CI on every push, that adds up.
What I Found: Ruff Replaces Everything
Ruff is a single Python linter and formatter written in Rust. It handles:
| Tool | What It Does | Ruff Equivalent |
|---|---|---|
| Flake8 | Linting (pyflakes + pycodestyle) | ruff check |
| Black | Code formatting | ruff format |
| isort | Import sorting | Built-in (rule I) |
| pyupgrade | Modernize syntax | Built-in (rule UP) |
| pydocstyle | Docstring checking | Built-in (rule D) |
| autoflake | Remove unused imports | Built-in |
Instead of five tools, I now have one:
$ ruff check src/$ ruff format src/Why Ruff Is Faster
Ruff is written in Rust, not Python. This gives it native performance that Python-based tools cannot match.
Key performance factors:
- Native code execution - No Python interpreter overhead
- Built-in caching - Avoids re-analyzing unchanged files
- Parallel processing - Analyzes multiple files simultaneously
- Single-pass architecture - No sequential tool chaining
Here’s what I measured on the CPython codebase:
Linting the CPython codebase from scratch:
Tool Time─────────────────────────Flake8 ~12 secondsRuff ~0.3 seconds
Speedup: 40x fasterIn real-world projects, Ruff claims 10-100x speed improvement depending on codebase size. I’ve seen similar results in my own work.
Tool Comparison at a Glance
| Feature | Black + Flake8 + isort | Ruff |
|---|---|---|
| Language | Python | Rust |
| Speed | ~12s on large codebase | ~0.3s (40x faster) |
| Tools needed | 3-6 separate tools | 1 tool |
| Config files | Multiple (pyproject.toml, .flake8, etc.) | Single pyproject.toml |
| Import sorting | Requires isort | Built-in |
| Format style | Black-compatible | Black-compatible (drop-in) |
| Rules | ~200 (Flake8 core) | ~800+ (includes plugin equivalents) |
Migration Was Easier Than Expected
I worried that migrating would break existing code. But Ruff has drop-in parity with Black’s formatting style and Flake8’s rule set.
Here’s my migration configuration:
[tool.ruff.lint]extend-select = [ "E", # pycodestyle errors "F", # pyflakes "I", # isort "UP", # pyupgrade "B", # flake8-bugbear]
[tool.ruff.format]# Black-compatible formatting (default)That’s it. One file, one configuration block.
The Evolution: Multi-Tool to Single-Tool
Before (Traditional Setup)┌─────────────────────────────────────┐│ CI/CD Pipeline │├─────────────────────────────────────┤│ flake8 ──► black ──► isort ──► ... ││ │ │ │ ││ ▼ ▼ ▼ ││ 12s total (sequential) │└─────────────────────────────────────┘
After (Ruff Setup)┌─────────────────────────────────────┐│ CI/CD Pipeline │├─────────────────────────────────────┤│ ruff check ──► ruff format ││ │ │ ││ ▼ ▼ ││ 0.3s total (parallel) │└─────────────────────────────────────┘Common Migration Mistakes
I made these mistakes initially. Here’s what to avoid:
1. Running Ruff alongside Black
This is redundant. Ruff’s formatter is Black-compatible. Running both creates unnecessary overhead and potential conflicts.
2. Not disabling Flake8 plugins that Ruff handles
If you use flake8-bugbear, flake8-comprehensions, or similar plugins, Ruff has native equivalents. Check the rule mapping and disable the old plugins.
3. Keeping separate isort configuration
Ruff handles import sorting with rule I. Remove isort from your dependencies and merge its configuration into Ruff’s lint.isort settings.
4. Forgetting to remove old tool dependencies
After migrating, clean up:
pip uninstall flake8 black isort pyupgrade pydocstyle autoflakeWho’s Using Ruff
Major projects have already migrated:
- Apache Airflow
- Apache Superset
- FastAPI
- Hugging Face
- Pandas
- SciPy
These aren’t small projects. They have complex codebases with custom linting requirements. Ruff handles them all.
Getting Started
If you’re on a Python project with existing tooling, migration takes minutes:
- Install Ruff:
pip install ruff - Run check:
ruff check src/ --fix - Run format:
ruff format src/ - Remove old tools from dependencies
- Update CI configuration
For new projects, just start with Ruff. No need to set up the traditional multi-tool stack.
What About Rules?
Ruff has 800+ built-in rules, covering:
| Category | Rules | Source |
|---|---|---|
| pycodestyle (E, W) | Style errors/warnings | Flake8 core |
| Pyflakes (F) | Logical errors | Flake8 core |
| isort (I) | Import sorting | isort |
| pyupgrade (UP) | Syntax modernization | pyupgrade |
| flake8-bugbear (B) | Bug detection | Popular plugin |
| flake8-comprehensions (C4) | List/dict comprehension | Popular plugin |
| pydocstyle (D) | Docstring style | pydocstyle |
You can enable rules incrementally. I started with the basics (E, F, I) and added more over time.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Ruff replaces Black, Flake8, isort, pyupgrade, pydocstyle, and autoflake with a single tool that runs 10-100x faster. Migration is straightforward with drop-in Black formatting compatibility and Flake8-equivalent rules. The result: simpler configuration, faster CI/CD, and unified tooling.
Comments