Warming up the neural circuits...
By the end of this chapter you will:
Manual style policing in code reviews is expensive and inconsistent.
Teams need fast automated feedback that catches both style drift and bug-prone patterns before merge.
Ruff consolidates linting and formatting into a single fast toolchain, reducing setup complexity and review friction.
Mental model: static checks are guardrails that keep your codebase healthy at scale.
ruff check .
ruff check . --fix
ruff format .check enforces lint rules; format enforces code layout consistency.
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM"]
ignore = ["E501"]High-value buckets:
F catches many correctness issues,B catches risky constructs,UP nudges modern Python usage,I enforces import ordering.[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.format]
quote-style = "double"
[tool.ruff.lint.per-file-ignores]
"tests/**" =Central config keeps editor, local CLI, and CI behavior aligned.
ruff check . --fix --unsafe-fixesUse unsafe fixes carefully in dedicated cleanup PRs, not mixed with feature changes.
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.ruff": "explicit"
}
}Fast local feedback shortens the defect loop dramatically.
Migration sequence:
1) Pin Ruff version
2) Run format-only PR
3) Enable core lint buckets
4) Fix remaining violations
5) Enforce in CISplit migration into focused PRs to avoid painful review noise.
- name: Ruff lint
run: ruff check .
- name: Ruff format check
run: ruff format . --checkSeparate lint and format checks improve error readability in pipelines.
Treat lint policy as a product: start with high-signal rules first, then tighten gradually as team confidence grows.
| Rule group | Primary value | Risk if ignored |
|---|---|---|
F (pyflakes) | correctness | undefined names, dead imports |
B (bugbear) | bug prevention | subtle runtime pitfalls |
UP (pyupgrade) | modernization | stale syntax and missed language improvements |
I (imports) | consistency | merge conflicts and style churn |
SIM (simplify) | readability | unnecessary complexity |
| Mistake | Why it hurts | Better move |
|---|---|---|
ruff: error: unrecognized arguments | Version/CLI mismatch | Pin Ruff version and sync docs/CI |
F401 imported but unused ignored everywhere | Hides dead code and wrong imports | Remove unused imports or narrow exceptions |
E902 IOError in lint stage | Lint target includes generated or inaccessible files | Scope include/exclude patterns explicitly |
| Massive mixed formatting + feature PRs | Review noise and merge pain | Run dedicated format/migration PR first |
| Formatter and linter conflict | Toolchain instability | Use single authoritative Ruff config in pyproject |
pyproject.toml and run ruff check . on a sample project.Python project with mixed style issuesDeterministic lint outputruff check --fix and ruff format, then inspect remaining manual fixes.Project with lint and format driftReduced violation count and clean formatted filesTest files requiring assertion style exceptionsScoped ignore behavior only in testsVS Code settingsAutomatic formatting and fix behavior on saveGitHub Actions workflowPipeline blocks on violations and passes after fixesBeginner:
"What is the difference between linting and formatting?"
Linting catches code-quality and bug-risk issues, while formatting enforces consistent code layout.
"Why gate linting in CI if developers run it locally?"
CI ensures policy consistency and catches cases where local tooling was skipped or misconfigured.
Senior:
"How would you migrate a large legacy repo to Ruff with minimal disruption?"
Use phased rollout: format-only PR, core high-signal rules, incremental tightening, and explicit ownership.
"How do you decide when to ignore a lint rule versus fixing the code?"
Prefer fixing; allow scoped ignore only when there is clear justification and documented tradeoff.
Need fast quality checks -> ruff check + ruff format
Need high signal -> prioritize F/B/UP/I rule groups
Need low friction adoption -> phased migration strategy
Need consistency -> single pyproject config across local and CI
Need long-term enforcement -> CI gates with pinned tool versionWhat is the practical difference between `ruff check` and `ruff format`?