Husky + lint-stagedでpre-commit時にコード品質を自動チェックする仕組みを作った話

コミット後のCIでフォーマット崩れや lint エラーが頻発していたプロジェクトに Husky と lint-staged を導入し、コミット前に自動チェックする仕組みを整えました。

🙌 結論から

コミット後にCIで lint エラーやフォーマット崩れが見つかって、「またやり直しか…」となる回数が、Husky と lint-staged の導入でほぼゼロになりました!

pre-commit のタイミングでステージしたファイルだけを自動でチェック・整形してくれるので、コミットした時点で品質がある程度保証されます。

チーム開発でレビュアーが「インデントが揃ってない」と指摘する手間も減って、さりげなく効果が大きい仕組みでした(^^)

💡 以前の問題

以前のプロジェクトでは、ESLint や Prettier の実行をCIに任せきりにしていました。

その結果、ローカルではエラーに気づかずコミット・プッシュしてしまい、CIが落ちてから慌てて修正することがよくありました。

# CI でようやく気づくパターン
git push origin feature/xxx
# → CI落ちる → 手元で lint 実行 → 修正 → 再プッシュ

この「CIで落ちてから気づく」というサイクルは、レビュー待ちの時間も無駄に伸ばしてしまいます。

レビュアー側も、本質的でないフォーマットの指摘にコメントを使うのは正直あまり気持ちのいいものではなく…

👀 Husky + lint-staged を導入する

まずは Husky と lint-staged をインストールします。

npm install -D husky lint-staged
npx husky init

package.json に lint-staged の設定を追加します。

{
  "lint-staged": {
    "*.{ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ]
  }
}

.husky/pre-commit に lint-staged を実行する1行を書くだけで設定は完了です。

npx lint-staged

これで git commit を実行するたびに、ステージされたファイルだけを対象に lint と整形が自動で走るようになります。

全ファイルではなくステージ済みファイルだけが対象なので、大きなリポジトリでも実行時間が気にならないのがうれしいポイントです🙌

✨ 導入してみた感想

導入してすぐに感じたのは、レビューのコメント数が減ったことです。

フォーマットやちょっとした lint 違反はコミット時点で自動修正されるので、レビュアーはロジックの妥当性に集中できるようになりました!

もちろん pre-commit のチェックに時間がかかりすぎると、コミットのたびにストレスを感じてしまうので、対象を --staged のファイルに絞っておくのは重要だと感じます。

たまに「--no-verify でチェックをスキップしたい」場面もありますが、これは緊急時以外は使わないルールをチームで決めておくと安心かも?

CIでのチェックも残しておけば、pre-commit をすり抜けたケースの最後の砦になってくれます。

⚡ Lefthookという速い選択肢も検討した

Husky + lint-staged の構成に満足していたんですが、チームメンバーから「大きいリポジトリだとコミットのたびに待たされる」という声が出て、代替の Lefthook も試してみました。

Lefthook は Go 製のGitフック管理ツールで、フックを並列実行できるのが最大の特徴です。

# lefthook.yml
pre-commit:
  parallel: true
  commands:
    lint:
      glob: "*.{ts,tsx}"
      run: eslint --fix {staged_files}
    format:
      glob: "*.{ts,tsx,json,md}"
      run: prettier --write {staged_files}

Husky + lint-staged は各チェックを順番に実行するのに対して、Lefthook は lintformat を同時に走らせられます。

大きめのリポジトリで比較したところ、体感でコミットにかかる時間がかなり短くなった印象でした(・∀・)

設定ファイルも lefthook.yml の1つにまとまるので、.husky/pre-commitpackage.jsonlint-staged フィールドに分かれていた設定が1箇所にまとまるのも意外と嬉しいポイントです。

ただ、Husky のほうがエコシステムとしての情報量が圧倒的に多く、チームに新しく入るメンバーへの説明のしやすさも含めると、まだ移行に踏み切れていません。

「速さを取るならLefthook、実績と情報量の安心感を取るならHusky」というのが、今のところの私の結論です。新規プロジェクトなら一度Lefthookも比較検討する価値があると思います。

パパ系Webエンジニア〜愛用ガジェット一覧 🛍️

この記事を書いている作業環境のガジェットを楽天ROOMにまとめています ✨ 在宅エンジニアのデスク作りの参考にどうぞ 🙌

👍 まとめ

Husky と lint-staged の組み合わせは、**「コミット前にステージ済みファイルだけを自動チェックする」**というシンプルな仕組みながら、効果はかなり大きいです!

CIで落ちてから気づく手戻りが減るだけでなく、レビューでの本質的でない指摘も減らせます。

まだCIだけに lint を任せているプロジェクトがあれば、pre-commit フックを仕込んでみる価値は十分にあると思います(^o^)/

※ 各ツールのバージョンや機能は今後も更新されます。最新情報は Husky公式ドキュメント をご確認ください👀