🪝

Gitフック「Lefthookの勝ちデース」:マルチ言語・マルチリポジトリ環境におけるHusky vs Lefthook徹底比較

に公開

おはようございます、こんにちは、こんばんは。
スペースマーケットでWebエンジニアをしています、s0arです。

タイトルがオチなんですけど一旦全部読んでください。

個人開発のプロジェクトでGitフックの管理しようとして、Claude Codeに聞いたら「Lefthookがええやで~」って言われました。
ほな使ってみるやで~ってことで、せっかくだから、俺はこのメモを書くぜ!

この記事 is 何

Gitフックマネージャーとして広く使われているHuskyと、マルチ言語環境に強いLefthookを比較します。

結論から言うと、マルチ言語・マルチリポジトリ環境ではLefthookの勝ちデース

「え、Huskyでええやん」って思う人もいるかもしれへん。
Rails、iOS、Android、Flutterのリポジトリにもpackage.jsonを追加する覚悟があるなら無理にとは言わん。
お前がそう思うんやったらそうなんやろ、ワイは嫌やけど(変則関西式島)

前提:どんな環境を想定しているか

この記事では以下のような環境を想定しています:

  • 複数の独立したリポジトリ(モノレポではない)
  • 多様な技術スタック(Rails、NestJS、Next.js、iOS、Android、Flutter)
  • チーム全体でコード品質を統一したい
  • パフォーマンスとセットアップの容易さを重視

Huskyしか勝たんという皆様、それで全然OK。ブラウザバックしてOKデース。
でも数日後、ここに戻ってきてください、きっとHuskyのここがすごい!的な反論記事が書かれているでしょう。
(私の手によって)(見事なマッチポンプ)

逆にちょっとでも「ぼ、僕にできるかなぁ?」と思った方はこのメモの通りにすれば勝てマース。

言語・フレームワーク非依存性:最も重要な判断基準

複数の技術スタックを持つ環境において、この観点が最も重要な判断基準になります。

リポジトリ Node.js環境 Husky Lefthook
Ruby on Rails なし(通常) npm/package.json必須 gem install lefthook
NestJS あり 利用可 npm経由で利用可
Next.js あり 利用可 npm経由で利用可
iOS (Swift) なし npm/package.json必須 brew install lefthook
Android (Kotlin) なし npm/package.json必須 brew/aptで利用可
Flutter なし(通常) npm/package.json必須 brew/aptで利用可

Huskyの根本的な問題点は、インストールと実行にNode.js環境が必須である点です。

これ、何が困るかって言うと:

  • RailsプロジェクトにJavaScriptの依存関係が混入する(なんか…嫌やん…)
  • iOS/Android開発者にNode.jsのインストールを強制する(モバイルエンジニアさんキレない?大丈夫そ?)
  • 各リポジトリで異なるランタイム要件が発生する(這い寄る混沌)

一方、Lefthookは各言語のエコシステムに自然に統合できます:

# Ruby開発者(Railsリポジトリ)
gem install lefthook

# Node.js開発者(NestJS/Next.jsリポジトリ)
npm install lefthook --save-dev

# iOS開発者(Swiftリポジトリ)
brew install lefthook

# Android開発者(Kotlinリポジトリ)
brew install lefthook  # または apt install lefthook

# Flutter開発者
brew install lefthook

はえ〜、インストール方法が選べるの便利杉内
てかもうbrewだけでいいんじゃないかな

パフォーマンス比較:Lefthookが圧倒的優位

パフォーマンスの差は実測データで明確に示されています。

5つの並列タスク実行において、Lefthookは約1秒で完了するのに対し、逐次実行のHuskyは約5.15秒を要しました。

指標 Husky Lefthook
実行方式 逐次実行(bash経由) 並列実行(ネイティブ)
依存関係 lint-staged併用で約1,500パッケージ追加 単一バイナリ、依存なし
起動時間 shell初期化が必要 即時起動

Huskyの5倍速い
どっかの赤いやつでも3倍やからね?

Lefthookの並列実行は設定で簡単に有効化できます:

pre-commit:
  parallel: true  # これだけで並列実行が有効に
  commands:
    lint:
      run: yarn lint
    test:
      run: yarn test

parallel: trueを1行追加するだけ。簡単すぎて涙そうそう。

セットアップと設定管理

両ツールとも初期セットアップは数分で完了しますが、複数リポジトリ運用においてLefthookの統一フォーマットが大きな利点となります。

Huskyのアプローチ

.husky/ディレクトリ内に個別のシェルスクリプトを配置する方式です。フックが増えるとファイル数も増加します:

# Huskyセットアップ(Node.jsリポジトリのみ)
npm install --save-dev husky
npx husky init
# .husky/pre-commit, .husky/pre-push等のファイルを個別作成

Lefthookのアプローチ

単一のYAMLファイルで全フックを一元管理します。どの言語のリポジトリでも同じlefthook.ymlという構造で設定できます:

# lefthook.yml - 全設定がここに集約
pre-commit:
  parallel: true
  commands:
    lint:
      glob: "*.{ts,tsx}"
      run: yarn eslint {staged_files}
    format:
      run: yarn prettier --write {staged_files}
      stage_fixed: true

pre-push:
  commands:
    test:
      run: yarn test

設定ファイルが1つにまとまってるの、管理しやすくてグーなのです。

各言語向け設定テンプレート

複数リポジトリ運用では、言語ごとのテンプレートを用意することで導入がスムーズになります。

Ruby on Rails用
# lefthook.yml
pre-commit:
  parallel: true
  commands:
    rubocop:
      glob: "*.rb"
      run: bundle exec rubocop -A {staged_files}
      stage_fixed: true
    erb-lint:
      glob: "*.erb"
      run: bundle exec erblint {staged_files}

pre-push:
  commands:
    rspec:
      run: bundle exec rspec
NestJS / Next.js用
# lefthook.yml
pre-commit:
  parallel: true
  commands:
    eslint:
      glob: "*.{ts,tsx,js,jsx}"
      run: yarn eslint --fix {staged_files}
      stage_fixed: true
    prettier:
      glob: "*.{ts,tsx,js,jsx,json,md}"
      run: yarn prettier --write {staged_files}
      stage_fixed: true
    typecheck:
      run: yarn tsc --noEmit

pre-push:
  commands:
    test:
      run: yarn test
iOS (Swift)用
# lefthook.yml
pre-commit:
  parallel: true
  commands:
    swiftlint:
      glob: "*.swift"
      run: swiftlint --fix {staged_files}
      stage_fixed: true
    swiftformat:
      glob: "*.swift"
      run: swiftformat {staged_files}
      stage_fixed: true
Android (Kotlin)用
# lefthook.yml
pre-commit:
  parallel: true
  commands:
    ktlint:
      glob: "*.kt"
      run: ktlint -F {staged_files}
      stage_fixed: true
    detekt:
      glob: "*.kt"
      run: ./gradlew detekt
Flutter用
# lefthook.yml
pre-commit:
  parallel: true
  commands:
    format:
      glob: "*.dart"
      run: dart format {staged_files}
      stage_fixed: true
    analyze:
      run: flutter analyze

pre-push:
  commands:
    test:
      run: flutter test

どの言語でもlefthook.ymlという同じ形式で設定できるの、チーム全体で一貫性が保ててグーなのです。

チーム利用:リモート設定継承

複数リポジトリ構成で特に有効なのが、Lefthookのリモート設定継承機能です。

共通のフック設定を専用リポジトリで管理し、各プロジェクトから参照できます:

# 各リポジトリのlefthook.yml
remotes:
  - git_url: https://github.com/your-company/shared-hooks
    ref: main
    configs:
      - common.yml      # 共通ルール
      - rails.yml       # Rails固有(必要に応じて)

# ローカル設定で上書き・追加も可能
pre-commit:
  commands:
    project-specific:
      run: ./scripts/custom-check.sh

shared-hooksリポジトリの構成例:

shared-hooks/
├── common.yml          # commit-msg検証など共通ルール
├── rails.yml           # Rubocop等のRails向け設定
├── typescript.yml      # ESLint/Prettier等のTS向け設定
├── swift.yml           # SwiftLint等のiOS向け設定
├── kotlin.yml          # ktlint等のAndroid向け設定
└── flutter.yml         # dart format等のFlutter向け設定

これ、チーム全体で一貫したコード品質基準を維持しながら、各プロジェクト固有の設定も柔軟に追加できるんですよね。爆アド。

ローカルオーバーライドも可能で、個人の開発環境でのみ特定のフックをスキップしたい場合に便利です:

# lefthook-local.yml(.gitignore対象)
pre-commit:
  exclude_tags:
    - slow-tests

その他の比較ポイント

lint-stagedとの関係

Huskyはlint-stagedとの併用が事実上の標準となっています。
一方、Lefthookは{staged_files}globによる組み込みのファイルフィルタリングがあり、lint-staged不要です。

# Lefthookなら組み込みでステージングファイル対応
commands:
  lint:
    glob: "*.ts"
    run: eslint {staged_files}

lint-staged分の依存が減るの、地味にうれC。

コミュニティ規模

ドキュメントとコミュニティの規模ではHuskyが優位です。
週間ダウンロード数はHusky: 約1,900万、Lefthook: 約71万と大きな差があります。

ただし、Lefthookは Evil Martians による積極的なメンテナンスが継続しており、Discourse、GitLab、Yandexなどの大規模プロジェクトでの採用実績もあります。
GitLabが使ってるなら安心感は、ありまぁす!

クロスプラットフォーム対応

両ツールともWindows/Mac/Linuxをサポートしていますが、Lefthookはプラットフォーム別のネイティブバイナリを提供するため、より安定した動作が期待できます。

まとめ

要件 Husky Lefthook 勝者
複数リポジトリ構成 Node.js必須 言語別インストール可 Lefthook
パフォーマンス 逐次実行 並列実行 Lefthook
セットアップ容易性 Node.jsリポジトリのみ 全言語で統一フォーマット Lefthook
フレームワーク非依存 Node.js依存 完全非依存 Lefthook
チーム利用 大きなコミュニティ リモート設定共有 Lefthook

マルチ言語・マルチリポジトリ環境では、Lefthookが唯一の現実的な選択肢です。
いや、流石に過言かも?

でも、Huskyを選んだ場合、Rails/iOS/Android/Flutterの各リポジトリにpackage.jsonを追加して、開発者全員にNode.jsのインストールを強制することになります。
それはちょっと…ほら…ねえ…

推奨する導入アプローチ

  1. 共通フック設定を管理する専用リポジトリを作成
  2. 各言語向けのテンプレート(上記参照)を用意
  3. 各リポジトリでLefthookをインストールし、リモート設定を継承
  4. プロジェクト固有の設定はローカルで追加

この構成により、チーム全体で一貫したコード品質を維持しながら、各技術スタックの特性に合わせた柔軟な運用が可能!たぶんおそらくmaybe
Lefthook、良さげじゃんね?

以上終わりです。

参考リンク

GitHubで編集を提案
スペースマーケット Engineer Blog

Discussion