<

Ölçeklenebilir REST API Tasarımı: Sürümleme, Sayfalama ve İdempotency

Faturası iki yıl gecikmeyle kesilen bir karara şahit olduk geçen ay. Bir müşterimizin backend ekibi, "artık kullanılmıyor" dedikleri bir alanı API cevabından kaldırdı. Kullanılmıyordu — yeni uygulama sürümünde. Sahada ise kullanıcıların üçte biri hâlâ on sekiz ay önceki sürümü taşıyordu ve o sürüm, alan gelmeyince açılışta çöküyordu. Bir gecede binlerce çökme, mağazada bir yıldız yağmuru ve apar topar geri alınan bir deploy.

API tasarımı böyle bir alan: kararların bedeli hemen değil, yıllar sonra ödenir. Mobil uygulamanın eski sürümü kırıldığında, milyonuncu kayıtta sayfalama süründüğünde, çift çekilen ödemede destek hattı kilitlendiğinde. Yıllardır hem kendi yazdığımız hem devraldığımız API'lere bakım yapan bir ekip olarak, en pahalı üç dersi bu yazıya koyduk.

Eski sürüm sahada yıllarca yaşar

Web'de deploy edersiniz, herkes yeni kodu alır. Mobilde böyle bir lüks yok: güncellemeyi kullanıcı yapar, yapmayabilir de. Otomatik güncellemesi kapalı telefonlar, mağazaya erişemeyen kurumsal cihazlar, "yer yok" diye güncellemeyen kullanıcılar... Gerçek şu ki yayınladığınız her API sürümü, sahada yıllarca yaşayacak bir sözleşmedir.

Bu yüzden temel kuralımız tek cümle: alan ekleyebilirsin; var olanı değiştiremez, silemez, anlamını bozamazsın. Yeni alan eklemek güvenlidir çünkü eski istemci bilmediği alanı görmezden gelir. Kırıcı değişiklik kaçınılmazsa yeni sürüm açılır — /v2/ — ve eskisi, tarihi baştan ilan edilmiş bir planla emekli edilir: önce loglardan kimlerin hâlâ v1 kullandığı izlenir, sonra uyarı, en sonda kapatma. Sürümü URL'de tutmak zarif bulunmayabilir; header tabanlı sürümleme kâğıt üstünde daha şık. Ama URL'deki sürüm loglarda, tarayıcıda, curl komutunda çıplak gözle görünür — ve hata ayıklarken bu görünürlük, zarafetten kıymetlidir.

Emeklilik sürecinin iletişimi de teknik kararın kendisi kadar önemli. API'nizi kullanan iç ve dış ekiplere kapatma tarihini aylar önce, birden fazla kanaldan duyurun; cevaba eski sürümün öleceğini söyleyen bir uyarı başlığı ekleyin ki dikkatli istemciler bunu loglarında görebilsin. "Kapattık, kimse fark etmemiştir" cümlesiyle başlayan hikâyelerin sonu, bizim tecrübemizde hiç iyi bitmiyor.

Bir de kimsenin ilk sürümde düşünmediği mekanizma: zorunlu güncelleme kapısı. Uygulama açılışta tek soruluk hafif bir uca danışır — "benim sürümüm hâlâ destekleniyor mu?" Cevap hayırsa kullanıcıya nazik bir güncelleme ekranı gösterilir. Bu kapı yokken sahadaki antika sürümleri emekli etmenin tek yolu onları kırmaktır; kapı baştan varsa, emeklilik kontrollü ve saygılı olur. İki saatlik iş, yıllarca sürecek bir esneklik.

Sözleşmenin yazılı hâli de en az kuralları kadar önemli. Biz her API'yi OpenAPI şemasıyla tek doğruluk kaynağına bağlıyoruz: dokümantasyon oradan üretiliyor, mobil istemcinin ağ katmanı oradan türetiliyor, CI'daki sözleşme testi de yine oraya bakıyor. Şemayla gerçek davranış birbirinden koptuğunda pipeline kızarıyor. "Dokümantasyon güncel mi?" sorusunun en iyi cevabı, güncel kalmaktan başka çaresi olmayan bir dokümantasyondur.

Offset sayfalamanın milyonuncu kayıtta çöküşü

Her API sayfalamayla başlar ve neredeyse hepsi aynı tuzağa düşer: OFFSET. "OFFSET 100000 LIMIT 20" sorgusu veritabanına şunu söyler: yüz bin yirmi satırı oku, ilk yüz binini çöpe at. Tablo büyüdükçe her sayfa doğrusal olarak yavaşlar; kullanıcı derinlere indikçe API sürünmeye başlar. Üstelik ikinci bir dert var: sayfalar arasında araya yeni kayıt girdiğinde liste kayar — kullanıcı aynı kaydı iki kez görür ya da hiç görmez.

Doğru araç cursor (keyset) sayfalama: istemciye "son gördüğün kaydın işaretçisi bu" dersiniz, sonraki istek "id < son_gorulen ORDER BY id DESC LIMIT 20" ile gelir. Veritabanı indeksten doğrudan o noktaya atlar; birinci sayfayla milyonuncu sayfa aynı hızda döner ve kayma diye bir şey kalmaz. Sonsuz kaydırma yapan her mobil arayüzün doğal eşi budur. Yeni başlayan projelerimizde offset'i API sözleşmesine baştan sokmuyoruz; sonradan sökmesi, hiç takmamaktan on kat zahmetli çünkü istemciler "sayfa numarası" kavramına alışmış oluyor.

Dürüstlük borcu: cursor'ın da bedelleri var. "37. sayfaya atla" diye bir kavram kalmaz ve "toplam 4.812 sonuç" bilgisi bedava gelmez. Ama mobil arayüzlerin gerçeğine bakın — kullanıcı sayfa numarasıyla değil parmağıyla geziniyor; toplam sayı gerekiyorsa ayrı ve önbelleklenmiş bir sayım ucundan verilir. Yönetim panelindeki tablo görünümü gibi sayfa numarasının gerçekten anlamlı olduğu yerlerde offset'e izin veriyoruz; kural katılığından değil, her aracın yerini bilmekten yanayız.

Aynı ödeme iki kez çekilirse

Üçüncü ders, mobil ağların kaprisiyle ilgili. Senaryo klasik: istemci ödeme isteğini gönderdi, sunucu işledi, ama cevap yolda kayboldu — tünelde sinyal gitti, diyelim. İstemci ne görür? Zaman aşımı. Ne yapar? Tekrar dener. Sunucu ne yapar? Ödemeyi bir daha çeker. Müşteri ne yapar? Bankasını arar, sonra sizi, sonra sosyal medyayı.

Bu senaryonun sigortası idempotency deseni: istemci, kritik her POST isteğine benzersiz bir Idempotency-Key koyar. Sunucu bu anahtarı işlem sonucuyla birlikte saklar; aynı anahtarla ikinci kez gelen isteği yeniden işlemez, ilk cevabın aynısını döner. İstemci istediği kadar tekrar denesin — ağ istediği kadar kaprisli olsun — işlem bir kez gerçekleşir.

Ödeme, sipariş oluşturma, bildirim gönderimi gibi uçlarda bu desen tercih değil, zorunluluktur. Kurulumu da abartıldığı kadar zor değil: anahtar-cevap çifti için bir tablo ya da Redis, istek başında bir kontrol. İki günlük iş, yıllarca çift işlem şikâyeti sıfır.

Uygularken iki inceliğe dikkat: anahtarların saklama süresini baştan belirleyin (yirmi dört saat çoğu senaryo için yeterli, sonsuza dek saklamak gereksiz yük) ve yarış durumunu unutmayın — aynı anahtarla iki istek aynı milisaniyede gelirse ikisi de "bu anahtar yeni" diye işleme girebilir. Çözüm, anahtar kolonuna veritabanı seviyesinde benzersizlik kısıtı koymak; ikinci istek kayıt atamaz ve ilkinin sonucunu bekleyip döner. Bu detayı atlayan bir ödeme entegrasyonunun loglarını incelemiştik; çift çekimlerin tamamı yoğun saatlerdeki bu yarış penceresinden sızmıştı.

Sıkıcı kararların bileşik faizi

Fark ettiyseniz bu yazıda parlak hiçbir şey yok: sürüm disiplini, doğru sayfalama, tekrar güvenliği. API tasarımının doğası bu — iyi yapıldığında kimsenin fark etmediği, kötü yapıldığında herkesin hissettiği kararlar. Ama bileşik faiz gibi işlerler: bugün verilen doğru sıkıcı karar, iki yıl sonra "API'miz büyümeye sorunsuz eşlik ediyor" cümlesi olarak geri döner.

Bu üçlünün kardeşleri de var — tutarlı bir hata gövdesi formatı, istemciye kalan hakkını söyleyen rate limit başlıkları, alan bazlı kısmi cevaplar — ama üçü her projede, her ölçekte kendini amorti ediyor. Yeni API'ye başlarken kontrol listemizin ilk sayfası bu.

Girişteki çökme vakası ucuz atlatıldı; alan geri eklendi, ders defterlere yazıldı. Ama o ekip artık her API değişikliğini "sahada kim kırılır?" sorusuyla açıyor. Sizin API'nizin böyle bir stres testine ihtiyacı varsa konuşalım — sözleşme gözden geçirmesi, üretim kazasından her zaman ucuzdur.

📅 Yayınlanma:  ·  Yakup Zengin