Making sense of Python's formatting/linting tools: isort, Black, Flake8, Pylance, Ruff
Python projects tend to accumulate a pile of tools called “formatters”, “linters” and “type checkers”, and the names alone don’t tell you what each one actually does. This article covers the five most common ones — isort, Black, Flake8, Pylance and Ruff — in this order: (1) which category each tool belongs to, (2) what problem each category solves, and (3) how to actually combine them in practice.
1. Start with three categories
Python code-quality tools fall into three broad categories.
| Category | What it solves | Rewrites your code? |
|---|---|---|
| Formatter | Normalizes how code looks (line breaks, indentation, quote style, import order, …) according to a fixed rule set | Yes |
| Linter (static analysis) | Flags patterns that tend to hide bugs (unused imports, undefined names, unsafe comparisons, …) | No |
| Type checker | Uses type hints to catch type mismatches before the code ever runs | No |
Mapping the five tools onto these three categories gives:
| Tool | Category |
|---|---|
| isort | Formatter (import order specialist) |
| Black | Formatter (whole-file code style) |
| Flake8 | Linter |
| Pylance | Leans type checker (editor language server) |
| Ruff | Reimplements most of the formatter and linter categories in one tool |
In other words: isort and Black are formatters, Flake8 is a linter, Pylance leans toward being a type checker, and Ruff isn’t tied to a single category — it replaces large parts of several categories at once.
2. What each tool actually does
Formatters
isort — A formatter with exactly one job: sorting import statements. It groups imports into standard-library / third-party / local-project buckets and alphabetizes each group. It’s traditionally paired with Black under a simple division of labor: “Black formats the code body, isort formats the imports.”
Black — Famous for being “opinionated”: it has almost no configuration knobs and produces one deterministic formatting result for line wrapping, quote style (double quotes by default), trailing commas, and so on. It exists specifically to end team debates (tabs vs. spaces, single vs. double quotes) by removing the choice. It only formats — it never flags bugs.
Linters
Flake8 — The classic linter for PEP8 (style guide) violations, unused imports/variables, overly complex functions, and similar issues. Flake8 itself is a thin wrapper around pyflakes (bug detection) + pycodestyle (PEP8 checks) + mccabe (complexity metrics). It never rewrites your code — there’s no auto-fix.
Type checkers
Pylance — VS Code’s Python language server, built on Microsoft’s Pyright type checker. It provides real-time autocomplete, go-to-definition, type-mismatch detection, and hover type info directly in the editor. It’s less a CI tool and more infrastructure for a good developer experience while writing code.
The cross-category consolidator
Ruff — A Rust-based static analysis and formatting tool that reimplements Flake8 itself, dozens of Flake8 plugins, isort, and (partially) pyupgrade’s rule sets in a single binary. ruff check covers linting (Flake8-equivalent), ruff format produces Black-compatible formatting, and --fix auto-corrects most findings. Its defining trait is collapsing the formatter and linter categories into one extremely fast tool (it doesn’t do type checking).
3. Comparison table
| Tool | Type | Main job | Auto-fix | Speed | Type checking |
|---|---|---|---|---|---|
| isort | Formatter | Sorts imports | Yes | Fast | No |
| Black | Formatter | Formats the whole file | Yes | Normal | No |
| Flake8 | Linter | Flags PEP8/bug-prone code | No | Normal | No |
| Pylance | Language server / type checker | Editor completion & type checking | Partial (Quick Fix) | Fast (runs in background) | Yes (Pyright) |
| Ruff | Linter + formatter | Fast replacement for Flake8 + isort | Yes | Very fast | No |
4. How to combine them
- New project, want the leanest setup → Ruff alone (
ruff format+ruff check --fix) covers formatting, import sorting and most lint rules. CI and editor-on-save use the exact same rules. - Existing project already on Black + isort + Flake8 → Don’t force a rip-and-replace; migrate gradually onto Ruff’s Flake8/isort-compatible rules, since a few rules do behave slightly differently.
- Want stronger type safety → Neither Ruff nor Flake8 does type checking, so add Pylance (editor) and
pyright/mypy(CI) separately. - Editor comfort is the goal → Unlike the other, CLI-only tools, Pylance runs continuously in the editor providing completion, jump-to-definition and hover info — a different purpose from CI linting. Wire Ruff or mypy into CI separately.
Summary
As of 2026, the simple, fast standard setup is: “Ruff for formatting and most linting” plus “Pylance in the editor + mypy/pyright in CI for type checking.”