Get AI summaries of any video or article — Sign up free
Python Tutorial: Ruff - A Fast Linter & Formatter to Replace Multiple Tools and Improve Code Quality thumbnail

Python Tutorial: Ruff - A Fast Linter & Formatter to Replace Multiple Tools and Improve Code Quality

Corey Schafer·
4 min read

Based on Corey Schafer's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.

TL;DR

Ruff is an all-in-one Python linter and formatter that can replace multiple separate tools (linting, formatting, import sorting, and modernization) with one fast workflow.

Briefing

Ruff is positioned as an all-in-one, Rust-based Python linter and formatter that can replace a patchwork of tools—while running fast enough to make continuous feedback practical. Built by Astral (the same team behind the UV package manager), Ruff consolidates linting, formatting, import sorting, and code modernization into a single workflow, and it can automatically fix many issues without changing program behavior. That combination—speed plus consolidation—matters because it reduces tool sprawl and makes it easier to keep code consistent across projects.

The walkthrough starts with installation options. Ruff can be installed per-project into a virtual environment via pip, but that limits availability to each environment. For global use, pipx is presented as a common approach: pipx installs Python packages into isolated environments while exposing their command-line interfaces globally. Homebrew is also mentioned as an alternative, though the preference is to avoid Homebrew-managed Python packages for dependency cleanliness. The tutorial’s preferred method uses UV to install Ruff as a global tool (via `uv tool install rough`), followed by a quick verification using `ruff --version`.

From there, Ruff’s command-line workflow is demonstrated. Running `ruff check main.py` reports a set of errors with line numbers and error codes, and it distinguishes which problems are fixable. In the example script, Ruff flags issues such as multiple imports on one line, unused imports (e.g., `cis`, `time`, `datetime`, `random`), and a discouraged exception pattern: “do not use bare except” (E722). The error code becomes a learning path: Ruff’s documentation explains why the rule exists—such as how `except BaseException`-like behavior can interfere with interrupts like Ctrl+C and can mask other problems—and provides safer alternatives like catching more specific exceptions.

Ruff’s autofix capability is then put to work. Using `ruff check --fix` applies only safe fixes (unused imports and similar issues), leaving the non-automatable bare-except problem for manual correction. To preview changes without modifying files, the tutorial uses `--diff`, showing a Git-style diff of what would be altered. Formatting is handled separately with `ruff format`, and the recommended workflow is to run `ruff check --fix` first, then `ruff format`, so lint-driven edits don’t fight formatting.

For continuous feedback, Ruff’s `--watch` mode is highlighted: it monitors files and re-lints on save, immediately surfacing new issues like unused imports. Configuration is handled through `pyproject.toml` (or `ruff.toml`), with Ruff settings nested under `tool.ruff`. The tutorial shows extending the default rule set via `lint.extend-select`, using rule codes such as `PTH` to enforce `pathlib` usage over built-in `open`. It also covers selecting all rules (`lint.extend-select = [

Cornell Notes

Ruff is a fast, Rust-based Python linter and formatter that consolidates multiple common tools into one. It reports issues with precise line numbers and error codes, and it can automatically fix many safe problems using `ruff check --fix` while leaving riskier cases for manual review. Ruff also supports formatting (`ruff format`) and real-time linting (`ruff check --watch`). Configuration lives in `pyproject.toml` under `tool.ruff`, where rule sets can be extended (e.g., `PTH` for `pathlib`) and specific error codes can be ignored. This matters because it reduces tool sprawl, speeds up feedback loops, and makes code consistency enforceable across projects.

How does Ruff’s command-line workflow help developers learn while fixing code?

Ruff’s `ruff check <file>` output includes exact line numbers and error codes (like E722 for “do not use bare except”). Those codes map directly to documentation that explains why a pattern is problematic—e.g., bare exceptions can catch interrupts such as Ctrl+C and can disguise other failures. The tutorial uses the error code to look up the rule and then manually adjusts the code to match the recommended exception handling.

What’s the practical difference between `ruff check --fix` and `ruff check --diff`?

`ruff check --fix` applies safe automatic fixes and then re-reports any remaining issues (in the example, unused imports were removed, while the bare-except rule remained). `ruff check --diff` previews the exact changes in a diff-like view without modifying the file, letting developers verify what Ruff would change before committing.

Why is the order “lint fixes first, then formatting” recommended?

The tutorial recommends running `ruff check --fix` before `ruff format` because lint-driven edits can affect code structure (imports, spacing, and other changes). Formatting afterward ensures the final output follows Ruff’s formatting conventions without being disrupted by subsequent lint fixes.

How does Ruff’s `--watch` mode change day-to-day usage?

With `ruff check --watch`, Ruff monitors files and re-runs checks on save. In the example, adding an unused import triggers an immediate warning highlight in the terminal output as soon as the file is saved, reducing the need to repeatedly run manual commands while editing.

How do `pyproject.toml` rule selections like `lint.extend-select` affect what Ruff flags?

Ruff defaults are “non-controversial,” but configuration can extend them. The tutorial uses `lint.extend-select` to add specific rule codes such as `PTH` to enforce `pathlib` usage (e.g., flagging `open` and suggesting `pathlib.Path.open`). After saving the config, rerunning `ruff check` shows new errors that weren’t present under defaults.

What’s the purpose of ignoring specific error codes in Ruff configuration?

When enabling broader rule sets (even all rules), some checks may conflict with a developer’s preferences. The tutorial demonstrates copying a rule code (like T201 for print statements) and adding it to a `lint.ignore` list in `pyproject.toml`. After that, Ruff stops reporting that category of issue, reducing noise while keeping other rules active.

Review Questions

  1. When Ruff reports an error code like E722, what workflow should a developer follow to understand and fix it correctly?
  2. What kinds of issues are more likely to remain after running `ruff check --fix`, and why might Ruff avoid auto-fixing them?
  3. How would you structure a `tool.ruff` configuration in `pyproject.toml` to both extend rules (e.g., PTH) and ignore a specific rule code?

Key Points

  1. 1

    Ruff is an all-in-one Python linter and formatter that can replace multiple separate tools (linting, formatting, import sorting, and modernization) with one fast workflow.

  2. 2

    Ruff can be installed per-project with pip, globally with pipx, or globally with UV; verify with `ruff --version`.

  3. 3

    Use `ruff check` for linting and `ruff format` for formatting; a common workflow is `ruff check --fix` first, then `ruff format`.

  4. 4

    Ruff’s `--fix` applies only safe, behavior-preserving changes, while `--diff` previews edits without modifying files.

  5. 5

    Ruff supports real-time feedback via `ruff check --watch`, re-linting on file save.

  6. 6

    Ruff configuration belongs in `pyproject.toml` under `tool.ruff`, where rule sets can be extended with `lint.extend-select` and noise can be reduced with `lint.ignore`.

  7. 7

    Ruff’s type-hinting rules are not the same as a type checker; pair Ruff with a type checker like mypy for deeper correctness checks.

Highlights

Ruff’s error codes (like E722 for bare `except`) act like a direct index into rule documentation, turning lint output into actionable learning.
`ruff check --fix` is intentionally conservative: it fixes what’s safe (e.g., unused imports) and leaves riskier issues for manual correction.
`ruff check --watch` enables a tight edit–save–feedback loop by re-running checks automatically.
Ruff can enforce style choices such as preferring `pathlib` by enabling rule codes like `PTH` in `tool.ruff` configuration.

Topics

  • Ruff Setup
  • Python Linting
  • Autofix and Diff
  • VS Code Integration
  • Rule Configuration

Mentioned