Skip to content

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:

Before Ruff - Multiple Tools
$ 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:

ToolWhat It DoesRuff Equivalent
Flake8Linting (pyflakes + pycodestyle)ruff check
BlackCode formattingruff format
isortImport sortingBuilt-in (rule I)
pyupgradeModernize syntaxBuilt-in (rule UP)
pydocstyleDocstring checkingBuilt-in (rule D)
autoflakeRemove unused importsBuilt-in

Instead of five tools, I now have one:

After Ruff - Single Tool
$ 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:

  1. Native code execution - No Python interpreter overhead
  2. Built-in caching - Avoids re-analyzing unchanged files
  3. Parallel processing - Analyzes multiple files simultaneously
  4. Single-pass architecture - No sequential tool chaining

Here’s what I measured on the CPython codebase:

Performance Comparison
Linting the CPython codebase from scratch:
Tool Time
─────────────────────────
Flake8 ~12 seconds
Ruff ~0.3 seconds
Speedup: 40x faster

In 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

FeatureBlack + Flake8 + isortRuff
LanguagePythonRust
Speed~12s on large codebase~0.3s (40x faster)
Tools needed3-6 separate tools1 tool
Config filesMultiple (pyproject.toml, .flake8, etc.)Single pyproject.toml
Import sortingRequires isortBuilt-in
Format styleBlack-compatibleBlack-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:

pyproject.toml
[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

Tool Stack Evolution
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:

Cleanup After Migration
pip uninstall flake8 black isort pyupgrade pydocstyle autoflake

Who’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:

  1. Install Ruff: pip install ruff
  2. Run check: ruff check src/ --fix
  3. Run format: ruff format src/
  4. Remove old tools from dependencies
  5. 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:

CategoryRulesSource
pycodestyle (E, W)Style errors/warningsFlake8 core
Pyflakes (F)Logical errorsFlake8 core
isort (I)Import sortingisort
pyupgrade (UP)Syntax modernizationpyupgrade
flake8-bugbear (B)Bug detectionPopular plugin
flake8-comprehensions (C4)List/dict comprehensionPopular plugin
pydocstyle (D)Docstring stylepydocstyle

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