monorepo って何が良いんだっけ?
これは何
- monorepo に触れる機会があったので、 monorepo について改めてまとめておくメモ
monorepo について
おおよそ Monorepo Explained の内容
- monorepo とは関連する 複数の プロジェクトを管理する 1つ のコードリポジトリのこと
- 1つ のコードリポジトリで複数のライブラリ・アプリのコードを共存して管理・開発する
- monorepo は単なるコード置き場ではない
- 複数のプロジェクトが入ったリポジトリでも、それらのプロジェクトに明確な関係が無い場合、それは monorepo とは呼ばない
- 同じ用に、リポジトリが巨大なアプリを含んでいて、個別のパーツ分割やカプセル化が無ければ、それはただの大きなリポジトリであって monorepo では無い
- このようなリポジトリは大抵モノリシックであり、 monorepo とは別
- 優れた monorepo はモノリシックとは正反対である
- polyrepo: monorepo の反対
- 現在のアプリ開発における標準的な方法
- チーム、アプリ、プロジェクトごとにリポジトリを作成すること
- 各リポジトリには、単一のビルドアーティファクトとシンプルなビルドパイプラインがあるのが一般的
- 業界が polyrepo スタイルに移行した理由
- チームは、どのライブラリを使うか、いつアプリやライブラリをデプロイするか、誰が自分たちのコードに貢献したり使用したりできるか、自分たちで決定したい
- これは良いことだが、なぜ monorepo が必要なのか
- この自律性は孤立によって得られるもので、孤立はコラボレーションに害を及ぼすため
polyrepo の欠点
- 面倒なコード共有
- リポジトリ間でコードを共有するには、共有するコードのリポジトリを作成することになる
- ツールや CI 環境をセットアップし、コミッターをリポジトリに追加し、他リポジトリが依存できるようにパッケージパブリッシングをセットアップしなければならない
- リポジトリ間で互換性の無いサードパーティライブラリのバージョンを調整するのが大変
- コードの重複が多い
- 誰も共有リポジトリをセットアップする手間をかけたくないので、各チームは共通のサービスやコンポーネントの実装をそれぞれのリポジトリに書く
- これは初期段階の時間が無駄になるだけでなく、コンポーネントやサービスが変更されるたびに、メンテナンス、セキュリティ、品質管理の負担が増加する
- 共有ライブラリやコンシューマへのクロスリポジトリ変更にコストがかかる
- 共有ライブラリの重大なバグや破壊的な変更が発生した場合を考える
- 開発者は、リビジョン履歴が切り離された複数のリポジトリにまたがって変更を適用するための環境をセットアップする必要がある
- パッケージのバージョン管理やリリースの調整作業についても大変
- 一貫性のないツール
- 各プロジェクトは、テストの実行、ビルド、サービング、リンティング、デプロイなどに独自のコマンドセットを使用する
- これらは一貫性が無いため、プロジェクトごとにどのコマンドを使うかを覚えるというオーバーヘッドが発生する
monorepo の良さ
- 新しいプロジェクトを作成するオーバーヘッドが少ない
- 既存の CI セットアップを使用し、すべてのコンシューマーが同じリポジトリにあれば、バージョン管理されたパッケージを公開する必要は無い
- プロジェクト間のアトミックコミット
- コミット毎に全てが連動する
- 同じコミットで全てを修正すれば、ブレークチェンジが存在しない
- 全てのバージョンを 1つ に
- サードパーティライブラリの競合するバージョンに依存しているプロジェクトのために、非互換性を心配する必要が無い
- 開発者の機動性
- 異なるツールやテクノロジーを使って書かれたアプリのビルドとテストを一貫した方法で行うことが出来る
- 開発者は自信を持って他チームのアプリに貢献し、自分の変更が安全であることを確認できる
monorepo の機能
もちろん monorepo ツールによっては、サポートしていない機能もある
Fast
- Local computation caching
- タスクのファイルやプロセス出力を保存し、再生する機能
- 同じマシン上で、同じものを 2度 作ったりテストしたりしない
- Local task orchestration
- タスクを正しい順序で並行して実行する機能
- リストアップされたツールは、より限定的な Lerna を除いて、全てほぼ同じ方法でこれを行うことが出来る
- Distributed computation caching
- 異なる環境間でキャッシュアーティファクトを共有できる
- これは CI エージェントを含む組織全体が、同じものを 2度 ビルドしたりテストしたりすることが無いことを意味する
- Distributed task execution
- 1台 のマシンでコマンドを実行する際の人間工学をほぼ維持したまま、多くのマシンにコマンドを分散することが出来る
- Transparent remote execution
- ローカルで開発しながら、複数マシンで任意のコマンドを実行できる
- Detecting affected projects/packages
- 変更によって影響を受ける可能性のあるものを特定し、影響を受けるプロジェクトのみをビルド・テストする
Understandable
- Workspace analysis
- 余分な設定無しにワークスペースのプロジェクトグラフを理解できる
- Dependency graph visualization
- プロジェクトやタスク間の依存関係を可視化する
- グラフ内のノード検索、フィルタリング、非表示、フォーカス・ハイライト、クエリーが可能
Manageable
- Code sharing
- バラバラのソースコードの共有を容易にする
- Consistent tooling
- プロジェクト開発に使用するプログラミング言語に関係なく、一貫したエクスペリエンスを得るのに役立つ
- このツールは異なるテクノロジーを同じように扱う
- 例えば package.json や js/ts ファイルを分析して JavaScript プロジェクトの依存関係や、ビルド・テストの方法を把握することが出来る
- しかし Cargo.toml ファイルを解析して Rust と同じことをしたり、 Gradle ファイルを解析して Java と同じことをしたりする
- これにはツールがプラグイン可能である必要がある
- プロジェクト開発に使用するプログラミング言語に関係なく、一貫したエクスペリエンスを得るのに役立つ
- Code generation
- コード生成をネイティブにサポートする
- Project constraints and visibility
- リポジトリ内の依存関係を制約するルールの定義をサポートする
- 例えば、開発者は一部のプロジェクトを自分のチームだけのものとしてマークし、他の誰も依存できないようにすることが出来る
- 開発者は、使用されている技術に基づいてプロジェクトをマークし、バックエンドのプロジェクトがフロントエンドのプロジェクトをインポートしないようにすることも出来る
monorepo を構築するツール例
まとめとか感想とか
- monorepo は関連する複数のプロジェクトを 1つ のリポジトリでまとめる手法
- 各々でリポジトリを作るよりも、ライブラリの共有や CI/CD のセットアップを共通化できる
- 組織内でのコラボレーションを促進するという文化醸成的な良さ
- 大量のリポジトリが存在する環境だと、この機能・ライブラリってどのリポジトリで開発してるやつだっけ?みたいになることがあるので、リポジトリの数は少ないほうが覚えることが少なくて良い
- 一方で monorepo は銀の弾丸ではない
- アプリ開発のスピード感の違いから、ライブラリのアップデートに追いつけずに開発スピードを逆に落としてしまうといった事例もある
- CI/CD のセットアップも共通化できない場合もある
- 身も蓋もないが、 monorepo を採用するかはケースバイケースで要検討
- 採用する技術スタックや、開発していくサービスやツールの特性、組織のマネジメント状況などを加味して検討する感じになると思う
- 個人的には、いちいちリポジトリ作っていて、あれはどのリポジトリだっけ・・・?という場面が多々あったので、まとめちゃって良さそうなものは monorepo でやっていこうかなと思った
- npm を使用するプロジェクトを扱うなら npm workspaces がシンプルで良く、 npm を使用しない場合は Nx か Turborepo が良さそうに感じた