Ruff v0.16.0: 413 default rules and smarter Markdown formatting
The day CI suddenly started yelling
It starts innocently: a pull request, a green check the day before, and then… after upgrading a tool version, suddenly the pipeline floods you with new warnings. Not from your code. From your linter.
A linter is a program that inspects source code (without running it) and reports problems. In the Python world, that often means catching things like unused imports, suspicious logic, or style violations before the code ever hits production. Ruff is one of these tools, and it’s also a formatter (a program that rewrites code into a consistent style).
That “suddenly yelling” moment is exactly what Ruff v0.16.0 is built to do, but with more aggressive coverage: in v0.16.0, Ruff enables 413 rules by default, up from 59 in the previous default set. (astral.sh)
If you’ve ever wondered, why did my linter notice things it never noticed before?—this is the answer.
What “rules enabled by default” actually means
Ruff’s power comes from its rules. A rule is a specific check, like “this pattern is a likely bug” or “this import seems unused.” When a rule finds something, that result is a diagnostic. Think of diagnostics as the linter’s “here’s what looks wrong” messages.
Before v0.16.0, Ruff’s default rule set was narrower. Ruff still had hundreds of available checks, but far fewer were active by default. In v0.16.0, Ruff’s default expands dramatically: since the default set was last updated in the v0.1.0 era, the total number of rules in Ruff grew from 708 to 968, and the default selection now turns on many checks that can include syntax errors and immediate runtime errors. ()
So the change isn’t magic. It’s coverage.
How to migrate without drowning
When a tool expands its default rule set, two things usually happen:
- You learn about real issues you previously missed.
- You get a spike of noise that might not match your current team standards.
Ruff gives you a dial to control that. Two key configuration options are select and extend-select.
selectreplaces the default enabled rule set. You’re telling Ruff, “Use only these.” ()extend-selectadds extra rules on top of whatever defaults are active. It’s intended for “keep the defaults, plus more.” (docs.astral.sh)
A common “stop the flood” migration step is to temporarily revert to the old style of defaults:
[lint]
select = ["E4", "E7", "E9", "F"]
That exact snippet mirrors Ruff’s recommended way to go back to the old default set behavior. (astral.sh)
Then, over time, teams typically re-enable the parts they trust most (and suppress the rest with reasons, not vibes). The trick is treating this like a gradual rollout rather than a one-shot rewrite of your standards.
The “adopt like grownups” strategy
For many teams, the smoothest path is:
- Run Ruff v0.16.0 in a “see what changed” mode.
- Fix the diagnostics that clearly correspond to bugs or obvious correctness problems.
- For the remaining noise, decide whether it’s genuinely irrelevant for your codebase or whether it should become a new standard.
This approach keeps momentum while still gaining the benefits of the stronger default rules.
Ruff now formats Python inside Markdown code blocks
The other major v0.16.0 upgrade feels smaller until you’ve lived through the mess it solves.
Ruff’s formatter can now format Python code blocks embedded in Markdown files. In Markdown, code blocks are often written as fenced code blocks—delimited by triple backticks—like:
```py
print("hi")
```
The short tag after the opening backticks is called an info string. Ruff looks for info strings like python, py, python3, py3, and pyi and formats the code inside accordingly. (astral.sh)
Ruff also understands a Quarto style where the language sits inside curly braces (for example, {python}), and it may require configuration depending on file extensions. ()
A practical example you’ll actually hit
Most projects keep snippets in README files and docs. Those snippets slowly drift: someone copies a block from a newer version of code, the indentation gets weird, and suddenly the snippet is the only place your code style is “free range.”
With Ruff v0.16.0, ruff format can rewrite those Markdown snippets to match your Python formatting rules—so documentation code stops rotting.
When you don’t want formatting
Sometimes the snippet is intentionally odd (for teaching, for showing a before/after, or for matching an external API). Ruff supports suppression for formatting using:
# fmt: off/# fmt: oninside code blocks- HTML comments like
<!-- fmt:off -->/<!-- fmt:on -->around regions
These mechanisms let the formatter skip content it shouldn’t touch. ()
New suppression comments: ruff: ignore and friends
Once your rule set expands, suppression comments become part of daily life. Ruff already had mechanisms for disabling rules in a region using paired ruff: disable and ruff: enable comments.
Ruff v0.16.0 adds new suppression forms:
ruff: ignore[...]suppresses a diagnostic on the same line or on the following logical line.ruff: file-ignore[...]suppresses diagnostics for the entire file (similar in spirit tonoqa). ()
A subtle but crucial detail: Ruff talks about logical lines—meaning a multi-line statement or a suite header treated as one unit. For example, when ignoring a function signature that spans lines, Ruff can apply the suppression to the whole logical header. (docs.astral.sh)
Ruff also supports adding these ignore comments automatically with a CLI flag.
Why this matters
Suppression is only useful when it’s disciplined. The best suppression comments include a reason (“why this is safe,” “why this is temporary,” “why this is legacy code”). Ruff’s newer suppression styles are designed to make that pattern natural, not tedious. ()
Ruff shows diffs for fixes in check and format --check
Even when your linter and formatter can automatically fix issues, it’s hard to trust a tool blindly.
Ruff v0.16.0 improves the feedback loop: it now shows diffs for available fixes as part of the default output when running check and format --check. Previously, you needed separate output modes to see the changes introduced by applying fixes. ()
That matters because:
- It turns “a fix exists” into “here’s exactly what would change.”
- It makes it easier to review and decide whether to accept the formatting/lint changes.
Ruff continues to support --diff (useful when you’re watching changes carefully), but v0.16.0 brings the fix details into the normal flow so you’re less likely to miss what the tool is about to do. ()
The bigger picture: stronger defaults, better ergonomics
Ruff v0.16.0 is a bundle of two ideas that work together:
- More default rules means fewer bugs and dead code slipping through unnoticed. ()
- Better formatting and clearer suppression/fix output means the inevitable friction is easier to manage. ()
The expanded default rules can feel abrupt, especially if your team has relied on Ruff primarily as a formatter. But once you treat it as a controlled migration—fix the obvious issues first, then tune your configuration—you gain a linter that not only cleans style, but also spotlights correctness.
And once documentation snippets start being formatted consistently, your “codebase + docs” become a single coherent system rather than two slowly diverging worlds.
Conclusion
Ruff v0.16.0 turns the dial up: 413 default rules instead of 59, plus Markdown code block formatting and more expressive suppression comments. () In practice, that means fewer silent mistakes, better doc hygiene, and feedback that explains what fixes actually change.
It’s not just more noise. It’s more signal—once the migration gets handled with intention.
Comments (0)
No comments yet. Be the first to respond!
Leave a Comment
Your comment will be visible after review.