Keeping a Python project organized involves much more than choosing good variable names. As the codebase grows, small style problems, disordered imports, unused code and simple mistakes begin to consume review time. Ruff for Python automates much of this work by combining linting, import organization and formatting in one fast tool with a straightforward configuration.
In this guide, you will install Ruff, run your first checks, apply automatic fixes safely, configure pyproject.toml, integrate the tool with VS Code and prepare it for continuous integration. It also helps to review our guide to PEP 8 in Python, because Ruff turns many style recommendations into repeatable automated checks.
What is Ruff for Python?
Ruff is a static analysis tool that finds problems in Python files without running the application. Its official documentation describes the linter as a fast replacement for combinations involving Flake8 and many plugins, isort, pydocstyle, pyupgrade and autoflake. The project also includes a formatter available through ruff format. See the official Ruff documentation for the complete rule catalog and configuration reference.
In practical terms, one setup can detect unused imports, reorder imports, flag forgotten variables, identify syntax-related issues, apply modernization rules and keep code presentation consistent. Ruff does not replace automated tests or type checking, but it removes a large amount of repetitive feedback before code reaches a human reviewer.
Why use Ruff in a Python project?
- Fast feedback: checks can run on save, before a commit or inside a CI pipeline.
- Central configuration: rules, exclusions and formatting preferences can live in
pyproject.toml. - Fewer separate tools: linting, imports and formatting can share one workflow.
- Automatic fixes: many findings can be resolved with
ruff check --fix. - Consistency: every contributor follows the same project rules.
This consistency is valuable in teams, courses and open-source repositories. Instead of discussing every space or import during review, the team defines the mechanical rules once. Combine Ruff with tests and clear module boundaries. Our guides to Python modules and packages and virtual environments are useful foundations for that workflow.
How to install Ruff
Create or activate an isolated environment before installing project tools. The tutorial on Python virtual environments with venv explains the process on Windows, macOS and Linux. After activation, run:
python -m pip install ruffConfirm that the command is available:
ruff --versionYou can also add Ruff to a development dependency group managed by Poetry, uv or another project tool. The important point is to record it so another contributor can reproduce the same environment. For a detailed pip workflow, read how to install Python libraries with pip.
Run your first check with ruff check
Open a terminal in the project root and run the following command. The dot represents the current directory, so Ruff discovers Python files in that directory and its subdirectories.
ruff check .Consider an app.py file containing an unused import and imports that are not ordered:
import sys
import os
def greet(name):
message = "Hello"
return f"Hello, {name}!"
print(greet("Ana"))The result reports the file, line, column, rule code and a short explanation. The rule code matters because it lets you read the exact documentation or create a narrow exception when the project has a legitimate reason. Avoid treating all warnings as generic style noise; some rule families reveal real mistakes.
Apply automatic fixes
To apply fixes that Ruff exposes for the selected rules, use:
ruff check . --fixAlways review the Git diff before committing. Automatic fixes save time, but they do not remove the need to understand the change. A safe routine is to run the command, inspect the diff, execute the tests and only then create the commit.
Format Python code with Ruff
The linter searches for findings; the formatter rewrites code presentation consistently. To format all discovered Python files, run:
ruff format .A CI job should normally verify formatting without editing the checkout. Use check mode:
ruff format --check .The Ruff formatter documentation presents it as a formatter intended to be compatible with the style used by Black. In a new repository, select one primary formatter. Running two formatters over the same files can create alternating changes and noisy commits.
Configure Ruff in pyproject.toml
The pyproject.toml file is a central location for Python project tooling. The Python Packaging User Guide explains that tool-specific configuration belongs under subtables of [tool]. Create or update the file in the repository root with a small starting configuration:
[tool.ruff]
line-length = 88
target-version = "py311"
exclude = [".venv", "build", "dist"]
[tool.ruff.lint]
select = ["E4", "E7", "E9", "F", "I"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "auto"line-length sets the preferred line width. target-version tells Ruff which minimum Python version the project supports. exclude prevents generated directories and virtual environments from being analyzed. The selected prefixes enable core style findings, important syntax and logic checks from Pyflakes, and import sorting.
Begin with a focused set and expand gradually. Enabling a very large rule collection in an old codebase may produce thousands of findings without a realistic remediation plan. A better migration fixes critical errors first, commits the baseline configuration and adds stricter families in small, reviewable steps.
Ignore a rule carefully
When one rule does not fit the entire repository, add it to an ignore list. For a local and justified exception, use # noqa: CODE on the relevant line. Prefer the exact code instead of a broad # noqa, because the narrow form preserves other useful checks.
result = legacy_call() # noqa: F841Every exception should have a reason. If many lines require the same suppression, revisit the rule selection, refactor the code or create a per-file configuration. The goal is not merely to obtain a green command; it is to make meaningful defects easier to notice.
Integrate Ruff with VS Code
Install the official Ruff extension, open the project root and allow the editor to read the repository configuration. The extension can display diagnostics, organize imports and format files. If other extensions already provide the same functions, disable duplicate responsibilities to prevent repeated diagnostics and formatter conflicts. See our list of VS Code extensions for Python developers for a leaner editor setup.
A common setup enables format on save and chooses Ruff as the default formatter for Python files. Still, keep pyproject.toml as the primary source of rules. Editor-only settings are hard to reproduce in a terminal, on another developer’s machine or in CI.
Run Ruff before every commit
Manual checks work at first, but they are easy to forget. Add a script, a pre-commit hook or a task command. A minimal quality sequence can be:
ruff check .
ruff format --check .
pytestThis sequence runs static checks, confirms formatting and executes tests. If a step fails, the commit hook or pipeline should stop. For a large existing repository, introduce the workflow on changed files first and broaden coverage as legacy findings are resolved.
Use Ruff in continuous integration
In GitHub Actions, GitLab CI or another CI service, install the dependencies and run the same commands developers use locally. Do not create a completely separate rule set for the server. Continuous integration is most useful when it reproduces the local workflow in a clean environment.
python -m pip install ruff pytest
ruff check .
ruff format --check .
pytestPin or constrain versions according to the repository policy and update deliberately. A new release can add rules, change formatting behavior or expose additional fixes. Test updates in a branch and read the project release notes before applying them broadly.
Ruff vs Black, Flake8 and isort
| Tool | Primary role | Good fit |
|---|---|---|
| Ruff | Linting, imports and formatting | Projects seeking a fast, unified workflow |
| Black | Opinionated formatting | Teams already standardized on Black |
| Flake8 | Plugin-based linting | Legacy projects that depend on specific plugins |
| isort | Import organization | Workflows that intentionally keep separate tools |
A stable project does not have to migrate simply because a newer tool exists. Consider plugin dependencies, current conventions and the cost of changing every file. Ruff is attractive for new projects because it reduces configuration and dependency count. For older projects, a staged migration is usually safer than replacing everything in one pull request.
Common mistakes when adopting Ruff
- Running
--fixwithout review: inspect the diff and execute tests. - Enabling too many rules immediately: start with a useful baseline.
- Keeping two active formatters: assign one formatter to the same Python files.
- Configuring only the editor: commit the rules with the repository.
- Scanning the virtual environment: exclude
.venvand generated folders. - Using broad suppressions: specify rule codes and document exceptions.
Recommended workflow for a new project
Create the virtual environment, install Ruff and the test runner, add the configuration to pyproject.toml and perform an initial check. Format the repository, resolve the findings, commit the baseline and configure the editor. Finally, copy the same commands into the continuous integration workflow.
This creates three layers of feedback: the editor responds immediately, the terminal offers an explicit local validation and the CI job protects the main branch. Ruff handles repetitive mechanical work, leaving reviewers more time for architecture, security, domain rules and clarity.
Frequently asked questions
Does Ruff replace automated tests?
No. Ruff detects static patterns and certain mistakes, but it cannot confirm that the application produces the correct business result. Unit and integration tests validate behavior.
Can I use Ruff only as a linter?
Yes. You can run only ruff check and keep another formatter. Configure the tools so their responsibilities do not overlap unnecessarily.
Can Ruff be introduced into a legacy project?
Yes, but use a gradual strategy. Start with critical rule families, fix a manageable set of findings and increase strictness over time.
Should CI run ruff check –fix?
Usually CI should verify and fail rather than modify tracked files. Automatic fixes are better applied locally or through a dedicated bot that opens a reviewable change.
Where should Ruff configuration live?
A pyproject.toml file in the repository root is a practical choice because it can centralize Ruff and other Python tooling.
Conclusion
Using Ruff for Python makes linting, import organization and formatting a normal part of development. Begin with a simple configuration, review automatic changes, store the rules in the repository and run the same commands in the editor, terminal and pipeline.
Ruff delivers the most value as one part of a broader quality workflow. Virtual environments isolate dependencies, tests protect behavior and human review evaluates decisions no automatic tool fully understands. Together, these practices make a Python codebase more consistent, maintainable and ready to grow.






