İçeriğe atla

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.

Ayhan Sipahi Ayhan Sipahi

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 (tek yönlü, yavaş)

Build gönder

İnceleme: %90 24 saat altında, günlere uzayan kuyruk

Haftalar ve aylar boyunca gönüllü adopsiyon

Eski sürümler tam olarak yok olmaz

Sunucu saati (geri alınabilir, hızlı)

Deploy

Bug bulundu

Saniyeler içinde rollback

İ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.

Mobil uygulama (donmuş binary)

BFF (sahibi: UI ekibi)

Servis A

Servis B

Servis C

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.

uyumlu

doğrulanmamış çift

can-i-deploy: sürüm + ortam

Matris doğrulandı mı?

exit 0: deploy devam eder

sıfır dışı: deploy engellenir

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.

Contract

Eski istemciler tükenince eski şekli kaldır

Migrate (mobilde uzun)

Yeni istemciler yeni şekli kullanır

Eski istemciler eski şekilde kalır

Expand

Sunucu eski + yeniyi destekler

İ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şımSunucu kontrol ederİstemci kontrol ederGeriye dönük uyum / karmaşıklık bedeli
Yalnızca-veri APIVeri değerleriLayout, aksiyonlar, renderEn düşük: alanları eklemeli geliştir
BFF-şekilli yanıtlarVeri artı deneyim başına yanıt şekliLayout ve renderOrta: deneyim başına yanıt sözleşmeleri
Tam SDUIVeri, layout ve mevcut aksiyonlarYalnızca genel renderEn 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#

İlgili yazılar

Bruno API Koleksiyonlarını Git'te Tutmak: Bedava Postman Değil, Bir İş Akışı Değişikliği

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

Native Mobil Uygulamalar için Sunucu Güdümlü Arayüz

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

Sunum Servisi için Hexagonal Mimari: BFF Karşılaştırması

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

Web ve Mobil Uygulamalarda Uzun Süren API İsteklerini Yönetmek

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

Optimistic UI vs Decoupled Akış: Async Backend'ler için UX Desenleri

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