39 puan yazan GN⁺ 2025-08-25 | 1 yorum | WhatsApp'ta paylaş
  • Yazılım mühendisliğinde API’ler temel araçlardır ve iyi bir API’nin sıkıcı derecede tanıdık ve basit olması arzu edilen bir özelliktir
  • Bir API bir kez yayımlandıktan sonra değiştirilmesi zor olduğundan, kullanıcı alanını bozmama ilkesi (WE DO NOT BREAK USERSPACE) önemlidir
  • Değişiklik kaçınılmaz olduğunda sürümleme (versioning) gerekir, ancak bu karmaşıklığı ve bakım maliyetini ciddi biçimde artıran gerekli bir kötülüktür
  • API kalitesi sonuçta ürünün kendi değerine bağlıdır; kötü tasarlanmış bir ürün iyi bir API oluşturmayı zorlaştırır
  • Kararlılık ve ölçeklenebilirlik için API anahtarı tabanlı kimlik doğrulama, idempotency, rate limiting, cursor tabanlı sayfalama gibi unsurlar dikkate alınmalıdır

Giriş: API tasarımının önemi ve bağlamı

  • Modern yazılım mühendislerinin temel işlerinden biri API’lerle etkileşime girmektir
  • Yazar da REST, GraphQL, komut satırı araçları gibi çeşitli biçimlerde genel kullanıma açık ve kurum içi API’ler tasarlama/uygulama/kullanma deneyimine sahiptir
  • Mevcut API tasarımı tavsiyeleri, karmaşık kavramlara (REST’in tanımı, HATEOAS vb.) takılma eğilimindedir
  • Bu yazı, gerçek deneyime dayanarak pratik API tasarım ilkelerini derler

Tanıdıklık ile esneklik dengesi: iyi API’nin ilk koşulu

  • İyi API, “sıradan ve sıkıcı” bir API’dir; yani daha önce kullanılan API’lerle benzer şekilde çalışmalıdır
  • Kullanıcılar API’nin kendisinden çok kendi hedeflerine ulaşmaya odaklandığı için, giriş eşiği düşük bir tasarım gerekir
  • Bir kez yayımlanan API’yi değiştirmek çok zordur, bu yüzden ilk tasarım aşamasında dikkatli olunmalıdır
  • Geliştiriciler mümkün olduğunca sade bir API isterken, uzun vadeli esneklik bırakma ihtiyacı da her zaman vardır
  • Sonuç olarak temel mesele, tanıdıklık ile uzun vadeli esneklik arasındaki dengeyi kurmaktır

Kullanıcı alanını asla bozma (WE DO NOT BREAK USERSPACE)

  • Mevcut yanıt yapısına alan eklemek çoğu durumda sorun yaratmaz
  • Ancak alan kaldırma, tür veya yapı değiştirme tüm tüketici kodunu bozacak sonuçlar doğurur
  • API’yi sürdürenlerin, mevcut kullanıcıların yazılımlarını bilerek bozmama sorumluluğu vardır
  • HTTP’deki referer başlığındaki yazım hatasının bile düzeltilmemesinin nedeni, kullanıcı alanını koruma kültürüdür

API’yi bozmadan değiştirmek: sürümleme stratejisi

  • Yıkıcı değişiklikler API’de yalnızca zorunlu olduğunda yapılmalıdır; bu durumda doğru yaklaşım sürümlemedir
  • Eski ve yeni sürüm aynı anda çalıştırılarak kademeli geçiş sağlanmalıdır
  • Sürüm tanımlayıcısı URL (/v1/), başlıklar ve başka yollarla verilebilir; kullanıcılar kendi hızlarında geçiş yapabilir
  • Sürümlemenin muazzam bir bakım maliyeti (artan endpoint sayısı, test, destek) ve kullanıcı kafa karışıklığı gibi dezavantajları vardır
  • Stripe gibi iç çeviri katmanları kullanılsa bile, temel karmaşıklıktan kaçınılamaz
  • API sürümlemesine başvurmak son çare olmalıdır

API’nin başarısı tamamen ürün değerine bağlıdır

  • API, özünde gerçek bir iş ürününün arayüzünden ibarettir
  • OpenAI, Twilio gibi API’lerde de kullanıcıların asıl istediği şey, API’nin sunduğu işlevin kendisidir
  • Değerli bir ürünse, API rahatsız edici olsa bile kullanılır
  • API kalitesi bir tür “marj” özelliğidir: ancak temel rekabet gücü benzer olduğunda seçim kriteri olur
  • Buna karşılık, hiç API’si olmayan bir ürün teknik kullanıcılar için büyük bir engeldir

Ürün tasarımı kötüyse API de iyi olamaz

  • Teknik olarak çok iyi yapılmış bir API olsa bile, pazarda karşılığı olmayan bir ürün için bunun anlamı yoktur
  • Daha da önemlisi, temel kaynak yapısı mantıksız ya da verimsizse bu API’de de görünür
  • Örneğin yorumları linked list olarak saklayan bir sistemde RESTful bir tasarımı doğal biçimde kurmak bile zorlaşır
  • UI’da gizlenebilen teknik sorunlar API’de doğrudan ortaya çıkar ve kullanıcıdan sistemi gereksiz yere derinlemesine anlamasını ister

Kimlik doğrulama (Authentication) ve kullanıcı çeşitliliği

  • Uzun ömürlü API anahtarı tabanlı kimlik doğrulama mutlaka desteklenmelidir
  • OAuth gibi daha güvenli yöntemler ek olarak desteklense bile, API anahtarlarının giriş eşiği çok daha düşüktür
  • API tüketicileri yalnızca mühendislerden ibaret değildir; geliştirici olmayanlar da (satış, planlama, öğrenciler, hobi amaçlı geliştiriciler vb.) çoktur
  • Zor veya karmaşık kimlik doğrulama gereksinimleri (OAuth vb.) uzman olmayan kullanıcılar için bir engel oluşturur

İdempotency ve yeniden deneme işlemleri

  • Aksiyon içeren isteklerde (ör. ödeme, durum değişikliği vb.) hata durumunda yeniden deneme (retry) güvenliği önemlidir
  • İdempotency, aynı isteğin birden fazla kez gönderilse bile sonucun yalnızca bir kez işlenmesini garanti etmek demektir
  • Standart yöntem, tekrar işlemeyi önlemek için parametre veya başlıkta bir idempotency key iletmektir
  • İdempotency key saklama için Redis gibi basit bir anahtar/değer deposu yeterlidir; çoğu durumda periyodik süre sonu uygulamakta sakınca yoktur
  • Okuma/silme isteklerinde (REST tarzında) buna genellikle ihtiyaç yoktur

API güvenliği ve rate limiting

  • Kod üzerinden yapılan API istekleri, kullanıcı etkileşiminden çok daha hızlı gerçekleşebilir
  • Düşünmeden yayımlanan tek bir API bile beklenmedik bir amaçla (ör. büyük ölçekli sohbet sistemi) kullanılabilir
  • Rate limiting mutlaka gereklidir ve maliyeti yüksek işlemlerde daha sıkı uygulanmalıdır
  • Belirli bir müşteri için geçici API devre dışı bırakma (killswitch) seçeneği de düşünülmelidir
  • Rate limit bilgisi yanıt başlıklarıyla (X-Limit-Remaining, Retry-After vb.) açıkça bildirilmelidir

Sayfalama (Pagination) stratejisi

  • Büyük veri kümelerini (ör. milyonlarca ticket) verimli döndürmek için sayfalama şarttır
  • Offset tabanlı sayfalama basittir, ancak büyük veri üzerinde giderek yavaşlar
  • Cursor tabanlı sayfalama, sorgu performansını düşürmeden çok büyük veri kümelerinde de etkilidir
  • Cursor tabanlı yaklaşımın uygulanması ve kullanımı biraz daha zor olsa da, uzun vadede kaçınılmaz bir değişim olabilir
  • Yanıta next_page gibi alanlar ekleyerek bir sonraki isteğin cursor bilgisini açıkça göstermek akıllıcadır

İsteğe bağlı alanlar ve GraphQL hakkındaki görüş

  • Maliyeti yüksek veya yavaş alanlar varsayılan yanıttan çıkarılmalı, yalnızca gerektiğinde isteğe bağlı olarak eklenmelidir
  • includes parametresi vb. ile ilişkili veriler dâhil edilebilir
  • GraphQL veri yapısı esnekliği açısından avantaj sunsa da, geliştirici olmayanlar için erişilebilirliğin azalması, caching/edge case karmaşıklığı ve arka uç uygulama zorluğu gibi sorunları vardır
  • Pratik deneyime göre GraphQL benimsemesi yalnızca gerçekten gerekli olduğunda uygundur

İç kullanım API’lerinin özellikleri

  • Kurum içi API’ler, dışa açık API’lerden birçok açıdan farklıdır
  • Tüketiciler çoğunlukla uzman yazılım mühendisleri olduğundan, daha karmaşık kimlik doğrulama ya da yıkıcı değişiklikler kabul edilebilir
  • Yine de idempotency, hata önleme ve operasyonel yükü azaltma için tasarım ilkeleri geçerliliğini korur

Kısa özet

  • API’leri değiştirmek zordur, kullanmak ise kolay olmalıdır
  • Kullanıcı alanını bozmamak, API’yi sürdürenlerin en önemli yükümlülüğüdür
  • API sürümlemesi yüksek maliyetli olduğundan yalnızca son çare olarak kullanılmalıdır
  • Sonuçta API kalitesini belirleyen şey ürünün öz değeridir
  • Kötü tasarlanmış bir ürünün eksikleri API düzeyinde giderilmeye çalışılsa bile bunun sınırları vardır
  • Basit kimlik doğrulama desteği, gerekli aksiyon isteklerinde mutlaka idempotency, ayrıca rate limiting/sayfalama gibi kararlılık önlemleri önemlidir
  • İç API’lerde strateji kullanım amacına ve hedef kitleye göre değişse de, dikkatli tasarım hâlâ gereklidir
  • REST, JSON gibi formatlar veya OpenAPI gibi araçlar temel mesele değildir; önemli olan açık dokümantasyondur

1 yorum

 
GN⁺ 2025-08-25
Hacker News yorumu
  • "userspace'i asla bozma" öğüdü meşhur, ama bunun ters tarafına da iyi değiniliyor. Yani, "kernel API'si haber vermeden bozulabilir" deniyor. Önemli olan, "hiçbir API'yi rastgele bozma" değil, "yalnızca kararlı olduğunu ilan ettiğin kısmı asla bozma" şeklindeki ince dengedir

    • Linux kernel userspace'i bozmasa bile, GNU libc userspace uyumluluğunu oldukça sık bozar. Bu yüzden sonuçta Linux kullanıcı alanı, kernel geliştiricileri ne kadar uğraşırsa uğraşsın sık sık bozulur. Yeni sürüm libc ile derlenen programlar ve kütüphaneler daha eski libc üzerinde düzgün çalışmayabiliyor; dolayısıyla fiilen tüm bileşenleri tek seferde yükseltmek gerekiyor. Biraz ironik biçimde, Windows bu sorunu redistributable modeliyle onlarca yıl önce çözmüştü

    • Linux'ta meşhur biçimde kararlı bir herkese açık driver API'si yok; duyduğuma göre bu da Google'ın Fuschia OS geliştirme motivasyonlarından biriydi. Linux, kullanıcı alanı ve donanım için farklı yönelimlere sahip gibi görünüyor

  • Yazar sürüm tabanlı API'leri pek sevmiyor gibi, ama ben uygulama yaparken en baştan itibaren sürümlemeyi mutlaka devreye almayı hep tavsiye ederim. Geleceği öngöremezsiniz; bir gün dış etkenler yüzünden sizin tarafınızda da kırıcı değişiklikler kaçınılmaz olur

    • Aslında yazarın da sürümlemeyi önerdiğini düşünüyorum. Metinde "versioning is how you change your API responsibly" diyor; yani özünde sürümlemeyi teşvik ediyor. Sadece yeni bir sürüme geçişin son çare olması gerektiğini söylüyor

    • Endpoint'e durduk yere "v1" eklememe görüşüne katılıyorum. Pratikte API büyüdükçe olan şey şu: önce mevcut endpoint'e alanlar ya da seçenekler eklenerek geriye dönük uyumluluk korunmaya çalışılıyor. Sonra tamamen uyumsuz bir değişiklik gerekirse genelde endpoint adı baştan konuyor ve bambaşka bir endpoint oluşturuluyor (/v2 değil). Eğer tüm API'yi değiştirmek gerekiyorsa da eski servis emekliye ayrılıp adı bile yeni olan tamamen farklı bir servis yayına alınıyor. 25 yıllık çalışma hayatımda "/v1" ve "/v2"nin yan yana kullanıldığı bir servisi yalnızca bir kez gördüm

    • Yazarın niyetinin baştan /v1'i endpoint'e koymayın demek olduğunu sanmıyorum. Ana fikir, yeni bir sürümün (/v2) hiç doğmaması için elinizden geleni yapmanız gerektiği. /v2 ortaya çıkarsa her bug fix'te iki tarafa da kod dokunmanız gerekir, koşullu dallanmalar katlanarak artar ve kod tabanı spagettiye döner. Sonuçta birden çok sürümü desteklemek zorunda kalınmasının nedeni, ilk /v1 tasarımında gelecekteki uyumluluğun yeterince düşünülmemiş olmasıdır

    • Sürümlemeyi sonradan eklemenin de sorun olmadığını düşünüyorum. Örneğin önce /api/posts ile başlayıp sonraki sürümü /api/v2/posts olarak eklemek gayet yeterli

    • En baştan sürümü URL'ye gömme yaklaşımına katılmıyorum. Bunu yapınca gerçekten birden fazla sürüm daha sık kullanılmaya başlanıyor ve bence bu daha iyi bir sonuç değil

  • Bu yazı çok faydalıydı. Buna bir tavsiye daha ekleyeyim: API dokümantasyonuna ulaşmak ne kadar zorsa, API kalitesi de genelde o kadar düşüktür. Dokümantasyonu ancak sözleşme imzalayarak alabiliyorsanız, o API'nin kalitesinin berbat olacağını varsaymakta sakınca yok

  • idempotency key'i comment tablosunda tutmak yerine Redis gibi bir key/value deposuna koymayı önermiş ama bunun tüm hata durumlarında gerçekten sağlam idempotency garanti edip etmediğini merak ediyorum. Diyelim ki sunucu SET key 1 NX gibi koşullu bir yazma yaparken anahtarın zaten var olduğunu gördü; bu durumda yorum oluşturmayı atlaması gerekir, ama bu noktada önceki isteğin gerçekten DB'ye yazılmış olmama ihtimali de vardır. idempotency key kaydı, gerçek işlemle birlikte aynı transaction içinde commit edilmeli ve gerekirse rollback edilebilmelidir. Sonuçta idempotency key'in özü, "bu işlem ya da isteğin benzersiz kimliği" olmalıdır. Örneğin “yorum oluşturma”, “yorum güncelleme” gibi işlemler için kaynağa özgü bir tanımlayıcı olmalıdır

    • idempotency için ayrı bir bileşen (ör. redis vb.) eklemekten kaçınmak gerekir. Bu, soyutlamanın bozulması ya da tuhaf çalışması gibi sorunlara veya teslimat garantilerinin yanlış anlaşılmasından doğan hatalara yol açabilir. Bunun yerine, yazma işlemiyle birlikte etiket ya da metadata saklayarak kullanıcıların ilerlemeyi doğrudan izlemesine ve bunu mevcut verilerle birlikte tutmasına imkân vermek çok daha iyidir
  • Cursor tabanlı sayfalamanın avantajı şu: kullanıcı bir sayfayı yükleyip ‘sonraki’ye basana kadar yeni öğeler eklenmiş olsa bile daha önce gördüğü kayıtları tekrar görmek zorunda kalmaz. Cursor yaklaşımı, önceki sayfanın son nesne ID'sini saklayıp ondan sonraki öğeleri verdiği için özellikle sonsuz kaydırma için kullanışlıdır. Buna karşılık, cursor tabanlı yaklaşımın dezavantajı “N'inci sayfaya atla” özelliğini kurmanın zor olmasıdır

    • Cursor mutlaka opaque olmalı ki DB boyutu gibi bilgileri dışarı sızdırmasın. Ayrıca cursor içine durum bilgisini (arama parametreleri, cache durumu, routing bilgisi vb.) encode ederek daha çeşitli işlevler de kurulabilir
  • Bugünlerde "API" dendiğinde çoğu insan bir web uygulamasına istek gönderip parametre ve header ayarlayarak veri almayı düşünüyor; oysa API aslında "Application Programming Interface", yani bir uygulama programının arayüzü demektir. Terim ilk kez 1940'larda kullanıldı ve 1990'lara kadar neredeyse başka bir anlam taşımadı. API'nin tarihi 80 yılı aşkın ve bu konuda çok eski kaynaklar var. O dönem programcıların hangi problemlerle uğraşıp bunları nasıl çözdüğünü düşünmek, bugün de insana fayda sağlayabilir

  • İç kullanıcıları sadece 'kullanıcı' olarak görmek gerektiği fikrine katılmıyorum. Daha teknik kişiler ve büyük olasılıkla programcı olsalar da onlar da meşgul; kendi projelerine odaklandıkları için API değişikliklerine uyum sağlayacak zaman veya alan bulamayabiliyorlar. Mümkünse dışarı açmadan önce ekip içinde bolca "dogfooding" testi yapmak önemlidir. Bir kez dışarı açıldığında, ‘userspace'i bozmama’ sözüne mutlaka sadık kalmak gerekir

    • İç kullanıcılar söz konusuysa, onlarla doğrudan iletişime geçip migration'ı yönlendirmeyi sağlayan telemetry araçları genelde uygulanmış olur. Bu sayede API sürümlerini kullanım dışı bırakmak da mümkün olur; dolayısıyla sürümlemeyi stratejik biçimde devreye almak gayet cazip olabilir. API sürümlemesine bizzat katıldım ve bunu yapmayan organizasyonlarla kıyaslandığında gerçekten faydasını gördüm

    • Sürümleme yaklaşımının bu sorunu çözmeye yardımcı olduğunu düşünüyorum. İç kullanıcıları gözetmenin en iyi yollarından biri, spesifikasyon üzerinde birlikte çalışmak ve o spesifikasyonun üzerinde çalışılan sürümünü de paydaşlarla paylaşmaktır. Sürekli güncellenen belgeler bile bir referans noktası sağladığında, hem iç hem dış geri bildirim çok daha akıcı hale gelir ve gereksiz politik sürtüşmelerden kaçınıldığı sürece son derece yararlı olabilir

  • idempotency key'i redis'te tutmak yerine, mümkünse gerçek verinin yazıldığı aynı transaction içinde birlikte saklamak daha güvenilir olur diye düşünüyorum

  • "userspace'i asla bozma" uyarısı gerçekten çok önemli. Spotify, Reddit, Twitter gibi örneklerde son dönemde bu ilkenin göz ardı edilmesi üzücüydü

  • Bu arada https://jcs.org/2023/07/12/api bağlantısında API ile ilgili iyi öneriler güzelce derlenmiş; birlikte bakmanızı tavsiye ederim