Ruff Rule Codes Explained: What E, W, F, I, UP, S, B, and C90 Mean
Run ruff check on a codebase and it answers in codes: F401, E711, B006. The letters are not severity levels, and they are not arbitrary. Each prefix names the linter the rule originally came from.
Ruff re-implements 968 rules (as of version 0.15), most drawn from older linters in the flake8 ecosystem, and it kept the established codes wherever it could. Learn a handful of prefixes and the wall of codes turns into a map of the tools Ruff replaced.
How to read a rule code
A rule code is a prefix plus a number. The prefix identifies the source linter; the number identifies the rule within it:
F401
│└─ rule 401 in that linter's catalog
└── F = PyflakesPrefixes run from one to five characters (F, UP, C90, ASYNC), and configuration selects rules by prefix. In pyproject.toml, select = ["E"] enables every pycodestyle error, ["E7"] enables only the statement-level checks like E711, and ["E711"] enables that one rule:
[tool.ruff.lint]
extend-select = [
"I", # all isort rules
"B", # all flake8-bugbear rules
"S1", # only the S1xx flake8-bandit rules
]Match each prefix to its source tool
The prefixes from this page’s title cover the categories new Ruff users meet first:
| Prefix | Source tool | What it catches |
|---|---|---|
E, W |
pycodestyle | PEP 8 style: indentation, whitespace, comparisons (E errors, W warnings) |
F |
Pyflakes | Logical errors: unused imports, undefined names, duplicate arguments |
I |
isort | Import sorting and grouping |
UP |
pyupgrade | Syntax made obsolete by your target Python version |
S |
flake8-bandit | Security problems: hardcoded passwords, pickle, weak hashes |
B |
flake8-bugbear | Likely bugs: mutable default arguments, useless expressions |
C90 |
mccabe | Cyclomatic complexity over a threshold (one rule, C901) |
These eight prefixes are a fraction of the catalog. Ruff 0.15 organizes its rules into 59 groups, including SIM (flake8-simplify) and PL (Pylint). Run ruff linter to print the full prefix-to-tool list for your installed version.
Why does Ruff use other tools’ codes?
The prefixes predate Ruff. They are inherited from the flake8 plugin ecosystem, where each tool claimed its own letter namespace:
- pycodestyle defined the
EandWcodes. It was originally released aspep8and renamed in 2016 at Guido van Rossum’s request, to stop people from conflating the tool with the PEP 8 document itself (pycodestyle issue #466). - Pyflakes never had codes of its own. flake8 assigned the
Fprefix to Pyflakes messages when it wrapped the tool (flake8 error codes). - Plugins picked letters on a first-come basis. flake8-bugbear took
B, so when flake8-bandit ported bandit’s checks (whose native IDs also start with B), it shifted them toSfor security. - The letter
Cis shared by two plugins: mccabe usesC9xxand flake8-comprehensions usesC4xx. Ruff lists them under separate selectors,C90andC4, so each can be enabled on its own (a bareCselects both).
Ruff kept these codes for the major categories, so a flake8 user’s select and ignore lists and # noqa: F401 comments mostly carry over. A few plugins were re-prefixed along the way: flake8-commas’ C812 became COM812, and Pylint’s codes gained a PL prefix. Where a source tool never had codes (isort and pyupgrade rewrite files rather than report numbered violations), Ruff coined the prefixes itself: I holds two isort rules, UP holds 48 pyupgrade rules. Rules original to Ruff live under RUF.
How do you look up a rule?
Pass any code to ruff rule and it prints the rule’s documentation in the terminal:
$ ruff rule F401
# unused-import (F401)
Derived from the **Pyflakes** linter.
Fix is sometimes available.
## What it does
Checks for unused imports.
...
The same content is in the Ruff rules index, which lists all 968 rules grouped by source tool.
Which categories should you enable?
Out of the box, Ruff enables only the Pyflakes rules (F) and a subset of pycodestyle errors (E4, E7, E9). That default catches real bugs like unused imports and comparisons to None, but no style, import-order, or security rules until you opt in.
For a curated starting set, see How to configure recommended Ruff defaults. Two categories from the table have dedicated guides: How to sort Python imports with Ruff covers I, and How to enable Ruff’s security rules covers S.
Learn More
- Ruff: a complete guide covers configuration, editor integration, and migration
- Set up Ruff for formatting and checking your code walks through a first Ruff setup
- How to disable Ruff rules for a block of code shows
# noqain practice - The Ruff Linter documents rule selection and the default rule set