# Ruff 0.16.0 Enables 7x More Rules by Default


Upgrade [Ruff](https://pydevtools.com/handbook/reference/ruff.md) to 0.16.0 and your next `ruff check` may flag hundreds of new violations. Your code didn't change; Ruff enabled 354 additional rules by default.

Since v0.1.0 in October 2023, the out-of-the-box selection stayed `["E4", "E7", "E9", "F"]`: pyflakes detections and a narrow slice of pycodestyle. Those conservative defaults are why the handbook's [guide to recommended Ruff defaults](https://pydevtools.com/handbook/how-to/how-to-configure-recommended-ruff-defaults.md) covers enabling bugbear, simplify, pyupgrade, and the rest via `extend-select`. With 0.16.0, many of those rules are on by default.

## Which categories are now on by default

The new default set runs 413 rules across 34 categories. The ones that fire most often in real codebases:

- **`B` (flake8-bugbear):** 29 rules. Mutable default arguments, function calls in defaults, loop variable capture, unsafe `setattr`/`getattr` usage.
- **`UP` (pyupgrade):** 42 rules. `Optional[X]` to `X | None`, deprecated `typing.List`/`Dict`/`Tuple` to built-in equivalents, unnecessary `__future__` imports.
- **`SIM` (flake8-simplify):** 21 rules. Nested `with` statements, redundant boolean comparisons.
- **`PL` ([pylint](https://pydevtools.com/handbook/reference/pylint.md)):** 67 rules covering conventions, errors, refactoring suggestions, and warnings.
- **`C4` (flake8-comprehensions):** 17 rules. Unnecessary generator-to-list wrapping, redundant collection constructors.
- **`DTZ` (flake8-datetimez):** 10 rules. `datetime.now()` without timezone, `utcnow()` usage.
- **`I` (isort):** import sorting.
- **`ASYNC` (flake8-async):** 10 rules. Blocking calls inside async functions, missing checkpoints in async loops.
- **`FURB` (refurb):** 17 rules. `sorted()[0]` instead of `min()`, bitwise operations over set operations, redundant type conversion.
- **`RUF` (Ruff-specific):** 36 rules. Mutable class variable defaults, implicit optional, ambiguous class-variable annotations.
- **`TRY` (tryceratops):** 5 rules. Verbose `raise` in `except` blocks, redefining the caught exception variable.
- **`YTT`** (flake8-2020), **`PYI`** (stub file checks), **`PT`** (pytest style), **`FA`** (future annotations), and more.

The full list is on Astral's [Default Rules page](https://docs.astral.sh/ruff/default-rules/).

## Freeze the old defaults

If you want zero behavior change, pin the previous selection in [pyproject.toml](https://pydevtools.com/handbook/reference/pyproject.toml.md):

```toml
[tool.ruff.lint]
select = ["E4", "E7", "E9", "F"]
```

This restores exactly what 0.15.x ran.

## Triage the new violations

For codebases willing to adopt the new defaults, triage one category at a time:

```bash
ruff check --select B .
ruff check --select UP .
ruff check --select SIM .
```

Most `UP` violations have auto-fixes:

```bash
ruff check --fix --select UP .
```

For categories with more judgment calls, such as `PL` or `TRY`, run `ruff check --select PL .` to see which codes fire in your codebase, then add the ones you want to silence to `ignore`:

```toml
[tool.ruff.lint]
ignore = ["PL..."]  # pin specific codes after reviewing what fires
```

Audit any `extend-select` entries before removing them. The new defaults enable only a subset of each category (42 of the available `UP` rules, 21 `SIM`, 17 `C4`), not every stable rule under the prefix. A broad `extend-select = ["UP"]` adds every stable UP rule, so dropping it would silently disable the ones outside the defaults. Check what your `extend-select` adds beyond the default set on Astral's Default Rules page first.

## See what else 0.16.0 changed

- **Markdown formatting.** `ruff format` now reformats Python code blocks inside Markdown files by default, so `ruff format .` on a docs repo will touch `.md` files. To opt out, add `"*.md"` to the `exclude` list or wrap a block in `fmt: off`/`fmt: on` comments.

- **New suppression syntax.** Two new comment forms join `# noqa`:

  ```python
  import math  # ruff: ignore[F401]

  # ruff: ignore[F401]
  import os
  ```

  `# ruff: file-ignore[F401]` suppresses a rule for the entire file. The existing `# noqa` syntax still works; the new forms are an alternative. For block-level suppression, see [how to disable Ruff rules for a block of code](https://pydevtools.com/handbook/how-to/how-to-disable-ruff-rules-for-a-block-of-code.md).

- **Inline fix diffs.** `check` and `format --check` now print a diff of the available fix beside each diagnostic, so CI shows what would change without a separate `--fix --diff` pass.

- **CI output formats.** `ruff format --check` now supports `--output-format github` and `--output-format gitlab`, rendering inline PR annotations for format violations the same way the linter already did.

- **JSON output.** The `filename`, `location`, and `end_location` fields in JSON output may now be `null` for diagnostics without a file location, rather than defaulting to empty strings or row 1, column 1. Tooling that reads Ruff's JSON output should handle `null` in those fields.

## Learn More

- [Ruff 0.16.0 release post (Astral)](https://astral.sh/blog/ruff-v0.16.0)
- [Default Rules reference (Astral docs)](https://docs.astral.sh/ruff/default-rules/)
- [Ruff reference](https://pydevtools.com/handbook/reference/ruff.md)
- [How to configure recommended Ruff defaults](https://pydevtools.com/handbook/how-to/how-to-configure-recommended-ruff-defaults.md)
- [Ruff: a complete guide](https://pydevtools.com/handbook/explanation/ruff-complete-guide.md)
- [How to disable Ruff rules for a block of code](https://pydevtools.com/handbook/how-to/how-to-disable-ruff-rules-for-a-block-of-code.md)
- [How to migrate from Black, isort, flake8, and pyupgrade to Ruff](https://pydevtools.com/handbook/how-to/how-to-replace-black-isort-flake8-pyupgrade-with-ruff.md)
