BFF ile Mobil API Sürümleme: İstemciyi Geri Alamadığınızda Ship Etmek
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 kaldıraçlar ş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üresi sınırlıdır ama kontrolü sizde değildir. 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 ortalama planlama için faydalıdır; paydaşlara release tarihi olarak söz vermek içinse fazla değişkendir. 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 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 kaldıraçlarının sunucu tarafında olması 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 her zaman buna izin vermeyebilir.
BFF Katmanı#
İlk yapısal kaldıraç, bir backend-for-frontend (BFF): her kullanıcı deneyimi için bir backend; sahibi de UI’ı sahiplenen ekip. 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 “bir deneyim, bir BFF”: tek bir ortak gateway her tüketiciye hizmet etmez. 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: tek ekip, kendi istemci release’ini ve sunucu değişikliğini tek bir deploy sırasında hizalar.
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ç duyuyorsa değerlidir. Tek bir ince 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ına dönüşür ve yerini yalnızca istemci saati release hızınızı belirleyecekse 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, uyumsuzlukları ancak her şey bir araya geldiğinde keşfeder ve BFF’in kazandırması gereken release hızını bu süreçte kaybeder.
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, gerçek tüketicilerin gerçek beklentilerine karşı CI’da, merge öncesinde yüzeye çıkar; ortak bir staging ortamı bunu ancak günler sonra yakalardı. (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ü tüm önceki sürümlere genişletir, bir önceki sürümün ötesine geçer. 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, uyumluluk sözünü 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.
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. Migrate aşamasında ise 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. Bugün eklenen bir alan, altı ay önce kurulmuş bir binary’yi bozmaz; bu da eklemeli evrimi istemci saatinde çalışır tutar.
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 ortadan kaldırır: hiç binary ship etmeden bir feed’i yeniden düzenleyebilir ya da bir akışı değiştirebilirsiniz.
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). Spotify’ın kendi geçmişi de bu kayda dahildir: 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. Bunları belgelenmiş vendor deneyimi olarak okuyun; SDUI’yi 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.
Kapanış#
Savunulabilir varsayılan, tam SDUI’nin epey gerisinde durur: katmanda bir BFF, nadir uyumsuz değişiklik için parallel change ile yalnızca-eklemeli evrim ve sözleşme kapısından geçen bağımsız deploy’lar. SDUI, yerini yalnızca UI varyasyon oranı kalıcı bedelini açıkça gerekçelendirdiğinde hak eder. Faydalı bir sonraki adım, desteklediğiniz en eski istemcinin hâlâ neyi çağırdığını denetlemek.
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 (yeni sekmede açılır) - Deneyim başına bir backend, UI ekibinin sahiplendiği sunucu tarafı sınır.
- API Gateway pattern - microservices.io (yeni sekmede açılır) - Her istemci türü için ayrı bir gateway.
- Apple - App Review (yeni sekmede açılır) - Gönderimlerin ortalama %90’ı 24 saatten kısa sürede inceleniyor.
- MacRumors - iOS adoption stats (yeni sekmede açılır) - Büyük bir mobil OS’in kurulu tabanının kabaca üçte ikisine ulaştığı yönsel adopsiyon.
- Pact - Documentation (yeni sekmede açılır) - 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 (yeni sekmede açılır) - Big-bang entegrasyonun yerini alan deploy kapısı.
- Confluent - Schema evolution and compatibility (yeni sekmede açılır) - BACKWARD/FORWARD/FULL/TRANSITIVE modları deploy sırasını belirler.
- Semantic Versioning 2.0.0 (yeni sekmede açılır) - Beyan edilmiş bir public API’ye karşı MAJOR/MINOR/PATCH uyumluluk sözleşmesi.
- Parallel Change - Martin Fowler / Danilo Sato (yeni sekmede açılır) - Big-bang cutover olmadan expand/migrate/contract.
- Tolerant Reader - Martin Fowler (yeni sekmede açılır) - İstemciler için Postel Yasası; sunucular serbestçe alan ekleyebilsin diye bilinmeyen alanları yok say.
- Stripe API versioning (yeni sekmede açılır) - Yapısı gereği geriye dönük uyumlu kalan, tarih tabanlı API sürümleri.
- A deep dive into Airbnb’s server-driven UI system (yeni sekmede açılır) - Ghost Platform; SDUI ile belgelenmiş vendor deneyimi.
- Spotify HubFramework (GitHub, deprecated) (yeni sekmede açılır) - Component-driven UI framework, emekliye ayrılmış; SDUI bedeli.
- Building Microservices, 2nd ed. (Sam Newman) (yeni sekmede açılır) - Karar birimi olarak bağımsız deploy edilebilirlik.
- Are you an Elite DevOps performer? - Google Cloud (yeni sekmede açılır) - Üretime giden süre ana hattı için lead-time çerçevesi.
İlgili yazılar
Bruno .bru dosyalarını repo'ya commit etmek, API sözleşmesini kodla aynı PR ve geçmişte tutar. Tek gerçek bedel, bilinçli bir secret sınırıdır.
testing · ci-cd · developer-experience +1
Sunucu güdümlü arayüz, sunucu tarafı kompozisyonun mobil karşılığıdır. Zor kısmı JSON render etmek değil, eski sürümlerde ayakta kalan sürümlenmiş bileşen sözleşmesidir.
mobile · react-native · architecture
Bir UI parçasının arkasındaki ince sunum servisi yapışkan koda dönüşür. Port-ve-adaptör, çekirdeği somut hiçbir şeye bağımlı bırakmayarak bunu sürdürülebilir tutar.
architecture · nodejs · typescript +3
Tek bir backend üzerinde çalışan web SPA ve mobil uygulama için uzun süreli işlere dair tek bir varsayılan desen ve onu geçersiz kılmanız gereken durumlar.
api-design · real-time · webhooks +4
Async backend'le çalışan tasarımcılar için pragmatik rehber: üç etkileşim şekli, hangisi ne zaman, ve karşı durmanız gereken dört anti-pattern.
event-driven · state-management · design-patterns +2