Yazılım•Enes Ünlüer

Turborepo'ya Geçişin 7. Ayı: Kuralları Repo'nun Sahibi Yaptık

Turborepo geçişinin yedi aylık gelişimini gösteren ölçüm ve süreç özeti

Şubat ayında Turborepo'ya geçiş sürecini ve bu karardan beklentilerimizi yazmıştım. O yazı geçiş planını anlatıyordu. Yedi ay sonra ise daha basit bir soruya dönmek istedim: Bu yapı ekibin günlük işini gerçekten kolaylaştırdı mı?

Kısa cevap: Evet. Fakat beklediğimiz her nedenle değil.

Monorepo tek başına düzen getirmedi. Asıl farkı; ekipçe belirlediğimiz sınırlar, tek teslimat akışı ve zamanla otomatik hale getirdiğimiz kurallar yarattı.

Ölçüm çerçevesi

Bu değerlendirmeyi ana geliştirme hattındaki git geçmişi ve statik kaynak taraması üzerinden yaptım. Rakamlar kişileri değerlendirmek için değil, yapının ekip çalışmasına etkisini görmek için kullanıldı.

Ölçüm Değer
İncelenen dönem 256 gün
Aktif geliştirme günü 208
Ana hattaki toplam commit 3.718
Doğrudan kod commiti 2.448
Merge edilen PR 1.123
Katkı veren kişi 16

Branch geçmişi, kişi bazlı performans, iç sistem adları ve iş ortaklarına ilişkin bilgiler ölçümün dışında tutuldu. Böylece çalışma, repo'nun ne içerdiğinden çok nasıl davrandığına odaklandı.

Yedi ay sonra kısa tablo

Beklenti Sonuç Gözlenen sinyal
Ürün geliştirme kesintiye uğramasın Geçiş kademeli ilerledi Günlerin %81,25'inde aktif geliştirme vardı
Değişikliğin etkisi aynı yerde görülsün Tek PR akışı çalıştı Commitlerin %19,61'i birden fazla workspace'e dokundu
Ortak kod tekrarları azalsın API ve UI katmanları öne çıktı Paylaşımlı importların %95,4'ü iki ana katmanda toplandı
Teknik borç kendiliğinden azalsın Gerçekleşmedi Sayaçlar ancak otomatik kurallardan sonra düşmeye başladı

Bu tablo bana bir altyapı kararını yalnızca kurulumun tamamlanmasıyla değerlendirmemek gerektiğini hatırlattı. Önemli olan, yapının aylar sonra ekip davranışını nasıl etkilediği.

Geçişi ayakta tutan şey kademeli ilerlemekti

Geçişi büyük bir yeniden yazım olarak ele almadık. Mevcut ürün çalışmaya devam ederken uygulamaları ve ortak paketleri adım adım aynı yapıya taşıdık.

Bu yaklaşımın iki faydası oldu:

  1. Ürün işleri altyapı çalışmasını beklemedi.
  2. Tasarladığımız sınırları gerçek kullanım içinde sınayabildik.

256 günlük pencerenin 208 gününde geliştirme devam etti. Tam ayların commit medyanı 438,5; son tam ayda ise 716 commit vardı. Bu sayıları üretkenlik puanı olarak değil, altyapı çalışmasının teslimat akışını durdurmadığını doğrulayan bir sinyal olarak okuyorum.

Bazı ilk varsayımlarımız değişti. Örneğin her ortak ihtiyacın ayrı bir pakete dönüşmesi gerekmediğini gördük. Ekip, domain tiplerini API katmanına yakın tutan daha sade bir desen geliştirdi. Başlangıçtaki şemayı korumak yerine çalışan deseni standartlaştırdık.

Repo ownership'ını üstlenirken burada önemli olan, ilk tasarımı savunmak değil; ekip için daha anlaşılır ve sürdürülebilir olan çözümü görünür hale getirmekti.

Gerçek kazanç tek çalışma akışı oldu

Başlangıçta CI süresi ve cache verimliliği önemli hedeflerdi. Bunlar hâlâ değerli, ancak günlük çalışmada en belirgin kazanım başka yerde oluştu: tek değişiklik için birden fazla repoda ayrı PR açma ihtiyacı ortadan kalktı.

Artık ortak bir değişiklik:

  • İlgili uygulama ve paketlerle aynı PR içinde görülebiliyor.
  • Tek review akışında değerlendiriliyor.
  • Etkilenen uygulamalar otomatik olarak belirleniyor.
  • Birbirinden kopuk yayın süreçleri yerine aynı bağlamda ilerliyor.

Özellikle Next.js tabanlı uygulamalarda SSR ve SEO gibi birden fazla katmana dokunan değişikliklerde bu görünürlük, araç seviyesindeki hız kazanımından daha değerli oldu.

Ana hattaki 2.448 doğrudan commitin 480'i birden fazla workspace'e dokunuyordu. Bu oran dönemin başındaki %12,5 seviyesinden son tam ayda %26,7'ye yükseldi. Yani ekip, zaman içinde ortak katmanlarla daha sık ve aynı değişiklik bağlamı içinde çalışmaya başladı.

Kurallar iş akışına girdiğinde anlam kazandı

İlk aylarda eski bağımlılıkların ve geçici istisnaların zamanla azalacağını düşündük. Azalmadılar. Yeni yapı hazır olsa bile günlük teslimat baskısı ekibi doğal olarak çalışan eski yola yönlendirdi.

Çözüm daha fazla dokümantasyon yazmak değildi. Kuralları iş akışının parçası yaptık:

  • Yeni legacy importlar lint seviyesinde engellendi.
  • Mevcut istisnalar tek bir listede görünür hale getirildi.
  • Liste yalnızca küçülebilen bir borç bütçesi olarak ele alındı.
  • Kontroller PR tamamlanmadan önce çalışan bir kalite kapısına bağlandı.

Böylece kural, bir kişinin hatırlatmasına bağlı olmaktan çıktı. Review görüşmesi “bu standardı uygulamalı mıyız?” sorusundan “mevcut istisnayı nasıl azaltırız?” sorusuna döndü.

Bir kural iş akışında karşılık bulmuyorsa, zamanla yalnızca iyi niyetli bir not haline geliyor.

Otomatik kuralların devreye alınmasından sonraki ilk 30 günlük görünüm de yönün değiştiğini gösterdi:

Borç göstergesi Başlangıç 30 gün sonra Değişim
Muafiyet listesi girdisi 116 96 −%17,2
Suppression taşıyan dosya 80 62 −%22,5
any kullanımı 157 99 −%36,9

Otuz gün uzun vadeli sonuç çıkarmak için yeterli değil. Ancak kuralların yalnızca yazılı olduğu dönemle, otomatik olarak uygulandığı dönem arasındaki farkı görmek için anlamlı bir erken sinyal verdi.

Paylaşımlı katmanları kullanım şekillendirdi

İlk planda birden fazla ortak paket vardı. Yedi ay sonunda yükün büyük bölümünü API ve UI katmanlarının taşıdığı görüldü. Daha küçük paketlerin bazıları beklediğimiz kadar anlamlı bir sorumluluk kazanmadı.

Toplam 2.244 paylaşımlı importun %95,4'ü iki ana katmanda toplandı. Bu dağılım, paket sayısını artırmanın tek başına paylaşılabilirlik sağlamadığını; asıl değerin ekip tarafından düzenli kullanılan sınırlar oluşturmakta olduğunu gösterdi.

Buradan çıkan karar yeni paketler üretmek değil, sınırları sadeleştirmek oldu:

  • Gerçekten ortak olan kod paylaşımlı katmanda kalmalı.
  • Tek bir domain'e ait tipler o domain'in API sözleşmesine yakın durmalı.
  • Kullanılmayan soyutlamalar sırf ilk planda yer aldı diye korunmamalı.
  • Bağımlılık yönleri araçlarla denetlenmeli.

Mimariyi ekibin kullanımından bağımsız düşünmemek gerekiyor. İyi sınır, diyagramda simetrik görünen değil; değişiklik yaparken karar yükünü azaltan sınır.

TypeScript göçü bir sonuçtu, hedef değil

Bu dönemde TypeScript oranı belirgin biçimde yükseldi:

Dönem TypeScript dosya oranı
İlk değerlendirme %39,58
Üç ay sonra %46,59
Yedinci ay %75,31

Ancak bu artışı tek başına başarı ölçüsü olarak görmüyorum. Dosya silmek, taşımak veya yeniden düzenlemek de oranı değiştirebilir. Bu nedenle oranı teslimat sürekliliği ve borç sayaçlarıyla birlikte değerlendirdik.

Asıl değer, yeni kodun hangi sözleşmeye uyacağının netleşmesi ve eski alanlara dokunulduğunda güvenli biçimde iyileştirilebilmesi oldu. Bu yaklaşımın kod güvenliği tarafını TypeScript ile Tip Güvenliği yazısında daha ayrıntılı ele almıştım.

Repo ownership'ını üstlenirken çıkardığım notlar

Yedi ayın sonunda yanımda kalan beş prensip var:

  1. Geçiş, ürün akışından ayrı bir dönem olmamalı. Küçük ve geri alınabilir adımlar daha sağlıklı ilerliyor.
  2. Sınırlar insan hafızasına bırakılmamalı. Lint, CI ve bağımlılık kontrolleri ortak kararları koruyor.
  3. Ölçüm davranışı açıklamalı. Commit veya dosya sayısı tek başına kaliteyi göstermiyor.
  4. İlk tasarım değişebilir. Ekibin bulduğu daha sade desen, planlanan yapıdan daha değerli olabilir.
  5. İyi ownership tek kişiye bağımlılık üretmez. Kararlar görünür ve otomatik olduğunda sahiplik ekip içinde güvenle dağılabilir.

Sonuç

Turborepo geçişinin en kalıcı çıktısı klasör yapısı veya cache ayarı olmadı. Ortak değişiklikleri aynı bağlamda görebilmek ve ekip kararlarını otomatik kontrollerle koruyabilmek oldu.

Bugün aynı geçişe başlasak task graph'ından önce sınırları, istisna bütçesini ve kalite kapılarını tanımlardık. Çünkü monorepo'yu sürdürülebilir yapan şey, herkesin aynı kişiyi takip etmesi değil; herkesin aynı açık kurallarla çalışabilmesi.

Bir sonraki yazıda bu temelin üzerinde Clean Architecture ile Turborepo'yu birlikte nasıl konumlandırdığımızı ele alacağım.