3 Python Linters Compared by Speed and Depth

Compare three Python linters: Ruff, Flake8, and Pylint. Speed, rule coverage, Flake8 drop-in vs Pylint residual, VS Code setup, and when to keep each one.

Updated 9 min read
Python code on a laptop screen

If you pick one Python linter in 2026, pick Ruff: a Rust binary that lints and formats in milliseconds. Flake8 is the style stack you migrate off when plugins are few; Pylint still wins on inference, duplicate code, and import cycles. Astral's FAQ says Ruff is not a pure replacement for Pylint.

A linter analyzes how code runs; a formatter only rewrites how it looks. Type checkers (mypy vs pyright) catch "int where str expected."

Browser paste toys exist; this roundup covers three CLI tools. Install and config for Ruff live in the Ruff guide.

Key Takeaways

  1. Ruff is the default for new Python work: 900+ built-in rules, autofix, and a Black-compatible formatter. Pair it with a type checker.
  2. Flake8 to Ruff is a drop-in when you have few plugins, already run Black, and stay on Python 3. Custom plugins stay on a thin Flake8.
  3. Pylint still has a job: default Ruff caught 6 of 14 semantic categories in an August 2026 fixture; Pylint caught 14 of 14.

Top 3 Picks for Python Linters

Ruff is the 2026 default. Flake8 is the stack you migrate from. Pylint covers residual semantic depth.

  1. Ruff - Best for greenfield repos and CI speed
  2. Flake8 - Best for plugin-era style stacks you are leaving
  3. Pylint - Best for inference, duplicate code, and custom checkers

Evaluation Criteria

  • Speed. Wall-clock on a real tree beats a vendor slogan. Use Astral's 10-100x claim plus one named 2026 fixture, not a single "262x" number.
  • Rule coverage. Count overlap, not headline rule totals. Ruff's 900+ does not mean it replaces Pylint's inference.
  • Migration path. Flake8 drop-in has three FAQ conditions. Pylint is partial. Custom plugins stay behind.
  • Autofix and editor loop. You want on-save fixes in the IDE, autofix in pre-commit, and --check only in CI.

Comparison Table

Software

Best For

Speed

What It Catches

Drop-in Path

License

Ruff

Greenfield + CI

10-100x vs Flake8

900+ rules, autofix

Default target

MIT

Flake8

Plugin stacks

Baseline

Style + plugins

Migrate to Ruff

MIT

Pylint

Inference, dupes

Slowest of three

~409 rules, depth

Keep alongside

GPL-2.0+

Ruff vs Flake8 vs Pylint at a glance

1. Ruff

Ruff documentation homepage

Best for greenfield Python and CI that has to finish in seconds

Ruff is a Rust linter and formatter. It ships 900+ rules that stand in for Flake8 plus popular plugins, Black, isort, pydocstyle, pyupgrade, and autoflake.

Config lives in pyproject.toml or ruff.toml. The first-party VS Code extension is charliermarsh.ruff. Daily install and command-loop steps live in the Ruff guide.

OpenAI announced it would acquire Astral. Until a close is published, treat Ruff as the same MIT CLI.

Pros

  1. Millisecond runs on large trees, with a documented 10-100x range versus Flake8 and Black
  2. One binary replaces a five-tool pre-commit stack, including ruff format
  3. Autofix plus a first-party editor extension, so you rarely type ruff check by hand

Cons

  1. No plugin API. Charlie Marsh said at PyCon US that Ruff is "not like extensible in any way," and that this is a long-standing feature request
  2. Not a pure Pylint replacement (inference, duplicate code, import cycles)
  3. Default select is curated and small. "Ruff found nothing" means you have not enabled the codes you care about

Pricing

  • License: MIT, free on PyPI
  • No paid SKU. Current line is Ruff 0.16.x.

2. Flake8

Flake8 documentation homepage

Best for existing style stacks you plan to migrate

Flake8 calls itself "Your Tool For Style Guide Enforcement." It wraps PyFlakes, pycodestyle, and McCabe, then loads third-party plugins. That plugin surface is the product.

Maintainer Ian Stapleton Cordasco still ships the wrapper (PyPI 7.3.0). Config lives in .flake8, setup.cfg, or tox.ini. Flake8 does not read pyproject.toml natively.

Teams still run Flake8 when an in-house plugin has nowhere else to go: Ruff will not load it.

Pros

  1. The plugin ecosystem covers style, complexity, and shop-specific codes that Ruff cannot load
  2. Familiar E / F / W codes. # noqa: F401 survives a Ruff move
  3. A thin leftover Flake8 is a sane end state when one custom plugin remains

Cons

  1. Each plugin re-tokenizes. A named 2026 fixture put Ruff at about 53x (1,677 files, F rules)
  2. No native pyproject.toml, so you keep a second config file after the rest of the stack moved
  3. Quiet maintenance relative to Ruff. You migrate off Flake8; you do not start there in 2026

Pricing

  • License: MIT, free on PyPI
  • Current line is Flake8 7.3.

3. Pylint

Pylint documentation homepage

Best for semantic depth you still cannot get from Ruff

Pylint is a static analyser: errors, coding standard, smells, duplicated code, Pyreverse UML, and Python plugins. Maintainers Pierre Sassoulas and Claudiu Popa still ship 4.x. Config is .pylintrc or pyproject.toml.

Pylint does more type inference than Ruff. Issue #970 still lists cyclic-import and duplicate-code as unchecked (updated 28 July 2026). Pylint's own README now tells you to use Ruff alongside.

On r/learnpython, Pylint threads are mostly VS Code source-roots failures. The other recurring verdict is skip it.

u/eleqtriq (October 2025): "I wouldn't even use pylint. Use ruff or flake8. Ruff is orders of magnitude faster."

Pros

  1. Inference that Ruff still misses: missing members, wrong argument counts, file opened without encoding
  2. Cross-file checks Ruff does not ship: duplicate-code and import cycles
  3. Custom Python checkers and Pyreverse, which a Rust binary with no plugin API cannot host

Cons

  1. Slowest of the three. An 8-file pyobfuscate fixture was 23.3 ms for Ruff vs 1125.5 ms for Pylint (~48x)
  2. License is GPL-2.0-or-later. That is a real constraint for some shops; Ruff and Flake8 are MIT
  3. Microsoft's first-party Pylint extension still exists, and beginners hit source-roots / "No files to lint" before they hit a useful diagnostic

Pricing

Speed: What the Numbers Actually Say

Astral documents Ruff as 10-100x faster than existing linters (Flake8) and formatters (Black). Charlie Marsh at PyCon also said "10, 100, even like a thousand times" on large codebases. Treat 1000x as talk-stage and keep 10-100x in the table.

One named 2026 measurement sits beside that range. pyobfuscate ran 1,677 files, F rules, cache off, Ruff 0.16.1 vs Flake8 7.3.0: 0.28 s vs 14.72 s (~53x). That source also sells a WASM linter, so treat it as an independent fixture, not a vendor-neutral lab.

Dagster's Nick Schrock is quoted in the Ruff docs: 250k LOC, Pylint 2.5 min to Ruff 0.4 s.

Rule Coverage: More Rules Do Not Replace Pylint

The FAQ numbers: Pylint ~409 rules, Ruff over 900, of which at least 209 overlap. Both totals move. 900+ is the stable figure.

pyobfuscate's 14-category fixture (Ruff 0.16.3 vs Pylint 4.0.7, cache off) is the only disclosed head-to-head on what each actually flags:

  • Ruff default: 6/14
  • Ruff expanded (E,F,W,B,BLE,I,PL,RET,SIM,UP,S307): 9/14
  • Pylint focused: 14/14

Pylint-only in that fixture: missing member, missing argument, duplicate code, import cycle, file opened without encoding. Those gaps match #970.

Ruff has what Pylint lacks as a default stack: the Flake8-plugin universe (bugbear B, pyupgrade UP, isort I), autofix, and a formatter. Default Ruff is curated; enable more with --select. select = ["ALL"] on day one is how you get the rules that make code worse.

On r/Python, the argument is which rules to disable.

u/akl773 (August 2026) on FastAPI: B008 bans a function call in a default argument, which is correct in general. Every FastAPI codebase uses Depends() in that position. A blanket ignore then hides the real mutable defaults.

How to Migrate from Flake8 or Pylint

Two paths. Do not flatten them.

Flake8 to Ruff is the default. Ruff can be used as a drop-in replacement for Flake8 when used (1) without or with a small number of plugins, (2) alongside Black, and (3) on Python 3 code. Under those conditions, Ruff implements every rule in Flake8.

Mechanical map: pycodestyle to E,W; pyflakes to F; isort to I; pyupgrade to UP (set target-version or it defaults py310); bugbear to B; Black to ruff format. The old flake8-to-ruff converter is no longer supported (it warns that it may break after Ruff v0.0.233). Map prefixes by hand.

Line-length trap: Flake8's pycodestyle default is 79. Ruff and Black default to 88. Flip line-length before you read the first CI log as a regression.

In-house plugin: keep a minimal Flake8. List leftover plugin codes in lint.external so RUF100 does not strip their # noqa.

Pylint to Ruff is partial. Ruff is not a "pure" drop-in (and vice versa), and enabling Ruff's PL group can add noise from Pylint-extension rules that were not on by default. Move the fast/style layer to Ruff; keep Pylint or a type checker for inference, duplicate code, import cycles, and Python plugins.

Sebastián Ramírez (@tiangolo) collapsed FastAPI's stack onto Ruff in October 2023:

So, @FastAPI now uses the Ruff formatter. 😎✨ Ruff alone is now replacing (for me): * flake8 * autoflake * isort * pyupgrade * black ...and Ruff is still crazy fast. 🚀 I keep intentionally adding broken code just to ensure it is indeed running. 🤪
Sebastián Ramírez · @tiangoloView on X

Pytest did the same in February 2024 (Charlie Marsh): autoflake, Black, isort, pyupgrade, Flake8, and pydocstyle in one tool.

Linter vs Formatter vs Type Checker

Microsoft's linting docs draw the line: linting analyzes how the code runs and detects errors; formatting only restructures how code appears.

Black is still the style default (PSF, PyPI 26.5.x). ruff format is the Black-compatible speed play. Astral's October 2023 claim: over 30x Black and over 99.9% of lines identical on Django and Zulip. The Ruff vs Black page owns that comparison.

Ruff does not type-check. Corey Schafer: Ruff has rules that enforce writing type hints, which is a different job from catching "int passed where str expected." Pair Ruff with mypy, pyright, or basedpyright; those tools are not entries here.

Python Linter Setup in VS Code

The Ruff extension leads the Pylint extension in Marketplace installs. Both are in the millions. Microsoft's Python linting page still lists first-party Pylint, flake8, mypy and community Ruff.

Skip the old "enable Pylint" checkbox. Microsoft moved linting out of the Python extension. Pylance is a language server and does not lint.

Text
"[python]": {
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "charliermarsh.ruff",
  "editor.codeActionsOnSave": {
    "source.fixAll": "explicit",
    "source.organizeImports": "explicit"
  }
}

On r/learnpython, the production loop is IDE on save, pre-commit autofix, CI --check only.

u/zanfar (February 2026): "Given that any decent IDE will integrate it, I would assume it's very rare that any developer ever runs it manually."

How to Choose the Right Python Linter

  • New repo, Python 3, no custom checkers: Ruff (ruff check + ruff format). Add a type checker beside it.
  • Existing Flake8 + Black + isort, few plugins: drop-in Ruff under the three FAQ conditions. Keep a thin Flake8 only for an in-house plugin.
  • Existing Pylint, and you rely on no-member / duplicate-code / import cycles: move style and speed to Ruff; keep Pylint on a slower job, or replace that depth with a type checker.
  • FastAPI: keep Ruff, carve out B008 / Depends(). Start select = ["E", "F", "I"], not ALL.

OpenAI announced the Astral acquisition on 19 March 2026; Astral posted the same day. Terms were not disclosed, and no first-party close date has been published.

Named projects keep collapsing five pre-commit tools into one binary. Charlie Marsh noted Python 3.14 support for uv and Ruff on stable-release day (October 2025). The daily Python experience is uv plus Ruff; the leftover fight is which Ruff rules to turn off.

Tags

Frequently Asked Questions

Related Articles