自分は今は社内 Monorepo での作業がメインで、たまに Android とかさわってる。 レポジトリの壁というか、レポジトリの違いを含むインフラの違いの壁は、組織の壁より厚い。 この話は前にも書いたことがある。 だから向井さんの言っていることはよくわかる。 Monorepo が強制するインフラ共通化が押し下げた組織の壁の低さを、しばしば実感する。
たとえば最近だと、仕事でやっている Android アプリの APK のビルド方法が変わった際にビルドツールチェインにあるマイナーなバグにあたってしまい、 そのツールのバグを直したことがあった。そんなツールがあるとは知らなかったというくらい降って湧いた話。 でもビルドシステムが統一されているおかげでコードをビルドするのもテストするのも簡単で、 IDE も普段の設定そのまま。コードレビューもいつもと同じ。 はじめてのコードベース、レビュー相手のこともそのチームのこともなにもしらないが、つつがなくしごとが片付いた。
一方、自分が仕事で関わっている電話機の、仕事で関わっているカメラ固有の機能 (HAL) のコードを直したいとなるとすごい大変。
まったく別カルチャー (Android) の、わけのわからないビルドシステムの罠をくぐり抜け、コーディング規約ふくめ全然違うコードを睨み、
いじっていたブランチが間違っていることに気づき、コードレビューをアップロードする方法もわからず、
そもそも git rebase
ってどうやるんだっけ・・・みたいになる(最後のは自分が悪い)。
組織的には隣接チームだしレビュー相手も面識あるけど、そういうの関係なくつらい。
Monorepo のいやなところ 1 - 統一されすぎ
と利点は享受しつつ、個人的にいまの Monorepo はそんなに好きじゃない。
好きでない理由のひとつめは、自分のいるプロジェクトであるモバイルアプリが、Monorepo 住民のメインストリームではないこと。 この Monorepo のメインストリームは C++ なり Java なり Go なりで RPC のサーバを作る人々である。 JS (今は TS) なフロントエンドも、まあまあ歴史がある。それらと比べるとモバイルアプリ勢は人口も少なく歴史も浅い。
Monorepo の結果として利用を強いられるビルドシステムや CI/CD などのインフラもそれを反映している。 たとえば Bazel というビルドシステムは、モバイルで必須のクロスコンパイルがいまいち得意でない。 ホスト側でツールをビルドして、そのツールを使ってコードを生成して、それをデバイス向けにコンパイルみたいなのが、 できるけどぎこちない。Bazel の全体としての洗練度を考えるとぎこちなさが際立つ。 コードレビューツール付随の静的解析も、Android で使えない Java の API とかを勧めてくる。知らん。 CI/CD も毎週毎日バイナリをプッシュするサーバの人々向けにごく短いブランチ寿命を想定している。 自分のやってるアプリとか二ヶ月に一回もプッシュしないのでブランチも長生きで大量に cherrypick する。 CI ツールの Web UI が爆発気味。
マイノリティである一番の象徴として、自分のチームは CI でアプリのバイナリが入ったコンテナイメージをビルドしている。 コンテナイメージをビルドすると色々よろしくやってくれるインフラに便乗するためだが、わけがわからん。
厳密には Monorepo イコールインフラ統一ではないけれど、インフラ統一のしやすさが Monorepo の利点なのも事実。 しかしインフラがプロジェクトの多様性をカバーしきれないとマイノリティーは割を食いがち。 これでも今はだいぶマシになった方で、 5 年前とかはもっとだいぶひどかったという。 (五年前の自分は Monorepo の外にいる Android の many repo に住み Gradle でビルドする普通のアプリを書いていた。これはこれで全然よくなかったが、また別の機会に。)
Monorepo のいやなところ 2 - オープンソースとの相性悪すぎ
GitHub 世代以降のオープンソースプロジェクトは、小さなレポジトリをいくつもつくって依存管理ツールで寄せ集めるスタイルが主流。 商業的なソフトウェア開発も GitHub を使うようになった結果、CI や CD 含めソフトウェア開発のインフラが細粒度のレポジトリを想定するようになった。 コンテナイメージ単位でレポジトリがある、というと言いすぎだろうか。Monorepo はそういう既存のインフラを使えない。想定が違いすぎる。だからなにかと再発明が必要。
再発明しがちなのはインフラに限らず、コードもオープンソースを使いにくい。Google の Monorepo は自己完結を原則にしており、オープンソースのライブラリを使いたいなら許可を得た上でレポジトリの所定の場所にコピーをコミットしないといけない。バージョンをあげるたびにコピーしなおす。がんばれば一定程度はツールで自動化できるとはいえ、めんどくさい。
既存のオープンソースを使うのもめんどくさいが、書いたコードをオープンソースにするのも超めんどくさい。 今度は逆に Monorepo のコードを GitHub などにコピーしないといけない。 いくつかのオープンソースプロジェクトは、一旦コピーしたオープンソース側を upstream にすることで Monorepo → GitHub へのコピーを一度で済ませている。以降は GitHub 側で開発し、それを定期的に Monorepo にコピーする。コピー作業自体は Copybara というツールである程度は自動化できるらしいけど、 社内の開発インフラはまったく使えなくなる。かといってそのへんの SaaS をサクサク使わせてくれるとも限らない。チームまるごとくらいの規模があればなんとか運用できるけど、たとえばいちプログラマが書いたライブラリをいっちょ公開すっかと思っても壁が高い。GitHub メインにするのはほぼ無理。こうして社内からバーンとコピーされたまま力尽き死に絶えるオープンソースプロジェクトが後を絶たない。
TensorFlow にいたっては Monorepo でバンバン開発しつつ GitHub にもコミュニティからのコミットがガンガン入てくる。 だからコードのコピーが双方向におこる。カオス。
矯正ギプスかレガシーか
Monorepo は企業ソフトウェア開発のベストプラクティスを自然と実現してしまう矯正ギプスとして機能した。ツール・インフラの統一、ライブラリバージョンの統一、ビルドの自己完結性、ソースコードの可視性、変更のアトミック性などなど。こういうのは理論上は Monorepo でなくても実現できるけれど、Monorepo にしておくとすごいラクで、むしろ逆らうのが難しい。
GitHub を中心としたオープンソースのエコシステムがソフトウェア開発を席巻する 2010 年代以前、この矯正ギプスはほぼ手放しで正解だったと思う。 けれど Monorepo の対局にある GitHub 的なソフトウェア開発が広まるにつれ、そのエコシステムと相容れられない Monorepo の良さには陰が射した。
少し前に出た “Software Engineering at Google” という本では Monorepo と Manyrepo の利点欠点を比較し、いちばん重要なのはライブラリバージョンの統一 “One Version Rule” であって、これをはじめとする様々なベストプラクティスを実現できるなら別に Monorepo である必要はないと書いている。そしてバージョン管理は小さなレポジトリを Monorepo の原則に従って寄せ集める “Federated Monorepo” になるだろうと締めくくっている。
オープンソース的、というか GitHub/Git 的に federated なソフトウェア開発は open loop で、Monorepo で実現されていた完璧さは失われる。 破壊的変更に備え依存関係を全部書き直して回ることも、ライブラリのバージョンを統一しきることもできない。 組織のどこかにセキュリティホールを残したライブラリがあっても気づかないままかもしれない。 Monorepo 信奉者がこの雑さ、不完全さを嫌っているのは先の本を読むとよくわかる。 ツールやポリシーの力でこの不完全さを取り除き Monorepo の完全さを取り戻そうとするのが Federated Monorepo のビジョンだと理解している。
けれどそれってあまりに Monorepo 原理主義的な視野狭窄ではなかろうか。 Open loop な依存関係がもたらす不完全さを受け入れ、やんわり付き合っていくのがエコシステムの中で生きるというじゃないの。拒絶するんじゃなくて。 たとえば GitHub の Dependabot は自己完結な One Version Rule なしにオープンな依存関係と付き合うのを助けてくれる。こうしたツールを所定の向きに進歩させていけば 人類は Federate Monorepo の夢に近づけると思うけれど、そう厳しく取り締まらずもうちょっと loosely coupled かつ decentralized にやってこうやというのが GitHub 以降のオープンソースが世に問うたメッセージだと個人的には解釈している。
GitHub はもともと大企業的/閉鎖的なソフトウェア開発に嫌気が差した人々が民主的で個人主体なオープンソースの風を求めて集う場だったわけだから、 これはある意味で意図通りの帰結と言える。GitHub は Monorepo 的 elitism に戦いを挑み、勝利を収めつつあるように見える。 Monorepo はもはやレガシーなのではないか。
あゆみよる
とはいえエコシステム主体のソフトウェア開発が企業内ソフトウェア開発のスケール全てを飲み込みきれたとは思わないし、 Monorepo 世代の価値観から生まれたアプローチにはまだ汲み取れることはあると思う。
たとえばブランチに頼らず master で開発を続ける trunk-based development は Monorepo と相性が良い。外部の依存関係が古いままだといくらアプリがインクリメンタルなリリースでリスクを減らしても依存関係のアップデートという巨大リスクを排除できないが、依存関係がぜんぶ Monorepo の中にあって常に最新ならそういう big bang update のリスクはなくなる。
Manyrepo の、というかレポジトリの境界をまたいだ trunk-based development のバリエーションに Living on Edge がある。ライブラリのリリースをまたず、 常に trunk/master からコードを持ってきて使う。GitHub も Ruby on Rails の最新スナップショットを毎週更新しながら使うようになったとちょっと前に明かしている。
新しい世代のソフトウェア開発は、時代の風にのりつつ、同時にこうして前の世代の良いところを cherrypick しつつ、育っていくんじゃないかな。 Monorepo 勢も次の世代の良さを学んでちょっと若返ってほしいもんです。 Monorepo 勢であるはずの Facebook の GitHub をみると社内で使ってると思しき大小様々なインフラコードが活発に開発されていて、なんらかの良いバランスを発見したように見える。どうやってんだろうねえ。
Two-pizza team の Microservices で GitHub OSS してるかずよしさん的にはどうですか。