Replacing black, flake8 and isort with ruff: A Python Developer’s Guide
Getting rid of tooling complexity that distracts from actual development
Introduction
As a Python developer, you’re likely familiar with tools like Black for formatting, isort for import sorting, Flake8 for linting, and Mypy for type checking. While these tools are excellent individually, managing multiple tools can become cumbersome.
What if a single tool could replace most of them? Enter Ruff (made by Astral | Docs | GitHub), a fast, all-in-one Python linter and formatter that’s designed to simplify your development workflow while maintaining high standards for code quality.
This article will:
Explain the core capabilities needed in a modern Python project: formatting, import sorting, linting, type checking.
Compare tools like Black, isort, Flake8, Mypy, and Ruff using a matrix and detailed explanations.
Show you how to set up a new project using Ruff and configure it in VS Code for a clean, minimal toolchain.
Table of Contents
Core Capabilities in Python Projects
Tool Comparison: Black, Flake8, isort, Mypy, and Ruff
Matrix of Capabilities
When to Use Which Setup
Setting Up a New Project with Ruff
Creating a Virtual Environment and Installing Ruff
Pre-Commit Hook Setup
Configuring Ruff in VS Code
Core Capabilities in Python Projects
1. Formatting
What it does:
Formatting automatically changes your code to match a consistent style guide (e.g., PEP 8), leaving little room for subjective style debates. It focuses purely on the style of your code, not its functionality.Examples of what formatting changes:
Indentation and whitespace.
Line breaks (e.g., keeping lines <=88 characters).
Proper use of quotes (e.g., converting
"
to'
or vice versa).Removing trailing commas or adding them in multi-line collections.
Tools: Black, autopep8, or Ruff (formatting mode).
Note: Sorting is a special case of formatting. Import sorting organizes imports into logical groups (e.g., standard library, third-party, local modules) and ensures they follow a consistent order. isort is the go-to tool for automating this process.
2. Linting
What it does:
Linting identifies programming errors, potential bugs, stylistic issues, and bad practices. It doesn’t change your code but points out issues.Examples of what linting catches:
Unused imports/variables.
Code that violates style guides (e.g., PEP 8 in Python).
Syntax errors.
Logic errors (e.g., unreachable code).
Non-compliance with best practices (e.g., naming conventions).
Tools: Ruff, Flake8, Pylint.
3. Type Checking
What it does:
Type checking ensures your code adheres to its type annotations. It checks that variables, functions, and return values are used consistently with the types you specified. The main benefit is reducing runtime errors.Examples of what type checking catches:
Calling a function with the wrong types (see below)
Missing return values in a function annotated with a return type.
Using variables in ways that violate their type annotations.
def add(x: int, y: int) -> int:
return x + y
add(1, "2") # Type checker flags this as an error
Tools: Mypy, Pyright, Pyre
Note that ruff is not a type checker. This is explicitly mentioned in their FAQs (more on that below). The official recommendation is to run a type checker in addition to ruff.
Tool Comparison: Black, Flake8, isort, Mypy, and Ruff
Ruff aims to unify many of the capabilities offered by these tools. Here’s a side-by-side comparison:
Ruff’s FAQs also have direct comparisons between these tools. Here are some highlights that shed some more light on ruff’s capabilities:
flake8:
“Ruff can be used to replace Flake8“
black, isort:
“Ruff can also replace Black, isort, yesqa, eradicate, and most of the rules implemented in pyupgrade.”
“The Ruff formatter is designed to be a drop-in replacement for Black.“
“The Ruff linter is compatible with Black out-of-the-box, as long as the
line-length
setting is consistent between the two.““Ruff's import sorting is intended to be near-equivalent to isort's when using isort's
profile = "black"
.”
mypy
“Ruff is a linter, not a type checker. […] A type checker will catch certain errors that Ruff would miss. […] It's recommended that you use Ruff in conjunction with a type checker, like Mypy […]”
Key Takeaways from the Comparison
Ruff can be used to replace black, isort and flake8, but not mypy.
This is because ruff is a linter and a formatter but not a type checker.
Which Setup to use?
Ruff’s own stance is that you should run ruff in combination with a type checker such as mypy, i.e. ruff+mypy. This is the standard setup recommendation.
Alternatively you can run ruff in combination with any of the other tools above in case ruff isn’t customizable enough for your needs. Personally I would recommend not to do so unless absolutely needed. Keep things simple.
Setting Up a New Project with Ruff
Here’s how you can start a clean Python project using Ruff and configure VS Code for an efficient workflow.
1. Install Ruff in a virtual environment
Install Ruff:
# Using uv
uv init
uv add --dev ruff
# Alternatively using venv and pip
python3 -m venv .venv
source .venv/bin/activate
pip install ruff
Optionally, add a pyproject.toml
file to configure Ruff:
See also, for more options:
[tool.ruff]
# Same as Black.
line-length = 88
indent-width = 4
target-version = "py312" # Adjust to your Python version
[tool.ruff.lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
select = ["E4", "E7", "E9", "F"]
ignore = []
# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []
[tool.ruff.format]
# Like Black, use double quotes for strings.
quote-style = "double"
# Like Black, indent with spaces, rather than tabs.
indent-style = "space"
# Like Black, respect magic trailing commas.
skip-magic-trailing-comma = false
# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"
# Enable auto-formatting of code examples in docstrings. Markdown,
# reStructuredText code/literal blocks and doctests are all supported.
#
# This is currently disabled by default, but it is planned for this
# to be opt-out in the future.
docstring-code-format = false
# Set the line length limit used when formatting code snippets in
# docstrings.
#
# This only has an effect when the `docstring-code-format` setting is
# enabled.
docstring-code-line-length = "dynamic"
[tool.ruff.lint.pydocstyle]
convention = "google" # Accepts: "google", "numpy", or "pep257".
2. Pre-Commit Hook Setup
Integrating Ruff into a pre-commit workflow further streamlines development by eliminating the need for having a separate hook for each tool.
Install pre-commit
:
pip install pre-commit
Add a .pre-commit-config.yaml
file to your project root:
Examples are supplied directly on https://github.com/astral-sh/ruff-pre-commit
See also args you could add: https://docs.astral.sh/ruff/configuration/#full-command-line-interface
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.8.3
hooks:
# Run the linter.
- id: ruff
args:
- "--fix"
- "--show-fixes"
# Run the formatter.
- id: ruff-format
# Add mypy for static type checking
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.13.0
hooks:
- id: mypy
additional_dependencies: []
Install the pre-commit hooks:
pre-commit install
Test the hooks by running:
pre-commit run --all-files
With Ruff in your pre-commit workflow, all linting, formatting, and import sorting issues are addressed in one single step.
3. Configure Ruff in VS Code
There also is an official VS Code extension for ruff
.
Once installed in Visual Studio Code, ruff
will automatically execute when you open or edit a Python or Jupyter Notebook file.
More on this can he found here: https://github.com/astral-sh/ruff-vscode
Note that installing the extension is not needed for the pre-commit setup above.
Conclusion
By adopting ruff
, you can streamline your Python development workflow and replace multiple tools with a single, fast solution. While specialized tools like black and mypy still have their place, <ruff’s speed and versatility make it an excellent choice for most modern Python projects.