How to Configure Ruff Linting Rules: select, ignore, and fixable Settings
I was working on a Python project recently and wanted to set up better linting. I’d heard about Ruff being super fast, so I gave it a try. But when I first ran it, I got confused by all the different rule codes and configuration options. What does “E501” mean? Why doesn’t Ruff complain about line length by default? Let me show you how I figured out Ruff’s lint configuration.
The Problem: Understanding Ruff’s Rule System
When I first used Ruff, I noticed something strange. Running ruff check on my code didn’t complain about long lines, but Flake8 did. What’s going on?
The answer is simple: Ruff doesn’t enable all the same rules as Flake8 by default.
This is actually a good thing. Ruff takes a conservative approach - it only enables rules that catch definite errors by default. Things like syntax errors, undefined variables, and import issues. This means you won’t get overwhelmed with warnings when you first start using Ruff.
But sooner or later, you’ll want to customize which rules Ruff checks. That’s where the select, ignore, and fixable settings come in.
The Three Key Settings
Ruff’s lint configuration revolves around three main settings:
1. select - Enable Rules
The select setting tells Ruff which rule categories or specific rules to enable. By default, Ruff uses:
[tool.ruff.lint]select = ["E4", "E7", "E9", "F"]This enables:
- E4 - pycodestyle indentation errors
- E7 - pycodestyle statement errors (like using
==instead ofis) - E9 - pycodestyle runtime errors (like syntax errors)
- F - Pyflakes errors (like undefined names or unused imports)
Notice what’s NOT included by default:
- W - pycodestyle warnings (including E501 line-length violations)
- C901 - McCabe complexity checks
This differs from Flake8, which enables more rules out of the box. If you’re migrating from Flake8, you might want to add more rules.
2. ignore - Disable Rules
The ignore setting lets you disable specific rules. For example, if you don’t want to enforce line-length limits:
[tool.ruff.lint]select = ["E4", "E7", "E9", "F", "W"]ignore = ["E501"]This enables all the default rules plus pycodestyle warnings, but ignores E501 (line too long).
3. fixable and unfixable - Control Auto-fixing
Ruff can automatically fix some violations when you run ruff check --fix. The fixable and unfixable settings control which rules can be auto-fixed.
By default, fixable = ["ALL"], meaning Ruff will fix all violations that have a fix available. But you might want to prevent certain fixes:
[tool.ruff.lint]select = ["E4", "E7", "E9", "F", "B"]unfixable = ["B"]This prevents Ruff from auto-fixing bugbear (B) violations. Why would you want this? Some fixes might change behavior or require manual review.
Understanding Rule Codes
Ruff uses a code system where each letter represents a category:
| Code | Category | Description |
|---|---|---|
| F | Pyflakes | Undefined names, unused imports |
| E | pycodestyle Error | Style errors |
| W | pycodestyle Warning | Style warnings |
| C | Complexity | McCabe complexity, cognitive complexity |
| B | flake8-bugbear | Common bugs and design problems |
| I | isort | Import sorting |
| N | pep8-naming | Naming conventions |
| UP | pyupgrade | Modernize Python syntax |
| ANN | flake8-annotations | Type annotation checks |
| S | flake8-bandit | Security issues |
You can see all available rules on the Ruff Rules page.
Common Configuration Examples
Example 1: Enable All Rules
I tried this when I wanted maximum checking:
[tool.ruff.lint]select = ["ALL"]ignore = [ "D", # Disable all docstring rules "ANN", # Disable type annotation requirements "E501", # Disable line-length checks]Using select = ["ALL"] enables every rule Ruff supports. Then I use ignore to disable the ones I don’t want.
Warning: This will produce A LOT of warnings on existing code. Start with a smaller selection and add rules gradually.
Example 2: Minimal Configuration with Bugbear
For new projects, I like to add bugbear rules:
[tool.ruff.lint]select = ["E4", "E7", "E9", "F", "B", "I"]ignore = ["E501"]This gives me:
- Default error checking
- Bugbear (B) for common bug patterns
- Import sorting (I)
Example 3: Flake8 Compatibility
If you’re migrating from Flake8, this configuration is similar:
[tool.ruff.lint]select = ["E", "F", "W", "C901"]ignore = ["E501"]Note: Ruff doesn’t enable W rules by default, so I add them explicitly.
Per-File Ignores
Sometimes you need to ignore certain rules in specific files. For example, __init__.py files often have imports at the bottom:
[tool.ruff.lint.per-file-ignores]"__init__.py" = ["E402"] # Allow module-level imports after other code"**/{tests,docs,tools}/*" = ["E402", "S101"] # Tests can use assertsThe key is the file pattern, and the value is a list of rule codes to ignore.
I use this for test files that intentionally use assert statements (S101) or have imports in unusual places.
The Difference Between select and extend-select
There’s also an extend-select option that adds to the default selection instead of replacing it:
[tool.ruff.lint]# This REPLACES the default selectionselect = ["B"]
# This ADDS to the default selectionextend-select = ["B"]Use extend-select when you want to keep the defaults and add more rules.
A Practical Configuration
Here’s a configuration I use for my Python projects:
[tool.ruff.lint]select = [ "E", # pycodestyle errors "F", # Pyflakes "I", # isort "B", # flake8-bugbear "UP", # pyupgrade]ignore = [ "E501", # line too long (let the formatter handle it) "B008", # do not perform function calls in argument defaults]
[tool.ruff.lint.per-file-ignores]"__init__.py" = ["E402"]"**/tests/*" = ["S101"]This configuration:
- Enables common error checks
- Sorts imports automatically
- Catches bug patterns
- Suggests modern Python syntax
- Ignores line length (let the formatter handle it)
- Allows test assertions
Related Knowledge: How Ruff Determines Fix Priority
When you run ruff check --fix, Ruff applies fixes in a specific order. Understanding this helps you write better configurations.
Ruff categorizes fixes into:
- Safe fixes - Apply automatically with
--fix - Unsafe fixes - Require
--unsafe-fixesflag - Display-only fixes - Show suggestions but never auto-apply
You can check if a rule is fixable by looking at its documentation page. For example, E501 (line-too-long) is NOT fixable because shortening lines requires human judgment.
Summary
In this post, I explained how Ruff’s lint configuration works. I covered the three main settings: select for enabling rules, ignore for disabling rules, and fixable/unfixable for controlling auto-fixing. I showed you practical examples including how to enable all rules, migrate from Flake8, and use per-file ignores. The key takeaway is that Ruff starts conservative with defaults but gives you powerful customization options through pyproject.toml.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments