BFF ile Mobil API Sürümleme: İstemciyi Geri Alamadığınızda Ship
Mobil binary geri alınamaz ve eski sürümler kalıcıdır; güvenlik ve hız sunucuya kayar: BFF, tüketici güdümlü sözleşmeler ve geriye dönük uyumlu sürümleme.
Sunucuda kötü bir deploy saniyeler içinde geri alınabilir; mobilde ise ship edilen binary kalıcıdır, app store incelemesi sizin kontrolünüz dışındadır ve eski istemci sürümleri aylarca kullanımda kalır. Bu asimetri, release stratejinizi belirlemesi gereken tasarım kısıtıdır: istemci, sistemin geri alamayacağınız tek parçasıdır; bu yüzden hem güvenlik hem hız, değiştirebildiğiniz tarafa kaymak zorundadır. Bu kısıttan doğan kollar şunlar: bir backend-for-frontend katmanı, bağımsız deploy için tüketici güdümlü sözleşmeler, geriye dönük uyumlu API evrimi ve server-driven UI’ın değerini nerede kanıtladığına dair net bir sınır.
Asimetrik Saat
Bir mobil ürünü iki saat yönetir ve bunlar farklı hızlarda çalışır. Sunucu saati hızlı ve geri alınabilirdir: production’da yakalanan bir regresyon, saniyelerle dakikalar arasında ölçülen bir redeploy ya da rollback uzaklığındadır. İstemci saati ise yavaş ve tek yönlüdür. Bir binary kullanıcıların eline geçtiğinde onu geri çağıramazsınız; iOS’ta kullanıcılar bilinen iyi bir eski sürüme geri dahi inemez. Düzeltme, app store incelemesinden geçmek ve sonra kullanıcıların kurmayı seçmesini beklemek zorundadır.
İnceleme sınırlıdır ama kontrol edilemez. Apple, ortalama olarak gönderimlerin %90’ının 24 saatten kısa sürede incelendiğini, birkaç güne uzayabilen bir kuyruk olduğunu belirtiyor. Bu, paydaşlara söz verebileceğiniz bir release zamanı değil, faydalı bir planlama girdisidir. Problemin daha yavaş yarısı ise adopsiyon. Yeni bir release’in benimsenmesi kademeli ve gönüllüdür; büyük bir mobil OS ship edildikten aylar sonra bile kurulu tabanının yalnızca üçte ikisi kadarına ulaşır ve aynı şekil uygulama güncellemeleri için de geçerlidir. Pratik çıkarım kesin bir eğri değil, yönseldir: eski istemciler haftalardan aylara kalır ve trafiğin anlamlı bir kısmı her zaman çok önce yayınladığınız sürümlerden gelir.
İstemci saati hızlandırılamadığı ya da geri alınamadığı için güvenlik kollarının sunucu tarafında yaşaması gerekir. Minimum sürüm kapısı (minimum-version gating) ve uzaktan kill switch’ler tam da bu nedenle yaygın bir uygulamadır: incelemeyi beklemeden bozuk bir özelliği devre dışı bırakmanıza ya da en eski istemciler için güncellemeyi zorlamanıza izin verirler. Forced-update’i genelde başvurabileceğiniz bir araç olarak görün; store’un her zaman onaylayacağı bir garanti olarak değil. Asıl köklü hamle ise yapısaldır. Değiştiremediğiniz parça binary ise, mümkün olduğunca az davranışın onun içine kilitlenmesi için tasarlayın.
BFF Katmanı
İlk yapısal kol, bir backend-for-frontend (BFF): her kullanıcı deneyimi için bir backend, UI’ı sahiplenen ekibin sahiplendiği bir backend. Sam Newman bu pattern’i, tek bir deneyime adanmış ve hem istemci hem sunucu bileşenlerinin release’ini hizalama sürecini basitleştiren bir backend olarak tanımlıyor. Kılavuz, her tüketici için tek bir ortak gateway değil, “bir deneyim, bir BFF”. Pattern, SoundCloud’da Phil Calçado’nun ekibi tarafından ortaya kondu; API gateway kataloğu da aynı fikri her istemci türü için ayrı bir API gateway olarak çerçeveler.
BFF burada önemli çünkü sunucu tarafı değişimi mümkün kılan sınırdır. Uygulama, UI ekibinin kontrol ettiği bir backend’le konuştuğunda, aksi halde binary içinde donmuş kalacak kararlar (hangi alanların gösterileceği, yanıtın nasıl şekilleneceği, hangi downstream servislerin çağrılacağı ve biri kullanılamaz olduğunda nasıl degrade edileceği) saniyeler içinde redeploy edebileceğiniz bir yüzeye taşınır. Her iki tarafı aynı ekibin sahiplenmesi bir koordinasyon devrini de ortadan kaldırır: bir istemci release’ini bir sunucu değişikliğiyle hizalamak, ekipler arası bir pazarlık olmaktan çıkıp tek ekibin deploy sırasına dönüşür.
Bedeli dürüsttür: bir BFF, çalıştırılacak, izlenecek ve güvenliği sağlanacak başka bir deployable’dır ve “deneyim başına bir tane” kuralı, bir iOS, Android ve web ürününün birkaç tane taşıması anlamına gelebilir. Takas, UI ekibi her değişiklik için backend ekiplerine bağımlı kalacaksa ya da her deneyim anlamlı biçimde farklı bir yanıt şekline ihtiyaç duyduğunda değerlidir. Tek ince bir istemci, tek bir kararlı servisle konuşuyorsa gerekçelendirmek zorlaşır; orada BFF, soğuracak fazla bir şey olmadan bir gecikme ve on-call yükü katmanıdır. Bu katman, aksi halde istemci saati release hızınızı belirleyecekse yerini hak eder.
Tek Başına Ship Etmenizi Sağlayan Sözleşmeler
BFF, UI ekibinin hareket etmesini sağlar ama başka ekiplerin sahiplendiği downstream servislerin önünde durur ve uygulama da BFF’in önünde durur. Bu sınırlar arasında bağımsız deploy edilebilirlik bir sözleşme problemidir. Hata modu big-bang entegrasyondur: ekipler değişikliklerini koordineli bir release penceresine kadar tutar, önceden test edilmiş bir uygulama setini birlikte deploy eder ve uyumsuzlukları ancak her şey bir araya geldiğinde keşfeder. O pencere, BFF’in kazandırması gereken hızı silen darboğaza dönüşür.
Tüketici güdümlü sözleşmeler bağımlılığı tersine çevirir. Her tüketici, gerçekten dayandığı sağlayıcı davranışının alt kümesini yayınlar; sağlayıcı da bu beklentilerin birleşimine karşı doğrulanır. Pact dokümantasyonu temel özelliği şöyle yakalar: mevcut tüketicilerin kullanmadığı herhangi bir sağlayıcı davranışı, testleri bozmadan serbestçe değiştirilebilir. Kırıcı değişiklikler, günler sonra ortak bir staging ortamında değil, gerçek tüketicilerin gerçek beklentilerine karşı CI’da, merge öncesinde yüzeye çıkar. (Pact, açık kaynak araç seti ve Pact Broker’dır; PactFlow ise SmartBear’ın ticari barındırılan broker’ıdır. Bi-directional contract testing, çalışan sağlayıcıyı doğrulamak yerine sağlayıcının kendi spec’ini tüketici beklentileriyle karşılaştıran ilişkili ama daha zayıf bir moddur; bu yüzden onu tam tüketici güdümlü doğrulamadan daha hafif bir garanti olarak görün.)
Bunu operasyonel hale getiren kapı can-i-deploy’dur. Koordineli bir seti birlikte deploy etmek yerine, broker’a tek bir uygulamanın belirli bir sürümünün, hedef ortamda zaten deploy edilmiş olanla uyumlu olup olmadığını sorarsınız:
pact-broker can-i-deploy \
--pacticipant mobile-bff --version 4.2.0 \
--to-environment production
Tüketici ve sağlayıcı sürümlerinin matrisini inceler ve deploy’a izin vermek için 0, engellemek için sıfır dışı bir kodla çıkar. Pact dokümanları bunu doğrudan, önceden test edilmiş uygulama setlerini birlikte deploy eden ve bir darboğaz yaratan eski yöntemle karşılaştırır.
Aynı ilkenin olay güdümlü bir karşılığı vardır. BFF ya da servisleri bir mesaj kuyruğu üzerinden haberleştiğinde, bir schema registry deploy sırasını belirleyen uyumluluk modlarını dayatır. Confluent’in Schema Registry’sinde varsayılan BACKWARD’dır; yani önce tüketiciler güncellenir; FORWARD önce üreticileri günceller; FULL her iki yönü de karşılar; ve TRANSITIVE kontrolü yalnızca bir önceki sürüm yerine tüm önceki sürümlere genişletir. O BACKWARD varsayılanı Confluent’e özgüdür; AWS Glue ve Apicurio gibi diğer registry’ler kendi varsayılanlarını seçer; bu yüzden varsaymak yerine çalıştırdığınız registry’nin davranışını doğrulayın. Sözleşme, senkron ya da asenkron biçimiyle, bir ekibin Newman’ın Building Microservices’te çerçevelediği bağımsız deploy edilebilirlik sorusunu yanıtlamasını sağlayan şeydir: bu servisi değiştirip başka hiçbir şeyi değiştirmeden tek başına deploy edebilir miyim.
Eski İstemciler Kaldığı İçin Geriye Dönük Uyumlu Evrim
Sözleşmeler bir değişikliğin deploy etmek için güvenli olup olmadığını söyler. Asimetrik saat ise ne tür bir değişikliğin güvenli olduğunu söyler: eklemeli, geriye dönük uyumlu olanı. Eski istemciler hiçbir zaman tamamen kaybolmadığı için her sunucu değişikliği, aylar önce ship ettiğiniz bir sürüm için çalışmaya devam etmek zorundadır. Mobil bir backend’in varsayılan duruşu bu yüzden yalnızca-eklemeli evrimdir ve iyi anlaşılmış birkaç pattern bunu pratik kılar.
Semantic versioning sözü sürüm numarasının kendisine kodlar. Bir MAJOR artış uyumsuz bir değişikliği, MINOR geriye dönük uyumlu eklemeleri, PATCH geriye dönük uyumlu düzeltmeleri işaret eder; şema yalnızca beyan edilmiş bir public API’ye karşı bir anlam taşır. Mobil bir backend için çıkarım nettir: MAJOR artışlar, kaçınmaya çalıştığınız değişikliklerdir, çünkü eski istemcileri ortada bırakanlar onlardır. Stripe, pratikte yalnızca-eklemeli evrime faydalı bir örnektir. Tarih tabanlı API sürümleri yapısı gereği geriye dönük uyumludur ve dokümantasyonu bu şema içindeki yükseltmeleri mevcut kodu bozmadan uygulanabilir olarak tanımlar. (Herhangi bir özel Stripe sürüm dizesini bir örnek olarak görün; şema zamanla ileri sarar.)
Gerçekten uyumsuz bir değişikliğe ihtiyaç duyduğunuzda, parallel change (expand/migrate/contract olarak da bilinir) bunu koordineli bir cutover olmadan güvenli kılar. Önce sunucuyu hem eski hem yeni şekli destekleyecek biçimde genişletirsiniz (expand), sonra tüketicileri kendi hızlarında geçirirsiniz (migrate) ve eski şekli ancak onu kullanan istemciler tükendiğinde kaldırırsınız (contract). Üretici başlangıçta bir kez deploy eder ve istemcilerin geçişini beklerken hiçbir zaman bloke olmaz; bu da tam olarak mobil durumun talep ettiği ayrıştırmadır. Bedel gerçektir: migrate aşamasında aynı davranışın iki sürümünü sürdürürsünüz ve mobilde bu aşama uzundur, çünkü ancak eski istemciler destek eşiğinizin altına düştüğünde biter.
İstemci tarafının kendi disiplini vardır: tolerant reader. Postel Yasası’nı izleyerek bir istemci gönderdiği şeyde tutucu, kabul ettiği şeyde liberal olmalıdır; bu da pratikte yalnızca ihtiyaç duyduğu alanları ayrıştırmak ve tanımadığı her şeyi yok saymak anlamına gelir. Toleranslı bir istemci, sunucunun serbestçe alan eklemesine izin verir, çünkü bilinmeyen alanları yok sayan eski bir binary daha zengin bir yanıta karşı çalışmaya devam eder. Eklemeli evrimi istemci saatinde hayatta tutan özellik budur: bugün eklediğiniz alan, birinin altı ay önce kurduğu binary’yi bozmaz. BFF ve tüketicilerinin tek bir repository’de mi yoksa birkaçında mı durduğu ayrı bir takastır ve buradaki kol değildir; kol, eski istemciler tükenene kadar her değişikliğin eklemeli kalmasıdır.
Server-Driven UI ve Bedeli
İstemci saatinden kaçmanın en agresif yolu server-driven UI (SDUI): yalnızca veriyi değil layout’u ve mevcut aksiyonları da sunucu temposuna taşımak; böylece binary, sunucunun gönderdiği talimatların genel bir renderer’ına dönüşür. Airbnb’nin Ghost Platform’u belgelenmiş bir örnektir; ekibi onu iOS, Android ve web genelinde layout ve aksiyonları orkestre eden, eski istemcilerin render etmeye devam etmesi için geriye dönük uyumlu section’lara sahip birleşik, belirli kararları dayatan bir server-driven UI sistemi olarak tanımlar. İşe yaradığında, tüm ekranlar için istemci saatini çökertir: hiç binary ship etmeden bir feed’i yeniden düzenleyebilir ya da bir akışı değiştirebilirsiniz.
Bedel kalıcıdır. Sunucunun sürdürebildiği her UI yeteneği, eski istemciler var olduğu sürece sunucunun uymak zorunda olduğu bir sözleşmeye dönüşür; bu da SDUI’nin bir önceki bölümün geriye dönük uyumluluk yükünden kaçmak yerine onu katladığı anlamına gelir. Ayrıca mühendislerin iyi anladığı bir karmaşıklığı (uygulamadaki tipli UI kodu) daha az tanıdık bir yere taşır (sunucu güdümlü bir şema ve sahadaki her sürüm için zarifçe degrade etmek zorunda olan bir renderer). Bedel sektör kaydında görünürdür: Spotify, HubFramework’ü inşa etti ve sonra emekliye ayırdı; kendi repository’si onu server-driven değil, backend güdümlü layout’lar için bir component-driven UI framework olarak tanımlar. SDUI’yi evrensel bir öneri değil, belgelenmiş bir vendor deneyimi olarak görün; onu sürdüren ekipler genelde yüksek ekran varyasyon oranlarına ve buna denk gelen platform yatırımına sahiptir.
| Yaklaşım | Sunucu kontrol eder | İstemci kontrol eder | Geriye dönük uyum / karmaşıklık bedeli |
|---|---|---|---|
| Yalnızca-veri API | Veri değerleri | Layout, aksiyonlar, render | En düşük: alanları eklemeli geliştir |
| BFF-şekilli yanıtlar | Veri artı deneyim başına yanıt şekli | Layout ve render | Orta: deneyim başına yanıt sözleşmeleri |
| Tam SDUI | Veri, layout ve mevcut aksiyonlar | Yalnızca genel render | En yüksek: her UI yeteneği kalıcı bir sözleşme |
Spektrum bir karardır, tırmanmak zorunda olduğunuz bir merdiven değil. Çoğu ürün için orta yol yeterlidir: deneyim başına yanıtları şekillendiren bir BFF ve render’ı sahiplenen bir istemci. Tam SDUI, UI varyasyon oranı, her değişiklik için binary ship etmeyi baskın darboğaz yapacak kadar yüksek olduğunda ve ekip sahadaki her sürüm boyunca geriye dönük uyumlu kalan bir renderer taşıyabildiğinde doğru karardır. Onu varsayılan olarak seçmek, anladığınız bir problemi sonsuza dek ödediğiniz bir bedelle takas eder.
Kapanış
Savunulabilir varsayılan, “tam SDUI’ye geç”ten daha dardır. Sınırda bir BFF sahiplenin ki UI ekibi binary release’i olmadan davranışı değiştirebilsin; her sunucu değişikliğini eklemeli tutun, parallel change’i nadir uyumsuz olan için saklayın; ekiplerin big-bang penceresinde değil bağımsız deploy etmesi için tüketici güdümlü sözleşmeler ve bir can-i-deploy kapısı kullanın; ve SDUI’ye yalnızca UI varyasyon oranı kalıcı bedelini açıkça gerekçelendirdiğinde başvurun. Görüş alanında tutulması gereken şey sınırdır: eklediğiniz her sunucu tarafı kol, daimi bir geriye dönük uyumluluk yükümlülüğüdür, çünkü onu çağıran istemciler hiçbir zaman tamamen yok olmaz. Faydalı bir sonraki adım, en eski desteklediğiniz istemcinin hâlâ neyi çağırdığını denetlemektir; çünkü o yüzey, sürdürmeyi taahhüt ettiğiniz gerçek sözleşmedir.
Bunun parçası olduğu daha geniş release-temposu resmi için Üretime Giden Süreyi Kısaltmak yazısına, güvenlik ekseninde paralel argüman için İnceleme Kuyruğu Olmadan Güvenlik yazısına bakın.
Kaynaklar
- Backends For Frontends — Sam Newman - Deneyim başına bir backend, UI ekibinin sahiplendiği sunucu tarafı sınır.
- API Gateway pattern — microservices.io - Her istemci türü için ayrı bir gateway.
- Apple — App Review - Gönderimlerin ortalama %90’ı 24 saatten kısa sürede inceleniyor.
- MacRumors — iOS adoption stats - Büyük bir mobil OS’in kurulu tabanının kabaca üçte ikisine ulaştığı yönsel adopsiyon.
- Pact — Documentation - Tüketici güdümlü sözleşmeler tüketici ve sağlayıcı arasındaki gizli kaplini kaldırır.
- Can I Deploy — Pact Docs - Big-bang entegrasyonun yerini alan deploy kapısı.
- Confluent — Schema evolution and compatibility - BACKWARD/FORWARD/FULL/TRANSITIVE modları deploy sırasını belirler.
- Semantic Versioning 2.0.0 - Beyan edilmiş bir public API’ye karşı MAJOR/MINOR/PATCH uyumluluk sözleşmesi.
- Parallel Change — Martin Fowler / Danilo Sato - Big-bang cutover olmadan expand/migrate/contract.
- Tolerant Reader — Martin Fowler - İstemciler için Postel Yasası; sunucular serbestçe alan ekleyebilsin diye bilinmeyen alanları yok say.
- Stripe API versioning - Tarih tabanlı eklemeli evrim (herhangi bir sürüm dizesi bir örnektir).
- A deep dive into Airbnb’s server-driven UI system - Ghost Platform; SDUI ile belgelenmiş vendor deneyimi.
- Spotify HubFramework (GitHub, deprecated) - Component-driven UI framework, emekliye ayrılmış; SDUI bedeli.
- Building Microservices, 2nd ed. (Sam Newman) - Karar birimi olarak bağımsız deploy edilebilirlik (parafraze, web kanonu olarak alıntılanmadı).
- Are you an Elite DevOps performer? — Google Cloud - Üretime giden süre ana hattı için lead-time çerçevesi.
İlgili yazılar
İç servis katmanı kurmadan önce kurup kurmayacağınıza karar verin. Katmanın çağrı başına maliyeti, VPC Lattice'in kazandığı hacim ve direct invoke'un hâlâ kazandığı an.
Private REST API gRPC’yi yapısal olarak taşıyamaz ve gRPC konuşan her AWS yüzeyi Lambda hedeflerini dışlar. gRPC’den neyi tutmalı, neyi bırakmalı.
Private REST API, onu açan resource policy, route bazlı AWS_IAM yetkileri, iki CDK stack'i ve Node 22 Lambda'dan çağrıyı imzalamak.
SigV4 hangi servisin çağırdığını kanıtlar, kimin adına çağırdığını değil. Doğrulanmış özneyi nasıl taşırsınız ve taşıma katmanı gerçekte neyi şifreler.
Aynı hesapta resource policy ile çağıranın identity policy'si bir OR'dur. Hesaplar arası çağrıda AND'e döner ve sessizlik reddeder. Veri sınırında bunun sonuçları.