- Twilio Segment, yüzlerce mikroservisten oluşan yapıyı işletirken karmaşıklık ve bakım yükü nedeniyle tek hizmete (monolitik) geçti
- Başlangıçta her destination API’sini ayırarak hata izolasyonu ve ölçeklenebilirlik sağladı, ancak hizmet sayısı 140’ın üzerine çıkınca operasyonel overhead hızla arttı
- Çok sayıdaki repository ve paylaşılan kütüphaneyi yönetmek zorlaştı ve test/dağıtım sırasında tüm hizmetleri etkileyen sorunlar ortaya çıktı
- Bunu çözmek için Centrifuge sistemi ve monorepo yapısı devreye alındı, test otomasyonu için de Traffic Recorder geliştirildi
- Sonuç olarak geliştirme hızı ve kararlılık önemli ölçüde arttı; Twilio Segment bugün üretkenlik ve operasyonel verimlilik için monolitik yapıyı koruyor
Mikroservislerin benimsenmesi ve sınırları
- Twilio Segment, müşteri veri altyapısı için mikroservis mimarisini benimsedi; her amaca özel hizmet bağımsız olarak event işleyecek şekilde tasarlandı
- Yüzlerce server-side destination’a (ör. Google Analytics, Optimizely vb.) veri iletildi
- Başlangıçta tek bir kuyruk kullanıldı, ancak belirli bir destination arızalandığında tüm sistemde gecikmeye yol açan head-of-line blocking sorunu ortaya çıktı
- Bunu çözmek için her destination için ayrı hizmet ve kuyruk oluşturuldu; böylece hata izolasyonu ve bağımsız ölçekleme sağlandı
- Ancak hizmet sayısı arttıkça operasyonel karmaşıklık ve bakım maliyeti hızla yükseldi; bu da geliştirme hızının düşmesine ve hata oranının artmasına yol açtı
Ayrı repository’ler ve paylaşılan kütüphanelerin sorunları
- Her destination farklı API formatları kullandığı için özel dönüşüm kodu gerekiyordu
- Başlangıçta bunlar tek bir repository’de yönetiliyordu, ancak test başarısızlıkları herkesi etkilediği için repository ayrımı yapıldı
- Sonrasında 50’den fazla yeni destination eklenince 50’den fazla repository ortaya çıktı
- Ortak işlevler için paylaşılan kütüphaneler kullanıldı, ancak sürüm uyumsuzlukları ve dağıtım yükü büyüdü
- Hizmetlerin yük desenleri farklı olduğundan otomatik ölçekleme ayarları zorlaştı ve bazı durumlarda operatörlerin elle müdahalesi gerekti
Monolitik dönüşüm ve Centrifuge’un devreye alınması
- 140’tan fazla hizmetin tek bir hizmette birleştirilmesine karar verildi
- Ayrı kuyrukların yerine tüm event’leri tek hizmete ileten Centrifuge sistemi geliştirildi
- Centrifuge daha sonra Twilio Segment’in Connections backend altyapısına dönüştü
- Tek hizmet yapısına geçişle birlikte operasyonel yükün azaltılması ve arıza müdahalesinin sadeleştirilmesi sağlandı
Monorepo ve test otomasyonu
- Tüm destination kodu tek bir repository altında birleştirildi ve 120’den fazla bağımlılık tek sürümde standartlaştırıldı
- Sürüm yönetimi sadeleşti ve bakım verimliliği arttı
- Test otomasyonu için Traffic Recorder devreye alındı
- Gerçek HTTP istek ve yanıtları kaydedilip tekrar oynatılarak dış ağ bağımlılığı ortadan kaldırıldı
- Test süresi dakikalardan milisaniyelere indi ve kararlılık büyük ölçüde arttı
- Test başarısızlık oranı düştü, geliştirici üretkenliği belirgin biçimde iyileşti
Monolitik yapının etkileri ve trade-off’ları
- Tek hizmete geçildikten sonra dağıtım hızı ve geliştirme verimliliği ciddi şekilde arttı
- 1 yılda paylaşılan kütüphane iyileştirme sayısı 32’den 46’ya çıktı
- Tek bir mühendis birkaç dakika içinde dağıtım yapabilir hale geldi
- Operasyonel verimlilik de iyileşti; ani yük artışları büyük worker pool’larla karşılanabildi
- Ancak hata izolasyonunun zorlaşması, cache verimliliğinin düşmesi ve bağımlılık güncellemelerinde risk gibi dezavantajlar da var
- Bu kayıpların bir kısmı operasyonel sadelik ve üretkenlik artışıyla telafi edildi
Sonuç
- Mikroservisler başlangıçtaki performans sorunlarını çözdü, ancak büyük ölçekli genişleme ve toplu güncellemeler için uygun değildi
- Monolitik dönüşüm, hem operasyonel kararlılığı hem de geliştirme hızını iyileştirdi
- Başarılı bir geçiş için sağlam bir test sistemi ve trade-off’ları kabullenme şart
- Twilio Segment bazı altyapılarda hâlâ mikroservis kullanıyor, ancak server-side destination’lar için monolitik yapı daha uygun görülüyor
2 yorum
Her şeyi parçalara ayırıp normalleştirmek riskli gibi görünüyor.
Hacker News görüşleri
Tüm hedeflerin kodunu tek bir repo içinde toplayınca, bunları tek bir hizmette birleştirebildiler
Sonuç olarak geliştirme verimliliği büyük ölçüde arttı. Artık paylaşılan bir kütüphanede her değişiklik yaptıklarında 140'tan fazla hizmeti dağıtmaları gerekmiyor
Tek bir mühendis artık birkaç dakika içinde dağıtım yapabiliyor
Eğer bir kütüphane değişikliği yüzünden tüm hizmetleri yeniden dağıtmanız gerekiyorsa, bu gerçek bir hizmet değil, dağıtık monolitik yapıdır
Paylaşılan kütüphaneleri tüm hizmetlerde zorunlu olarak senkronize etme fikri, hizmet mimarisi felsefesiyle baştan çelişiyor
Bu, “her kütüphane güncellemesinde her şeyi yeniden dağıtmak”tan çok Amazon tarzı bir ortak build ve dağıtım sistemine benziyor
Kütüphaneler merkezi olarak yönetilen tek bir kaynaktan alınıyor ve sürümler farklıysa uyumluluk sorunları nedeniyle herkesin migration yapması gerekiyor
Güvenlik açığı nedeniyle belirli bir sürümü kaldırmak gerektiğinde toplu yeniden dağıtım gerekebilir, ama merkezi yönetimin faydası çok daha büyük
Bu tür sistemler hâlâ mikroservis olarak sınıflandırılır, ancak maliyet ve operasyonel verimlilik açısından paylaşımlı bir ortam gibi çalışır
Buna dağıtık monolit demek aşırı bir yorum olur
Mikroservis desenini izledikçe dağıtım riski artıyor ama başta bu pek görünmüyor
Örneğin para ile ilgili bir kütüphanedeki hatayı düzelttiyseniz, gerçekte tüm hizmetleri yeniden dağıtmanız gerekip gerekmediğini sorgulamaya başlarsınız
Güvenlik açığı olan bir kütüphane, sistem tasarımından bağımsız olarak tamamen değiştirilmelidir
Böyle durumlarda monolitik yapı aslında daha kolay yönetilir
Gerçek mikroservislerde hizmetler mesaj alışverişi yapmalı ve JSON kullanmalıdır
Kodun kendisini değil yalnızca API'yi bilmek yeterli olmalıdır. Ancak o zaman herkes bağımsız olarak dağıtım yapabilir ve ölçeklenebilir
Paylaşılan modüller kullanmak daha mantıklı değil mi?
Önceki şirketimde her şey mikroservis olarak çalışıyordu, ondan önceki şirkette ise AWS serverless vardı
Her iki durumda da en büyük sorun hizmetler arası iletişimdi. Sözleşmeleri (contracts) senkronize etmek zordu ve dağıtımlar da karmaşıktı
Başlangıçta hızlı ilerliyorduk ama zamanla karmaşıklık patladı. Korkuya dayalı geliştirme ortaya çıktı ve toplantılar aşırı arttı
Şu anki şirketim monolitik yapı kullanıyor ve yönetmesi çok daha kolay. Tipler net, refactoring basit
Kendi platformumuz üzerinde kurulu yapay zeka ajanlarının kod tabanı içinde kendilerini iyileştirdiğini görmek ilginç
Tek dezavantaj uzun build süreleri ama araç zincirindeki gelişmeler sayesinde 2026'da 10 kat daha hızlı dağıtım bekliyorum
Benim vardığım sonuç şu: monolitik yapı sayesinde çok daha hızlı büyüyebildik ve ölçeklenebildik
Monolitik yapıda sorumlulukların ayrımı sürekli bozuluyordu ve ekipler arası bağımlılık çok yüksekti
Gerçek hız ve ölçek ancak ekipler ayrıştığında mümkün oldu
ORM'den DTO'ya geçmek için 2 yıl, 50 ekip ve 150'den fazla kişi gerekti
Bu kadar karmaşık bir iş, mikroservisler olmasaydı mümkün olmazdı
Bu yazıya bakınca, meselenin özü mikroservis vs monolitik gibi teknik bir tercih değil
Asıl konu mühendislik organizasyonunun kalitesi ve yapısı
Kod deposu ve test yapısı, organizasyonun seviyesini olduğu gibi yansıtıyor
“Bunu yapmayalım” diyebilecek kimse yoksa karmaşıklık patlıyor
Ekibin durup düşünmesini sağlayacak yetkili bir lider gerekli
API sorunları çıktığında kök nedeni analiz etmek yerine sadece veriyi düzeltip ticket'ı kapatıyorlardı
Aynı sorun tekrar etse bile temel neden çözülmüyordu
Sadece mülakat yaparak bile bir şirketin kod tabanının yapısını belli ölçüde tahmin edebilecek noktadayım
Bu aslında tam bir monolite dönüşten çok hâlâ bir SOA yapısı
Sadece hizmetlerin kapsamı büyümüş
Eğer 140 hizmet tek bir ekip tarafından yönetiliyorsa, SOA hizmetleri ölçeklemek için değil ekipleri ölçeklemek için kullanılan bir yapıdır
Tek bir ekip tüm paylaşılan kütüphaneleri yönetirse sürüm uyumsuzlukları ve API karmaşası ortaya çıkar
Sonuçta mimariyi organizasyon yapısı belirler. Burada olan şey, tek bir ekibin karmaşıklığı azaltmak için sistemi birleştirmesi
Bu “monolit” değil, ekip ölçeğine uygun şekilde kapsamı ayarlanmış bir hizmet seviyesi
Bence en ideal yapı bu. Ekip büyüdüğünde tekrar ayrılması gerekir
Mikroservis savunucusu değilim ama “monorepo vs mikroservis” şeklindeki sahte ikilik göze çarpıyor
Çok fazla araç, hizmet ile repo arasında 1:1 ilişki varsayıyor
Oysa her şeyi tek bir repo içinde tutup yine de bağımsız dağıtım yapmak mümkün
GitHub gibi yerlerde bunu klasör bazında bağımsız hizmetler olarak ele alabilmek güzel olurdu
Bazel ile bağımlılık ağacını yönettik ve
bazel queryile etkilenen target'ları bulup testleri otomatik çalıştırdıkGitHub Actions ile entegre ederek PR'ları engelleyen bir iş akışı oluşturduk
İyi çalıştı ama kurulumu aylar sürdü
Gerçek sorun aslında operasyon ve araç eksikliğiydi — CI, auto-scaling, on-call düzeni; hepsi yetersizdi
Her iki yaklaşım da başarısız olabilir
Node.js veya Python gibi ortamlarda event loop'un kaldırabileceği kod miktarının bir sınırı var
6-8 kişinin 200 hizmet yönettiği de oldu, 80 kişinin tek bir monoliti yönettiği de
Mikroservisler küçük değişikliklerde avantajlı ama sistem çapındaki değişikliklerde zorlayıcı
Monolitik yapı ise bunun tam tersi
Sonuçta önemli olan mimarinin kendisi değil, soyutlama, test ve gevşek bağlılık yöntemleri
Mikro olmanın ölçütü teknoloji değil, iş birimidir
Bunun daha altına inerseniz nanoservis olur
Beam, JVM, Rust, Go gibi ortamlarda bu zaten çözülmüş bir sorun
Bu bir CPU cache meselesi mi?
Genelde Go, Java veya C# kullanıldığını sanıyordum
Çoğu şirkette mikroservisler aslında sorunların %90'ının kaynağıydı
AWS, Google, Netflix gibi dev organizasyonlar değilseniz uygun değil
Sistemi baştan birleştirilebilir parçalara ayırmak zaten yeterince zor; üstüne bir de ağ sınırları eklemek akıllıca değil
Bir sonraki trendin React ve SPA'den uzaklaşıp sunucu merkezli yapıya dönüş olacağını düşünüyorum
Mikroservislere geçiş gerekçesinin “testler sık sık bozuluyordu” olması, bana çok ters bir yaklaşım gibi geliyor
Testler bozuluyor diye kod tabanının yapısını tamamen değiştirmek garip
Ekip bazında ayrı VM'ler ve CI/CD ayarları tanımlayınca test çakışmaları ortadan kalktı
Dezavantajı, özellikler arası çakışmaları anında fark edememekti ama kod sahipliği net olduğu için büyük bir sorun olmadı
Başlığa [2018] eklenmesi yönünde bir istek vardı
“Testler bozulunca alakasız kodları da düzeltmek zorunda kalıyorduk” diye repo'ları ayırmışlar ama,
testlerin çalıştırılma şeklini değiştirmek ya da manuel dağıtıma izin vermek gibi başka çözümler de olabilirdi
Repo ayrımı tek çözüm değildi