Monorepo Yönetiminde Asıl Zorlanılan Yerler
Monorepo bir dizin düzeni değil, bir araç yatırımı kararıdır. Tek depoya geçmek kod paylaşımını kendiliğinden kolaylaştırmaz; kolaylaştıran şey, geçtikten sonra kurmak zorunda kaldığınız build ve test altyapısıdır. Zor kısım depoları birleştirmek değil, birleşme sonrasında derleme ve test maliyetinin depo boyutuyla değil değişiklik boyutuyla artmasını sağlamaktır.
Monorepo neyi çözer, neyi çözmez
Çözdüğü asıl problem atomik değişikliktir. Ortak bir kütüphaneyi değiştirip onu kullanan on beş servisi aynı commit içinde güncelleyebiliyorsanız, sürüm uyumsuzluğu diye bir kategori masadan kalkar. Aynı işi ayrı depolarda yapmak sürüm yayınlamayı, bağımlılıkları yükseltmeyi ve on beş ayrı PR'ı takip etmeyi gerektirir.
Çözmediği şey sahipliktir. Tek depo, kodun kime ait olduğunu bulanıklaştırmaz ama netleştirmez de. CODEOWNERS benzeri bir sahiplik haritası yoksa monorepo, herkesin her yere dokunabildiği ve hiçbir değişikliğin doğru kişiye düşmediği bir yere döner. Depoyu birleştirmeden önce yapılacak iş budur, sonra değil.
Sparse checkout ile partial clone aynı şey değil
Bu ikisi sürekli birbirinin yerine anılıyor, oysa farklı maliyetleri kesiyorlar. git clone --filter=blob:none dosya içeriklerini indirmeyi ihtiyaç anına erteler, yani geçmişin ağırlığını azaltır. git sparse-checkout ise çalışma ağacında hangi dizinlerin açılacağını sınırlar, yani disk kullanımını ve dosya sistemi taramalarını azaltır. Geçmişi şişmiş ama ağacı küçük bir depoda birincisine, on binlerce dosyalık ağaçta ikincisine ihtiyacınız olur; gerçekten büyük depolarda ikisi birden açılır.
Depo boyutu paniği çoğu ekipte gereğinden erken başlıyor (birkaç gigabaytlık bir depo için sanallaştırılmış dosya sistemi kurmayı fazla buluyorum). Önce klonlama ve durum sorgulama sürelerini ölçün; rakam yoksa çözülecek bir performans sorunu da yok.
Hedefli build, bağımlılık grafiği kadar doğrudur
"Yalnızca değişeni derle" tavsiyesi doğrudur ve sessiz bir varsayım taşır: build sisteminin bağımlılık grafiği, gerçek bağımlılıkları eksiksiz biliyordur. Bir hedef, build dosyasında bildirmediği bir kaynağı okuyorsa grafik eksiktir.
Eksik grafiğin cezası derleme hatası değil, yanlış yeşildir. Değişen dosyaya bağlı olduğu bilinmeyen hedef yeniden derlenmez, testi hiç çalışmaz, CI yeşil yanar, hata canlıda görünür. Bazel ve Buck gibi araçların derlemeyi sandbox içinde çalıştırmasının tek sebebi budur: bildirilmemiş erişimi sessiz bir riskten gürültülü bir hataya çevirmek. Sandbox'ı kapatmak build'i hızlandırır, aynı hamlede grafiğin doğruluk garantisini de kapatır. Uzak build cache açıksa hasar tek makinede de kalmaz, yanlış artifact bütün ekibe dağıtılır.
Bu yüzden "monorepo'da tek dile inin" tavsiyesini de fazla kolaycı buluyorum. Çok dilli build grafiği pahalıdır, doğru. Ama şirketin tamamı zaten tek dil kullanıyorsa monorepo'nun getirisi en düşük olduğu noktadadır; tavsiye, problemi çözmek yerine monorepo'ya geçme gerekçesini siliyor.
Test seçimi ve flaky test aritmetiği
Depo büyüdükçe her PR'da tüm test paketini koşturmak imkânsız hale gelir, dolayısıyla testleri bağımlılık grafiğinden seçmek zorunludur. Seçimin doğruluğu da yine grafiğe bağlıdır; yukarıdaki sorun burada ikinci kez faturalanır.
Flaky testlerde otomatik tekrar yaygın bir refleks ama ne yaptığını görmek gerekiyor. Bir testin rastgele kırılma olasılığı p ise, üç denemenin üçünün birden kırılma olasılığı p³'tür. p = 0,3 gibi bariz bozuk bir testte bile boru hattı yaklaşık yüzde 97 oranında yeşil verir. Tekrar, flaky oranını düşürmez, yalnızca görünmez kılar. Tekrar sayısını raporlayın ve karantinaya alınan her testin bir sahibi ile son tarihi olsun; ikisi olmayan karantina, silinmiş test demektir.
Merge queue ne zaman ters çalışır
Ana dala giden PR'ları teker teker sıraya alıp test etmek doğru sonucu verir ve saatteki PR kapasitenizi doğrudan CI süresine kilitler. Bunu aşmak için kuyruklar toplu koşar: n adet PR birlikte test edilir, yeşilse hepsi girer, kırmızıysa ikili aramayla suçlu bulunur ve yaklaşık log₂(n) ek koşu harcanır.
Toplu koşunun kazandığı yer, ana dalın nadiren kırıldığı yerdir. Kırılma sık ise her parti bisect'e döner, ek koşular birikir ve toplu kuyruk seri kuyruktan yavaş çalışmaya başlar. Yani parti boyutunu büyütmek, CI'sı istikrarsız ekipte durumu iyileştirmez, kötüleştirir. Önce kırmızı oranını düşürün, parti boyutu sonraki iş.
Atomik commit, atomik dağıtım değildir
Monorepo'nun sürüm uyumsuzluğunu ortadan kaldırdığı iddiası derleme zamanı için doğru, çalışma zamanı için yanlıştır. Tek commit'te değişen on beş servis canlıya on beş ayrı anda çıkar; o aralıkta eski ve yeni sürümler birlikte çalışır. Şema ve kontrat değişiklikleri bu yüzden geriye dönük uyumlu olmak, yani iki aşamada yürümek zorundadır: önce yeni alanı okuyabilen sürüm, sonra onu yazan sürüm.
Bunu atlayan ekipler monorepo'yu suçluyor, oysa sorun dağıtım sırasının kendisinde. Depo düzeni, dağıtımın dağıtık olduğu gerçeğini değiştirmiyor.
Kaynak