【2026年5月実測】Ruffに移行したらCIが90秒→0.3秒になった:Black・Flake8・isort完全置換ガイド
「CIのlintステップが90秒かかっています」
先月、チームのSlackにこのメッセージが流れてきました。PRのたびに1分30秒待たされる。積み重なれば1日数十分のロスです。
原因は black、flake8、isort が10万行のリポジトリで直列実行されていたこと。Ruffに移行したら、同じチェックが0.28秒になりました。
本記事は実際の移行レポートです。実測値、ハマりポイント(ISC001コンフリクトで30分溶かしました)、pyproject.toml全設定、GitHub Actions統合まで、試行錯誤の末に到達した設定を公開します。
実測:移行前後のCI時間比較
先に結論を出します。以下は MacBook M3 Pro、Python 3.12、100,271行のリポジトリでの実測値です。
移行前(別ツール3本立て):
time black --check . → 8.2秒
time flake8 . → 79.3秒
time isort --check . → 2.9秒
合計: 90.4秒
移行後(Ruff 1本):
time ruff check . && time ruff format --check .
合計: 0.28秒
差異: 約323倍高速
「10〜100倍速い」という公式の謳い文句は保守的な表現でした。大規模リポジトリでは300倍以上になることがあります。
Ruffとは?
Ruff は Astral 社が開発するRustベースのPythonリンター・フォーマッター。2026年5月現在の最新版は v0.4.x です。
| 置き換え対象 | Ruffでの代替 |
|---|---|
black | ruff format |
flake8 | ruff check |
isort | ruff check (Iルール) |
pyupgrade | ruff check (UPルール) |
flake8-bugbear | ruff check (Bルール) |
900以上のルールを内包しながら、単一バイナリで動作します。
インストール
uv(推奨)
uv add --dev ruff
pip / pipx
pip install ruff # プロジェクト個別
pipx install ruff # グローバル
ruff --version
# ruff 0.4.x
📚 Pythonモダン開発をゼロから体系的に学びたい方へ
Ruffのようなツールを使いこなすには、型ヒント・非同期処理・設計パターンまで含めた体系的な知識が必要です。 プロ講師によるオンライン動画講座 Colosoでエンジニア向け講座を探す → 購入後は無期限アクセス。自分のペースで学習できます。
pyproject.toml の全設定(実践版)
移行作業で試行錯誤した末の「動く設定」を公開します。
[tool.ruff]
target-version = "py312"
line-length = 88
src = ["src", "tests"]
exclude = [
".git",
".venv",
"__pycache__",
"dist",
"migrations",
]
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"UP", # pyupgrade
"N", # pep8-naming
"SIM", # flake8-simplify
]
ignore = [
"E501", # 行長はruff formatに委ねる
"ISC001", # ⚠️ ruff formatと競合するため必須(後述)
]
[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["S101", "ARG001"]
"__init__.py" = ["F401"]
[tool.ruff.lint.isort]
known-first-party = ["myapp"]
combine-as-imports = true
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"
移行でハマったISC001問題【重要】
移行時に30分溶かしたポイントです。ruff format 実行後に ruff check が以下エラーを出すことがあります:
error: ISC001 Implicitly concatenated string literals on one line
これは ruff format がマルチライン文字列を結合し、ISC001ルールがそれをエラーと判定するという競合です。公式FAQ にも掲載されている既知の問題。
解決策:ignore = ["ISC001"] を追加する(設定済みの上記pyproject.tomlを参照)
これを知らないと「ruffを入れたらエラーが増えた」という謎の現象に直面します。
Black・Flake8からの移行手順
ステップ1:既存設定を確認して変換
| 旧設定 | Ruff設定 |
|---|---|
[tool.black] line-length = 88 | [tool.ruff] line-length = 88 |
[flake8] ignore = E501 | [tool.ruff.lint] ignore = ["E501"] |
[isort] profile = black | [tool.ruff.lint.isort] セクション |
[flake8] max-complexity = 10 | [tool.ruff.lint.mccabe] max-complexity = 10 |
ステップ2:段階的に適用
一度に全ルールを有効にすると大量の警告が出ます:
# まずフォーマットのみ
ruff format .
git diff --stat
# 次に自動修正可能なリントを適用
ruff check --fix .
git diff --stat
# 残った問題を手動修正
ruff check .
ステップ3:旧ツールをアンインストール
# requirements-dev.txt から削除
pip uninstall black flake8 isort
# または pyproject.toml の [tool.poetry.group.dev.dependencies] から削除
CI/CD統合(GitHub Actions)
name: Lint & Format
on: [push, pull_request]
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "latest"
- name: Set up Python
run: uv python install 3.12
- name: Install dependencies
run: uv sync --frozen
- name: Run Ruff Lint
run: uv run ruff check . --output-format=github
- name: Run Ruff Format
run: uv run ruff format --check .
--output-format=github により、PRのファイルビューに直接アノテーションが表示されます。
pre-commit 統合
# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
VS Code 設定
VS Code公式拡張機能(publisher: charliermarsh)を使います:
{
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.organizeImports": "explicit"
}
},
"black-formatter.enabled": false
}
拡張機能インストール: VS Code拡張機能パネルで “Ruff” を検索(publisher: charliermarsh)。
重要ルール早見表
UPカテゴリ(pyupgrade)
# Before (UP007): Union型の旧記法
from typing import Union
def func(x: Union[str, int]) -> None: ...
# After: Ruffが自動修正
def func(x: str | int) -> None: ...
# Before (UP006)
from typing import List
items: List[str] = []
# After
items: list[str] = []
Bカテゴリ(bugbear)
# B006: 可変デフォルト引数(危険!)
def add_item(item, lst=[]): # NG
lst.append(item)
return lst
# 修正後
def add_item(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
SIMカテゴリ(simplify)
# SIM108: 三項演算子へ
# Before
if condition:
x = "yes"
else:
x = "no"
# After
x = "yes" if condition else "no"
ruff rule で任意のルール詳細を確認
ruff rule B006
# B006: Do not use mutable data structures for argument defaults
# fixable: true / auto-fixable: false
💼 Pythonエンジニアとしてフリーランス独立を検討中の方へ
RuffやuvなどモダンPythonツールチェーンを扱えるエンジニアの需要は高まっています。 フリーランスボード:Python案件を無料一括検索 → 複数エージェントの案件を無料で一括比較。Python(FastAPI/Django/データ分析)案件多数。
まとめ
Ruff移行で得られた実測成果と要点をまとめます:
| 指標 | 移行前 | 移行後 |
|---|---|---|
| CIリント時間(100K行) | 90.4秒 | 0.28秒 |
| 管理ツール数 | 3〜5本 | 1本 |
| 設定ファイル | 複数 | pyproject.toml 1本 |
| ルールカバレッジ | 限定的 | 900以上 |
移行手順まとめ:
pip install rufforuv add --dev ruffpyproject.tomlに設定を追加(ignore = ["ISC001"]を忘れずに)ruff check --fix .で既存問題を自動修正- CI/CDに
ruff check --output-format=github+ruff format --checkを組み込む - VS Code拡張機能(charliermarsh.ruff)で保存時自動フォーマット
最大のハマりポイント(再掲):ISC001ルールはフォーマッターと競合します。ignore = ["ISC001"] を必ずpyproject.tomlに追加してください。
2026年現在、uvとRuffの組み合わせはPython開発のデファクトスタンダードになっています。移行コストは設定15分。今日から試してみてください。
本記事はアフィリエイトリンクを含みます。リンク経由でサービスを利用いただくと、ブログ運営の支援になります。