As a reminder: Ruff is an extremely fast Python linter, written in Rust. Ruff can be used to replace Flake8 (plus dozens of plugins), isort, pydocstyle, pyupgrade, and more, all while executing tens or hundreds of times faster than any individual tool.

Ruff is used in production by tens of thousands of open source projects and major enterprises. In the last year, we've been working to expand Ruff's feature set and improve stability for those that depend on Ruff in their daily workflows.

Today, we're excited to announce that Ruff v0.1.0 is out! This release is focused on stabilization, and includes a new preview mode, the introduction of fix safety levels, and a formal versioning policy.

Read on for discussion of the major changes, take a look at the 100+ changes in this release, or install Ruff now from PyPI or your package manager of choice:

pip install --upgrade ruff

A growing feature set

Earlier this year, Charlie looked back on the first 200 releases of Ruff, at which point 376 lint rules were supported. Ruff now supports over 700 rules, with new rules being added in every release. In case you missed it, we've released some exciting features this year:

We also released an alpha version of our Black-compatible code formatter. Expect more news on that front soon!

Introducing preview mode

In v0.0.289, we added a new preview mode to enable the use of unstable features when running Ruff.

Since new rules often benefit from a trial period, we want to be able to release them in a staged manner. Previously, we used a "nursery" rule category to do this, a concept taken from Clippy, Rust's linter.

However, the nursery required opt-in for each rule using their exact code, e.g., if you'd selected F and we released an unstable rule F999, you'd need to update your selection to [F, F999]. Requiring opt-in for each rule reduced exposure of new rules and could be tedious for users to update. Furthermore, as Ruff grows, we want a global concept for use of unstable features — the nursery concept doesn't scale beyond rule selection.

Preview mode will be used to enable new rules, unstable fixes, and experimental code analysis. Releasing these features — but gating them to users who opt-in — will allow us to collect community feedback and gain confidence that changes are a net-benefit.

Preview mode can be enabled per-invocation with the --preview flag or by updating your configuration file:

[tool.ruff]
preview = true

Check out the docs for preview mode and give it a try!

Respecting fix safety

Ruff's ability to automatically fix violations is one of its most powerful features. However, given Python's dynamic nature, it's not always possible to guarantee that a fix is "safe". Since May, we've been labeling a subset of Ruff's fixes as unsafe. When applying safe fixes, the meaning and intent of your code will be retained; when applying unsafe fixes, the meaning could change.

For example, unnecessary-iterable-allocation-for-first-element is a rule which checks for potentially unperformant use of list(...)[0]. The fix replaces this pattern with next(iter(...)) which can result in a drastic speedup:

$ python -m timeit "head = list(range(99999999))[0]"
1 loop, best of 5: 1.69 sec per loop

$ python -m timeit "head = next(iter(range(99999999)))"
5000000 loops, best of 5: 70.8 nsec per loop

However, when the collection is empty, this fix changes the raised exception from an IndexError to StopIteration:

$ python -c 'list(range(0))[0]'
Traceback (most recent call last):
File "<string>", line 1, in <module>
IndexError: list index out of range

$ python -c 'next(iter(range(0)))[0]'
Traceback (most recent call last):
File "<string>", line 1, in <module>
StopIteration

Since this could break error handling, this fix is categorized as unsafe.

Previously, the safety of a fix was only an internal note, but in v0.1.0, Ruff will only use safe fixes by default to increase confidence when applying fixes and highlight fixes that need additional review.

Unsafe fixes can be enabled by passing the --unsafe-fixes flag to ruff check:

# Show unsafe fixes
ruff check . --unsafe-fixes

# Apply unsafe fixes
ruff check . --fix --unsafe-fixes

or by setting unsafe-fixes in your configuration file:

[tool.ruff]
unsafe-fixes = true

When working with Ruff in an IDE (via our LSP), we will no longer apply unsafe fixes with the "Fix all" action. Instead, unsafe fixes can be applied individually with the "Quick fix" action allowing review of each fix. If you opt-in to unsafe fixes, they'll be applied during "Fix all". See the LSP docs for more details.

The safety of fixes can be adjusted per rule using the extend-safe-fixes and extend-unsafe-fixes settings, allowing you to customize safety levels for your use-case.

See the docs for more details on fix safety.

Ruff's versioning policy

Thus far, Ruff releases have only involved incrementing the patch version number. This versioning scheme suffers from a few core problems:

  • There is no documented policy and little room for a meaningful policy
  • Rules are added in most releases, resulting in significant new violations for users on upgrade
  • Users cannot get bug fix upgrades with confidence that they will not need to do other work
  • Ruff is not perceived as stable

With the promotion to v0.1.0, Ruff can adopt a meaningful rules around versioning to ensure that users know what to expect from each release. With the addition of a minor version number, we hope to:

  • Reduce the frequency of large changes
  • Give rules a period for stabilization before enabling
  • Differentiate between breaking and non-breaking releases
  • Move towards stronger stability guarantees

With the release of 0.1.0, Ruff includes a full versioning policy. Briefly:

  • The minor version will be used for stabilization of preview features, changes to stable behavior, and breaking changes.
  • The patch version will be used for bug fixes and any changes to rules or fixes in preview.

Check out the docs for a full description of the policy.

A new home for Ruff's documentation

Our documentation has moved from beta.ruff.rs/docs to docs.astral.sh. We're excited to remove the beta label and move the documentation under the umbrella of our growing company. All existing links redirect to the new domain, and should continue to work as-before.

Thank you!

Thousands of community members visit our repository every day, and we continue to be energized by the incredible positivity around Ruff. It's a privilege and joy to build this project in collaboration with our 282 contributors, plus all of those that have engaged with the project along the way, be it on Discord or by filing an Issue. A big thank-you to everyone that's participated in this wonderful community.


View the full changelog on GitHub.

We're hiring! Read more about Astral — the company behind Ruff.