You are still running Black, isort, and flake8 as three separate tools. Each one has its own config file, its own update cycle, and its own 200ms startup time. There is a better setup — and it ships as a single 2MB Rust binary that runs 100x faster. Ruff 0.15 just shipped with the 2026 style guide. Here is what changed and how to migrate in under 30 minutes.
In this tutorial, you will learn to:
- Understand what changed in Ruff 0.15 and the 2026 style guide
- Compare Ruff formatter against Black in 2026 — speed, rule coverage, and compatibility
- Migrate an existing Black + isort + flake8 project to Ruff-only
- Set up Ruff in GitHub Actions CI in under 10 minutes
- Configure Ruff with uv for a zero-config start in new projects
Page Contents
What Is Ruff and Why Does It Matter in 2026?
Ruff is a Python linter and formatter written in Rust by Astral — the same team behind uv. It replaces Black, isort, flake8, and dozens of their plugins with a single tool that runs 10 to 100 times faster. Ruff 0.15, released in February 2026, is the release that makes Ruff’s formatter a credible, full-time Black replacement for teams that want one tool instead of four.
Here is the core comparison in 2026:
| Tool | What it does | Speed |
|---|---|---|
| Ruff | Linter + formatter + import sorter | 10–100x faster than Black |
| Black | Formatter only | Reference (slow in Python) |
| isort | Import sorter only | Fast but separate tool |
| flake8 | Linter only (plus plugins) | Slow, startup overhead |
Prerequisites
You should be comfortable on the command line and have a Python project with an existing pyproject.toml. No prior experience with Ruff is needed — this is a ground-up guide. If you want a refresher on setting up Python tooling, see the Python libraries guide for the modern toolchain overview.
What Changed in Ruff 0.15: The 2026 Style Guide
1. Lambda Parameters Stay on One Line
The most celebrated change in Ruff 0.15 is how it handles long lambdas. Black would break long lambda parameters across multiple lines, which was widely considered its worst formatting decision. Ruff 0.15 keeps lambda parameters on the same line and parenthesizes the body if it needs to break — matching what most developers actually wanted:
# Black (2024 style) — breaks lambda awkwardly
result = (
lambda x, y, z, w: x + y + z + w
)(1, 2, 3, 4)
# Ruff 0.15 / 2026 style — parameters stay on one line
result = (lambda x, y, z, w: (x + y + z + w))(
1, 2, 3, 4
)
2. Improved Spacing Around Magic Literals
The 2026 style guide tightens formatting around type[None], type[int], and other special type literals. Ruff now formats these with consistent spacing that was inconsistent or unavailable in older Black versions:
# Ruff 0.15 — consistent spacing for magic literals
x: type[None] # no extra space inside brackets
y: type[int | str] # union types inside brackets formatted cleanly
3. Respect for Existing Line Lengths in Multi-Line Constructs
One of the longest-running complaints with Black was that it would reformat code in ways that created large diffs even when no meaningful changes were made. Ruff 0.15 is smarter about preserving existing line breaks in function calls, comprehensions, and other multi-line constructs — reducing noise in code review when you adopt it.
4. Ruff Ships as the Formatter Behind uv format
If you use uv, the uv format command runs Ruff 0.15 with the 2026 style guide by default. This means you do not need to install Ruff separately for new projects — it comes included in uv 0.10.0 and later. For teams standardizing on uv, the formatter is already there.
# uv format uses Ruff 0.15 under the hood — no separate Ruff install needed
uv format
# Check without modifying files
uv format --check
# Format specific paths
uv format src/**/*.py
Ruff vs Black in 2026: What Is Actually Different?
Ruff is not 100% identical to Black — it is 99% compatible with intentional deviations where Ruff’s maintainers believe the Black behavior was wrong. The official docs list known deviations. Key differences in practice:
| Area | Black | Ruff 0.15 (2026) |
|---|---|---|
| Lambda parameter formatting | Breaks params across lines | Keeps params on one line |
| Magic literal spacing | Inconsistent | Standardized in 2026 style |
| Line-length changes | Often reformats entire files | Preserves existing breaks where possible |
| Import sorting | Requires isort plugin | Built in, compatible with isort config |
| Rule extensibility | None — formatter only | 800+ lint rules via plugins |
| Speed | Reference Python speed | 10–100x faster |
The lambda formatting change is the most impactful for daily use — it eliminates the most common complaint developers had with Black. If you use black --diff in CI today and see large diffs on lambda-containing files, Ruff 0.15 will produce cleaner output.
Rule of thumb: If you are starting a new project in 2026, use
uv format(Ruff) directly. If you are migrating an existing Black project, set Ruff’sformatter.blackcompatibility mode to minimize diff noise during the transition.
Migrating from Black + isort + flake8 to Ruff
Step 1: Install Ruff
# Option 1: via uv (recommended)
uv tool install ruff
# Option 2: via pip
pip install ruff
# Option 3: standalone installer
curl -LsSf https://astral.sh/ruff/install.sh | sh
Step 2: Add Ruff Configuration to pyproject.toml
Add a [tool.ruff] section alongside your existing tool configs. Ruff can read your existing Black line-length and isort settings directly:
# pyproject.toml
[tool.ruff]
# Enable the formatter (replaces Black)
line-length = 88 # inherited from your Black config
# Enable rules that match your flake8 plugins
select = ["E", "F", "W", "I", "N", "UP", "B", "C4"]
# Tell Ruff to be compatible with your existing isort config
isort.required-imports = ["from __future__ import annotations"]
[tool.ruff.format] # formatter config — uses 2026 style guide by default in 0.15+
Step 3: Replace Black, isort, and flake8 Commands
Once Ruff is configured, it covers all three tools. Here is the command mapping:
| Old command | Ruff equivalent |
|---|---|
black . | ruff format . |
black --check . | ruff format --check . |
isort . | ruff check --select I --fix . |
flake8 . | ruff check . |
flake8 --fix . | ruff check --fix . |
# Run both linting and formatting in one pass
ruff check . && ruff format .
Step 4: Remove the Old Tools
Once Ruff is working correctly in your project, remove the old dependencies:
# Remove Black, isort, flake8
uv remove black isort flake8 flake8-import-order
# or via pip:
pip uninstall black isort flake8
GitHub Actions CI Setup: Ruff in Under 10 Minutes
Here is a complete GitHub Actions workflow that runs Ruff on every push and pull request using the official astral-sh/ruff-action:
# .github/workflows/ruff.yml
name: Ruff
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
with:
python-version: "3.12"
- name: Install dependencies
run: uv sync
- name: Run Ruff linter
run: ruff check .
- name: Run Ruff formatter check
run: ruff format --check .
Adding a Pre-Commit Hook
For developer-level enforcement, add Ruff to your .pre-commit-config.yaml so unformatted code never reaches your commit history:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.0
hooks:
- id: ruff-format # replaces black
- id: ruff # replaces flake8 + isort
args: [--fix, --select I] # --fix applies auto-fixes, I = isort rules
# Install pre-commit hooks (run once per developer machine)
uv run pre-commit install
Common Mistakes / Gotchas
- You are using an older Black config in Ruff: Ruff’s formatter is not identical to Black. If you see unexpected diffs, check the known deviations from Black in the Ruff docs. Most teams accept the small differences as improvements.
- You did not remove your old
.flake8config: Ruff readspyproject.toml [tool.ruff]sections, but old.flake8orsetup.cfgfiles can cause conflicts. Consolidate everything into[tool.ruff]. - You are on Ruff 0.14: The 2026 style guide ships in Ruff 0.15. If you are pinning an older version, you will not get the new lambda formatting. Run
uv tool upgrade ruffto get the latest. - You are mixing Ruff config with Black config: Ruff ignores Black’s
[tool.black]section inpyproject.toml. Make sure all formatting settings live in[tool.ruff]and[tool.ruff.format].
Summary & Next Steps
Ruff 0.15 with the 2026 style guide is the release that makes Ruff a genuine full-time replacement for Black, isort, and flake8. One binary, one config section in pyproject.toml, and a 10–100x speed improvement over the three-tool stack it replaces. Lambda formatting alone is worth the switch. If you are starting a new project, uv format gives you Ruff 0.15 out of the box with no separate installation needed.
- Ruff v0.15.0 Release Notes — official announcement with full changelog
- Known Deviations from Black — official Ruff docs comparing formatter behavior
- Ruff Formatter Docs — configuring the 2026 style guide
- GitHub Action for Ruff — official astral-sh/ruff-action
- Ruff Documentation — full reference
- 5 Python Libraries That Changed How I Code — the modern Python toolchain context

