Turborepo, Nx, Lerna ve Yarn Workspaces gibi araçlar monorepo yönetimini kolaylaştırır; ancak bundle boyutu ve performans bütçesi yönetimi için özel bir mekanizma sunmazlar. Bu boşluğu doldurmak, ekibin bilinçli bir strateji kurmasını gerektirir. Build cache stratejisi, bağımlılık grafiği görünürlüğü ve CI'da otomatik boyut kontrolü bu stratejinin üç temel bileşenidir.

Shared package'ların bundle'a etkisi görünmez kalır

Monorepo'da bir packages/ui gibi ortak bileşen kütüphanesi oluşturmak yaygın bir pratiktir. Bu kütüphaneyi kullanan her uygulama, kütüphanenin tamamını ya da tree shaking sonrası kalan bölümünü kendi bundle'ına dahil eder. Sorun şu: kütüphane büyüdükçe — yeni bileşenler, yeni bağımlılıklar eklendiğçe — tüm bağımlı uygulamaların bundle'ı otomatik olarak büyür. Bu büyüme çoğu zaman gözden kaçar çünkü hiçbir uygulamanın kendi kaynak kodu değişmemiştir; değişen yalnızca ortak bir bağımlılıktır.

Tree shaking bu sorunu kısmen çözer; kullanılmayan export'lar atılır. Ancak tree shaking'in etkinliği, kütüphanenin nasıl yapılandırıldığına bağlıdır. Barrel export — yani bir index.ts dosyasından tüm bileşenleri toplu olarak re-export etmek — tree shaking'i zayıflatır. Tüm bileşenler tek bir giriş noktasından dışa aktarıldığında, bundler bağımlılık ilişkilerini doğru analiz edemeyebilir ve kullanılmayan kodları bundle'a dahil edebilir. Shared package'ların her bileşeni ayrı dosya olarak export etmesi ve sideEffects: false ile yapılandırılmış bir package.json içermesi bu riski azaltır.

Bağımlılık grafiğini görünür kılmak için bundle analizi her uygulama için ayrı ayrı yapılmalıdır. Bir uygulamanın bundle'ında beklenmedik büyük bir shared package görünüyorsa, bu paketin içerdiği hangi bağımlılıkların gerçekten kullanıldığını sorgulamak gerekir. Çoğu durumda az kullanılan bir özellik için tüm bir kütüphaneyi çekmek sorunu yaratan unsurdur.

Ortak vendor chunk stratejisi uygulamalar arası cache verimliliğini etkiler

Monorepo'daki uygulamalar genellikle aynı npm paketlerini kullanır: React, lodash, date-fns, belki aynı UI kütüphanesi. Bu paketler her uygulamanın build'inde ayrı ayrı chunk'a dahil edildiğinde, tarayıcı her uygulama için bu kütüphaneleri yeniden indirmek zorunda kalır. Aynı kullanıcı monorepo içindeki farklı bir uygulamaya geçtiğinde ortak paketlerden yararlanamamak cache verimliliğini düşürür.

Bu soruna yönelik bir strateji, ortak bağımlılıkları ayrı bir vendor chunk'ta toplamak ve tüm uygulamaların bu chunk'ı paylaşmasını sağlamaktır. Ancak bu yaklaşım, uygulamaların aynı domain altında servis edildiği durumda anlamlıdır; farklı domain'lerde çalışan uygulamalarda tarayıcı cache'i zaten izole çalışır. Webpack'in SplitChunksPlugin yapılandırması veya Vite'ın manualChunks seçeneği bu ayarı yapmanıza olanak tanır. Ama dikkat: aşırı granüler chunk bölünmesi, HTTP istek sayısını artırarak kazanımı tersine çevirebilir. Code splitting kararları burada da belirleyicidir.

Monorepo'da ortak bağımlılık sürümlerinin tutarlılığı da bundle boyutunu doğrudan etkiler. Farklı workspace'lerin aynı paketin farklı sürümlerini kullanması, her sürümün bundle'a ayrı ayrı dahil edilmesi anlamına gelir. Bu durum özellikle büyük kütüphanelerde ciddi boyut artışına yol açar. Yarn'ın resolutions alanı veya npm'in overrides özelliği bu sorunu yönetmek için kullanılabilir; ancak sürüm sabitleme kararları dikkatli yapılmalıdır, çünkü uyumsuz API'lere zemin hazırlayabilir.

Build cache stratejisi yeniden hesaplama maliyetini azaltır

Monorepo'nun en büyük avantajlarından biri build cache'tir. Turborepo ve Nx, değişmeyen paketlerin build çıktısını yeniden hesaplamak yerine cache'ten sunar. Bu mekanizma doğru yapılandırıldığında CI süresini önemli ölçüde kısaltır. Ancak performans açısından dikkat edilmesi gereken nokta şudur: cache isabeti gerçek bir build çalıştırmaz; dolayısıyla bundle boyutu değişikliği de tespit edilmez.

Bu boşluğu kapatmak için her pakette bağımsız bir boyut denetimi yapılması gerekir. Örneğin her uygulamanın package.json'ına bir size-limit yapılandırması eklemek, CI'da o paketin çıktısı için bir üst sınır tanımlar. Bu araç, build çıktısını analiz eder ve belirlenen eşiği aşarsa pipeline'ı başarısız olarak işaretler. Böylece bir shared package değişikliği bir uygulamanın bundle'ını şişirdiğinde, CI bunu otomatik olarak yakalar. Versioned asset stratejisi ile birleştirildiğinde bu yaklaşım, her deploy'da bundle boyutunun denetlenmesini sistemin parçası haline getirir.

Her uygulamanın ayrı performans bütçesi olmalıdır

Monorepo'daki uygulamalar farklı profillere sahiptir. Bir pazarlama sayfasının başlangıç bundle'ı ile bir yönetim panelinin kabul edilebilir boyutu aynı değildir. Tek bir global boyut bütçesi bu farkı yönetemez; her uygulamanın kendi kriterleri olması gerekir.

Performans bütçesi yalnızca toplam bundle boyutunu değil, Core Web Vitals hedeflerini de kapsamalıdır. LCP için kritik yükleme süresi, INP için etkileşim gecikmesi ve CLS için düzen kayması eşikleri uygulama başına tanımlanabilir. Bu hedeflerin CI'da otomatik olarak denetlenmesi için Lighthouse CI veya benzer araçlar kullanılabilir. Her deploy öncesi bir temel ölçüm çalıştırılır; mevcut değerlerin belirlenmiş eşiği aşması pipeline'ı durdurur.

Pratikte bu sistemi kurmak zaman alır ve bakım gerektirir. Bütçe eşiklerinin zaman içinde güncellenmesi, yeni uygulamalar eklendiğinde yapılandırmanın genişletilmesi ve CI cache davranışıyla uyum sağlanması sürekli dikkat ister. Ancak bu yatırımın karşılığı açıktır: performans regresyonları kullanıcıya ulaşmadan yakalanır. Monorepo büyüdükçe bu güvence daha değerli hale gelir, çünkü her değişikliğin tüm uygulamalar üzerindeki etkisini manuel olarak izlemek imkansızlaşır.

Bağımlılık görünürlüğü olmadan regresyon kaçınılmazdır

Monorepo'da bir paketin başka paketleri nasıl etkilediğini anlamak için bağımlılık grafiğinin görünür olması gerekir. Turborepo'nun turbo run build --graph komutu, hangi paketin hangisine bağlı olduğunu ortaya koyar. Nx ise proje grafiğini görsel bir arayüzle sunar. Bu görünürlük hem geliştirme hem de hata ayıklama açısından kritiktir.

Bir shared package'a yapılan değişikliğin hangi uygulamaları etkilediğini bilmeden bundle boyutu denetimi eksik kalır. Örneğin packages/utils paketine yeni bir bağımlılık eklenmesi, bu paketi kullanan beş uygulamanın bundle'ını değiştirebilir. Build cache devrede olduğunda bu değişiklik yalnızca etkilenen paketlerin yeniden build edilmesiyle görülür; ancak boyut denetimi her etkilenen uygulama için ayrıca çalıştırılmalıdır.

Dynamic import kullanımı monorepo bağlamında özellikle değerli hale gelir. Shared package'tan gelen büyük bir modülün tüm uygulamalarda senkron olarak yüklenmesi yerine, ihtiyaç duyulduğunda asenkron olarak çekilmesi başlangıç bundle'ını küçültür. Bu yaklaşım özellikle az kullanılan özellikler için etkilidir: sadece belirli sayfalarda görünen grafik kütüphaneleri, admin paneline özgü bileşenler ya da isteğe bağlı özellik modülleri bu kategoriye girer.

Monorepo'da frontend performansı korumak, bir kez kurulan bir sistem değil; büyüyen kod tabanıyla birlikte sürekli güncellenmesi ve rafine edilmesi gereken, ekip kültürüyle bütünleşik bir pratiktir. Shared package sınırlarını net çizmek, her uygulamanın bağımsız performans bütçesini açıkça tanımlamak ve CI pipeline'ında otomatik boyut ve metrik denetimi sağlamak bu pratiğin üç temel direğidir. Bu direkler olmadan performans regresyonları sessizce birikir ve fark edildiğinde geri dönmek için çok daha fazla çaba gerekir.

Geliştirme sürecinde bir diğer önemli konu, shared package'ların nasıl yayınlandığıdır. Bazı ekipler bu paketleri npm'e publish eder; bazıları ise workspace referansıyla doğrudan birbirine bağlar. Npm tabanlı yayın akışında her paket için semantic versioning uygulanır ve bağımlı uygulamalar belirli bir sürümü tüketir. Bu yaklaşım sürüm geçmişini temiz tutar ancak monorepo'nun en büyük avantajlarından biri olan "anlık değişiklik yansıması"nı ortadan kaldırır. Workspace referansında ise her değişiklik anında tüm bağımlı uygulamalara yansır; bu hız avantaj gibi görünse de beklenmedik bundle değişikliklerine zemin hazırlar.

Monorepo'da performans disiplinini sürdürmenin pratik bir yolu, shared package'lara yönelik değişiklikler için ayrı bir review süreci tanımlamaktır. Yalnızca tek bir uygulamayı etkileyen bir değişiklik ile tüm monorepo'yu etkileyen bir shared package değişikliği aynı kritere tabi tutulmamalıdır. Bağımlılık grafiği görünür olduğunda — Turborepo veya Nx araçlarıyla — ekip hangi değişikliğin kaç uygulamayı etkilediğini sayısal olarak görebilir. Bu bilgi, code review önceliğini ve test kapsamını doğrudan etkiler.

Yeni bir uygulama monorepo'ya eklendiğinde performans bütçesinin sıfırdan tanımlanması önemlidir. Mevcut uygulamaların bütçe eşiklerini kopyalamak cazip görünebilir; ancak her uygulamanın kullanıcı profili, sayfa yapısı ve işlevsel gereksinimleri farklıdır. Bir e-ticaret uygulamasının ürün sayfası için belirlenen LCP hedefi, kurumsal bir raporlama arayüzü için yanlış bir referans noktası olur. Bütçeyi gerçek kullanıcı davranışından hareketle belirlemek — hangi sayfalar en çok ziyaret alıyor, hangi metrikler en çok şikayet üretiyor — soyut eşik değerlerinden daha etkili bir stratejidir.