ruffはRust製のPython用linter/formatterで、flake8・black・isort・pyupgradeなどを1コマンドに統合できます。現行は0.15.13(2026年5月14日リリース)。Astral社の公式サイトはpandasコードベースを0.5秒でlintできると示しており、flake8の30秒と比べて60倍の差。導入からCI組み込みまでをpyproject.tomlの実例で整理します。
ruffが置き換える4つのツールと速度差
ruffは複数のPython向け静的解析ツールをRustで再実装しています。役割と公式が示す速度差は次の表のとおり。
| 従来ツール | 役割 | ruffでの吸収 | 速度差(公式値) |
|---|---|---|---|
| flake8 | lint | ruff check | pandasで約60倍 |
| black | format | ruff format | 10〜100倍 |
| isort | import整理 | lint側のIルール | 同梱 |
| pyupgrade | 構文モダン化 | UPルール群 | 同梱 |
flake8のプラグイン群(flake8-bugbear、flake8-comprehensions、flake8-simplifyなど)も内部に取り込み済み。1つのRust製バイナリで完結するため、requirements-dev.txtからこれらPython製ツールの依存をまるごと外せます。
インストールから初回実行までの手順
pipとuvどちらでも入る
ruffはバイナリ配布。Pythonへの依存追加は実質ゼロです。
# uv 派 (推奨)
uv tool install ruff
# pip 派
pip install ruff
ruff --version
実行結果:
ruff 0.15.13
ruff check と ruff format の最小コマンド
ruff checkがlinter、ruff formatがformatter。引数なしで現在ディレクトリ以下を再帰的に走査します。
$ ruff check .
src/utils.py:3:1: F401 [*] `json` imported but unused
src/handlers.py:42:5: E731 Do not assign a `lambda` expression
Found 2 errors.
[*] 1 fixable with the `--fix` option.
初回はデフォルトルールだけが有効になります。公式の“Inferring the Python version”節に記載のとおり、Pyflakes(F)とpycodestyleのE4/E7/E9のみ。900以上ある全ルールのうちごく一部だけが有効な状態で始まります。
pyproject.tomlでルール選択とignoreを書く
selectとignoreの使い分け
有効化したいルール群は[tool.ruff.lint]のselect、特定の違反だけ抑止したいときはignore。両者は同じlintセクションに並べて書きます。
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"F", # Pyflakes
"I", # isort
"B", # flake8-bugbear
"UP", # pyupgrade
"SIM", # flake8-simplify
]
ignore = ["E501"] # 行長はformatterに任せる
実行サマリで何件・どのルールが引っかかったかを確認:
$ ruff check --statistics src/
3 F401 [*] unused-import
1 UP015 [*] redundant-open-modes
2 SIM108 [*] if-else-block-instead-of-if-exp
Found 6 errors.
[*] 6 fixable with the `--fix` option.
per-file-ignoresでテストだけ緩める
本体には厳しく、テストやマイグレーションには緩く、というファイル別の緩和はper-file-ignoresで書きます。テストのassert禁止(S101)や、自動生成コードに対するルール除外はここに集約。
[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101", "D"] # assert許可、docstringチェック停止
"__init__.py" = ["F401"] # 再エクスポートを許可
"migrations/*" = ["ALL"] # 自動生成コードは対象外
extend-selectで段階的に厳しくする
既存プロジェクトへ途中導入する場合、最小ルールから始めてextend-selectで順次広げます。一度に全ルールを有効化すると修正PRが肥大化。レビュー粒度が崩れる原因になります。実際に約12万行のPythonリポジトリでEとFから入れた後、月次でI、UP、SIMと段階的に追加した経験では、各段階のPRが200〜400ファイル程度で収まりました。
[tool.ruff.lint]
select = ["E", "F"] # 最初はここだけ
extend-select = ["I", "UP"] # 次の段階で足す
Fix safetyを理解する – safeとunsafeの違い
ruffは公式ドキュメントの“Fix safety”章で自動修正をsafeとunsafeに分類しています。
The meaning and intent of your code will be retained when applying safe fixes.
safe fixはコードの振る舞いを変えない範囲の修正。デフォルトの--fixはsafe fixだけを適用します。unsafe fixは挙動が変わる可能性があるため、--unsafe-fixesを明示しない限り走りません。
unsafe fixの具体例
公式が示す例: list(...)[0]をnext(iter(...))に書き換えるとき、空コレクションだとIndexErrorではなくStopIterationが飛びます。例外型が変わるためcatch側が破損し得る。これがunsafe扱いの理由です。
ruff check --fix . # safeのみ適用
ruff check --fix --unsafe-fixes . # unsafeも含めて適用
noqaで個別に抑制する
行単位の抑制はnoqaコメント。ファイル単位で全部止めるなら冒頭にruff: noqa、特定ルールだけ無効化するならruff: noqa: F841を1行入れます。
import json # noqa: F401 再エクスポート目的で残す
x = eval(s) # noqa どのルールにも引っかけない
ブロック単位の抑制(0.6系以降)は# ruff: disable[E501]と# ruff: enable[E501]のペアで挟みます。生成コードや長い文字列リテラルだけ局所的に外したいときに便利。
ruff formatでblackから移行する差分
ruff formatは公式ドキュメントの“Philosophy”章でBlack互換を掲げています。ほぼ同じ出力を出しつつ、公式が“Intentional deviations”として残している意図的な違いも存在。代表的な3点が次のh3。
f-string内部の式まで整形する
blackはf-stringの式部分に介入しません。ruffはクオート種別や折り返しをf-string内まで揃えます。
# before (blackでは残るパターン)
msg = f"{user[ 'name' ]}: {len( items )}件"
# ruff format 後
msg = f"{user['name']}: {len(items)}件"
docstring内のコードブロックを整形する
docstring-code-format = trueを有効にすると、docstring中のフェンス付きコードブロックまで整形対象。公開ライブラリのAPIリファレンスでよくある>>>サンプルやコード例も、本体コードと同じ書式に揃います。
[tool.ruff.format]
docstring-code-format = true
docstring-code-line-length = 80
quote-style = "double"
magic trailing commaの扱い
末尾カンマで強制改行する仕様(magic trailing comma)はblackと同じ動作。無効化したい場合はskip-magic-trailing-comma = trueを入れます。
CIで失敗させる – –check と –exit-non-zero-on-fix
CIでフォーマット崩れを検知したいときはruff format --check、lintで自動修正可能な違反があるだけで失敗させたいならruff check --exit-non-zero-on-fixを使います。
GitHub Actionsの最小例
- name: ruff lint
run: uv tool run ruff check .
- name: ruff format check
run: uv tool run ruff format --check .
差分が見つかった場合の出力:
Would reformat: src/handlers.py
Would reformat: tests/test_utils.py
2 files would be reformatted, 18 files already formatted
Error: Process completed with exit code 1.
pre-commitに繋ぐ
公式リポジトリruff-pre-commitを利用。commit直前にruff check --fixとruff formatを実行し、自動修正があればそのままcommitに含めます。revのバージョン固定が必須のため、updateはdependabotかrenovateに任せる運用が無理なく回ります。
# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.13
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
VSCodeで保存時に走らせる設定
VSCode公式拡張Ruff(Astral社製)を入れ、ワークスペース設定で保存時のformatとfixを有効化。設定の本体はcodeActionsOnSaveにあります。
{
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.ruff": "explicit",
"source.organizeImports.ruff": "explicit"
}
}
}
保存のたびにimport並び替えとsafe fixが走る挙動。チームで揃える場合は.vscode/settings.jsonをリポジトリにcommitすれば全員に同じ挙動が配布されます。explicitを指定する理由は、保存ショートカットに対してだけ反応させ、外部ツールからのファイル書き換えで誤発火させないため。
まとめ
- ruff 0.15.13は
ruff checkとruff formatでlint・format・import整理・構文モダン化を一括化 - pyproject.tomlの
[tool.ruff.lint]にselectとignoreを書き、per-file-ignoresでテストだけ緩める構成が扱いやすい - 自動修正は
safeとunsafeに分かれ、unsafeは--unsafe-fixesを明示するまで走らない - blackからの移行はf-string内整形・docstring内コード整形・magic trailing commaの3点を押さえれば差分は局所化
- CIは
ruff format --checkとruff check --exit-non-zero-on-fixで失敗させ、ローカルはpre-commitとVSCodeのcodeActionsOnSaveで揃える

