3 puan yazan GN⁺ 2024-04-27 | 1 yorum | WhatsApp'ta paylaş
  • Increase, API kaynaklarının kullanıcının ürünü nasıl anladığını belirlediğini düşünüyor ve ödeme ağlarının karmaşıklığını gizlemek yerine görünür kılan No Abstractions ilkesini benimsiyor
  • Stripe tarzı soyutlama hızlı entegrasyonda güçlü olsa da, Increase kullanıcıları payment network bilgisi temelinde doğrudan bağlantı ve derin entegrasyon istiyor
  • API, Nacha specification gibi altyapı ağ terminolojisini aynen kullanıyor ve ACH transfer sürecini değişmez alt nesnelerle modelliyor
  • Kullanıcının yapabileceği aksiyonlar büyük ölçüde farklıysa, ach_transfer ve inbound_ach_transfer gibi kaynakları ayırarak ilk başta daha ayrıntılı görünse de uzun vadede öngörülebilirliği artırıyor
  • Soyutlama seviyesi, entegrasyonu geliştiren kişinin alan deneyimine ve ayıracağı çabaya göre belirlenmeli; düşük soyutlama seçildiyse bu ilke sonraki süreçte de korunmalı

API kaynakları kullanıcının zihinsel modelini oluşturur

  • API resource, API’nin isimleridir; bunların adını ve modelini belirlemek, API tasarımının en zor ve en önemli bölümlerinden biridir
  • Hangi kaynakların dışarı açıldığı, kullanıcının ürünün nasıl çalıştığını ve neler yapabileceğini anlamasını sağlayan zihinsel modeli oluşturur
  • Increase, bu kararı yönlendirmek için “No Abstractions” adlı tasarım ilkesini kullanır
  • Stripe tarzı soyutlama ile Increase arasındaki fark

    • Stripe, karmaşık ödeme alanını kullanıcıların kolayca kullanabileceği bir API’ye dönüştüren soyutlama konusunda güçlüdür
    • Birden fazla ödeme ağını PaymentIntent adlı bir API resource ile modeller ve Visa ile Mastercard’ın chargeback reason code farklarını tek bir enum içinde birleştirerek kullanıcının bu iki ağı ayrı ayrı düşünmesini gerektirmez
    • Stripe kullanıcılarının önemli bir bölümü, ödeme sisteminin kendisini değil ürününü inşa eden erken aşama girişimlerdir; kredi kartı ayrıntılarını derinlemesine bilmekten çok hızlıca entegrasyon yapıp asıl ürün geliştirmeye dönmek isterler
    • Increase kullanıcıları payment network konusunda zaten derin bilgi sahibidir, finans teknolojisiyle çalışmaya devam eder ve doğrudan ağ bağlantısı ile derin entegrasyon için Increase’i kullanır
    • Bu kullanıcılar FedACH penceresinin ne zaman kapandığını ve transferin tam olarak ne zaman ulaşacağını bilmek ister; ayrıca ACH transfer’ın Standard Entry Class code değeri değişirse return timing’in de değişebileceğini anlar
    • ACH transfer ile wire transfer’ı tek bir API resource altında toplayıp altyapı ağlarının karmaşıklığını gizlemek, Increase kullanıcıları için basitleştirme değil rahatsızlık yaratır

No Abstractions ilkesinin API’ye yansıma biçimi

  • Gerçek ağ terminolojisini kullanmak

    • Increase, API resource ve attribute adlarını yeniden icat etmek yerine altyapı ağlarının söz varlığını kullanmayı tercih eder
    • Bir ACH transfer API’si oluştururken dışarı açtığı parameter adları Nacha specification içindeki field adlarını izler
  • Değişmez kaynaklar ve lifecycle object

    • Kaynaklar da gerçek dünyadaki olaylara ve mesajlara göre modellenir; bu yaklaşım daha fazla API resource’unu değişmez hale getirir
    • ACH transfer lifecycle’ında gönderilebilecek ağ mesajları kümesinde olduğu gibi, değişmez kaynaklar bir state machine biçimindeki lifecycle object altında gruplanır
    • ach_transfer object’i zamanla değişen bir status field’ına ve lifecycle ilerledikçe oluşturulan çeşitli değişmez sub-object’lere sahiptir
    • Yeni bir ach_transfer için status değeri pending_approval olabilir ve approval, submission, acknowledgement alanları null olabilir
    • FedACH’e gönderildikten sonra status değeri submitted olur; approval, submission, acknowledgement alanları da sırasıyla onay, gönderim ve teyit anındaki değişmez bilgilerle doldurulur
    • submission içinde trace_number ve submitted_at gibi değerler bulunur
  • Kullanım senaryosuna göre kaynakları ayırmak

    • Aynı API resource içinde bile instance bazında yapılabilecek aksiyon kümeleri ciddi biçimde farklıysa, Increase bunu birden fazla kaynağa bölmeyi tercih eder
    • originated ACH transfer ile received ACH transfer için mümkün olan aksiyonlar pratikte birbirinin tersidir; bu yüzden bunlar ach_transfer ve inbound_ach_transfer olarak ayrılır
    • Bu yaklaşım başlangıçta, API dokümantasyonunun sol tarafında çok sayıda kaynak görünmesine yol açacak kadar daha ayrıntılı ve göz korkutucu görünebilir
    • Ancak uzun vadede kaynaklar ile aksiyonlar arasındaki ilişki daha öngörülebilir hale gelir

İlkeler küçük tasarım kararlarını azaltır

  • Karmaşık bir API’yi yıllara yayılan bir süreçte tasarlarken sürekli küçük kararlar alınır; başta belirlenen temel ilkeler bu kararların yarattığı bilişsel yükü azaltır
  • Bir wire transfer Federal Reserve’e gönderilirken gereken Input Message Accountability Data, o transferin küresel olarak benzersiz kimliği gibi davranır
  • Soyutlaması yüksek bir API’de mühendisler bunun daha “kullanıcı dostu” görünmesi için trace_number, reference_number ya da id gibi hangi adın seçileceğini tartışabilir
  • Increase ise field adını doğrudan input_message_accountability_data olarak belirleyip ilerler
  • Kullanıcı bu field’ı ilk gördüğünde adı hemen sezgisel gelmeyebilir, ancak altyapı sistemine nasıl eşlendiğini doğrudan anlamayı kolaylaştırır

Soyutlama seviyesini belirlerken ölçütler

  • No Abstractions her API için uygun bir ilke değildir
  • Uygun soyutlama seviyesi, entegrasyonu geliştiren kişinin alan deneyimine, ürün alanına dair bilgisine ve entegrasyona ayıracağı enerjiye göre değişir
  • Soyutlaması yüksek bir API oluşturulduğunda yeni özellik eklemeden önce derinlemesine düşünmek gerekir
  • Soyutlaması düşük bir API oluşturulduğunda ise bu yöne bağlı kalmak ve sonradan soyutlama ekleme cazibesine direnmek gerekir

1 yorum

 
GN⁺ 2024-04-27
Hacker News yorumları
  • Her zaman ikisini birden sunmak da mümkün
    İnce ayarlı kontrol sağlayan ama derin uzmanlık gerektiren düşük seviyeli bir API sunup, onun üstüne yaygın kullanım senaryolarını birkaç basit işleme eşleyen yüksek seviyeli bir API inşa edebilirsiniz. Zaten bazı müşteriler bu tür yüksek seviyeli katmanı beceriksizce kendileri uyguluyor olabilir
    İki katmanı temiz biçimde ayırmak, düşük seviyeli API'ye soyutlama ekleme ya da yüksek seviyeli API'ye pürüzler ve özel durumlar katma baskısını azaltır. Çünkü müşteri bunları istiyorsa, bunlar zaten başka bir API'de vardır
    Müşterilerin bir katmandan diğerine nasıl geçeceğini öğrenebileceği materyaller de sağlarsanız daha da iyi olur. Ödeme ağının iç yapısını henüz derinlemesine bilmeyen ama o yönde gelişmek isteyen müşterileri de çekebilirsiniz

    • Nadir görülen karmaşık durumları ele alabilen düşük seviyeli API ve onun üstüne kurulmuş, yaygın durumlara yönelik basit bir yüksek seviyeli API olmalı
      Bugün Web File System API'yi kullanıyorum; tek bir string'i bir dosyaya yazmak için 7 fonksiyon çağrısı gerekti ve çoğu asenkrondi. Hata işleme buna dahil değil; ayrıca bunun bir worker içinde yapılması gerekiyor ve worker'ı ayarlamak da benzer şekilde zahmetli. IndexedDB, WebRTC, sıradan DOM işlemlerinde de benzer bir korkunçluk görülebiliyor; Vulkan, DirectX, ffmpeg ise bundan çok daha beter
      Her türlü özel durumu ele almak için karmaşıklık bir ölçüde haklı görülebilir, ama çoğu durum böyle özel durumlar değil
      API tasarımı önce yaygın durumlarda API'yi kullanan kodun nasıl görüneceğini kabaca çizmeyle başlamalı ve bu durumlar mümkün olduğunca basit olmalı. Örneğin fetch API bunu oldukça iyi yaptı; XMLHttpRequest ise hiç yapamadı
      https://developer.mozilla.org/en-US/docs/Web/API/FileSystemS...
      Tüm Web API'leri için birleşik bir kolaylık katmanı API'si olsa iyi olur diye birçok kez düşündüm. Tüm güçlü özellikleri tutarlı bir “standart kütüphane” wrapper'ıyla sarmalayan ve en azından en yaygın kullanım senaryolarını destekleyen bir şey. Modern tarayıcılar çok güçlü, ama her API'nin tasarımı birbirinden farklı ve gereksiz yere öğrenmesi ya da kullanması zor olduğu için bu güç iyi bilinmiyor veya yeterince kullanılmıyor
      DOM için jQuery'nin yaptığına benzer, ama daha az sihirli ve daha az ek özellikli bir biçim iyi olurdu. node.js belli ölçüde tutarlı bir API'ye sahip, ama biraz eski; örneğin Promise desteği düzensiz. Python'ın “Pythonic” API'ler peşinde koşma biçimine de benziyor
    • İstenen yüksek seviyeli API kütüphanenin dışında uygulanabildiğinde bu kalıbı özellikle seviyorum. Böylece düşük seviyeli API'nin yeterince esnek olup olmadığını doğrulayabilir ve siz de kullanıcı konumundan kendi API'nizi bizzat kullanmış olursunuz
      Aracın iç uygulama bakış açısına alıştıkça insanların onu gerçekte nasıl kullandığını unutmak çok kolay
    • Git buna bir örnek
      branch ve checkout gibi yüksek seviyeli “porcelain” komutları, commit-tree ve update-ref gibi düşük seviyeli “plumbing” komutları var
      https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Po...
    • .NET de bu yaklaşımı çok kullanır. Yakın zamanda dosya giriş/çıkışını ele alan bir geliştirme blog yazısı var: https://devblogs.microsoft.com/dotnet/the-convenience-of-sys...
    • Buna karşılık API yüzeyi iki katına çıktığı için dikkate alınması gereken bir ödünleşim var. Çoğu durumda doğru karar olabilir
  • Increase'in neden farklı bir yaklaşım seçtiğini açıklayan kısmı beğendim. Temel şeyleri tasarlarken bağlam çok önemlidir; insanlar genellikle bunu yeterince kabul etmez

  • Buradaki “soyutlama yok” aslında alttaki sistemin terimlerini olduğu gibi kullanın anlamına geliyor ve genel olarak iyi bir adlandırma ilkesi.
    Sorun, zamanla alttaki sistemler birden fazla hâle geldiğinde ve aynı şeye farklı adlar verilmeye ya da daha kötüsü aynı ad farklı şeyler için kullanılmaya başlandığında kaçınılmaz olarak ortaya çıkar. Bu örnekte alttaki ödeme sağlayıcılarının modelleri farklıysa ne yapılacak? Ya da Federal Reserve, Input Message Accountability Data’yı kullanımdan kaldırıp yeni bir kavramla değiştirirse ne olacak?
    Ödeme sektörü, taşımacılıktan ya da ağ protokollerinden çok daha basit olabilir. X.25 tabanlı bir paket anahtarlama ürünü yapıp sonra TCP/IP’yi de desteklemek isterseniz doğru soyutlama ne olur?

    • Dikkatle okuduğun için teşekkürler.
      Kullanımdan kaldırma konusu, şansımıza alttaki sistem çok değişmediği için sorun değil. Input Message Accountability Data ortadan kalkmayacak. Ama örneğin yalnızca Visa’da değil Mastercard’da da kart çıkarmaya başlarsak çatışmalar yaşayacağız.
      Birkaç soyutlama denedik; o noktada da deneyebiliriz. Sürekli koruduğumuz kurallardan biri, “alttaki nesneleri” soyutlamamak, kolaylık için daha üst düzey bileşimler sunmak. Örneğin “Card Payment” diye bir şey gerçekte yok (https://increase.com/documentation/api#card-payments). Bu yalnızca ilgili kart yetkilendirmesini ve takas/uzlaştırma mesajlarını bir araya getirme biçimi. Ama kullanıcı için çok faydalı ve mutabakatı doğrudan yapmak kolay olmadığı için bunu denedik. Yine de alttaki ağ mesajlarına, yani “alttaki nesnelere” ve tüm özgün alanlara API’den erişilebilmesi gerektiğini düşünüyorum.
      Ne yazık ki üzerinde çalıştığım herkese açık API’lerin %100’ü ödeme alanında; keşke başka bir bakış açım da olsaydı.
    • Yazıda “benzer nesneleri birleştirmeyiz” anlamı da açıkça söyleniyor; adlandırma kararını mümkün kılan da bu.
    • “Alttaki sistemin terimlerini olduğu gibi kullanmak” kulağa biraz alan odaklı tasarım gibi geliyor. Ancak bu durumda “alttaki sistem”, gerçek iş alanından ziyade uygulama merkezli tarafa biraz fazla kaymış olabilir.
      DDD’de genellikle iş alanının zaten oluşturduğu adları ve kavramsal modeli izlersiniz. Kendi “iyileştirilmiş” [0] modelinizi ya da terminolojinizi getirmeye çalışırsanız sürtüşme ve yanlış anlamalar doğar, entegrasyon hatası olasılığı artar ve onlarca ya da yüzlerce yıl içinde sınanmış uzmanlığı yok saymış olursunuz.
      [0] https://xkcd.com/793/
  • Yazı güzel.
    Stripe’ı seviyorsanız, ben de bir tasarımcı ve teknik kurucu olarak Stripe’ın sadeliğini ve frontend yetkinliğini şaşırtıcı buluyorum; onlara bakıp sadeleştirme ve yüksek kaliteli bir deneyim sunma becerilerini taklit etmeye çalışabilirsiniz.
    Ama Stripe’ın asıl ustalığı müşterisini iyi tanımasında. Ayrıca müşterinin arzuladığı sadeliği de iyi biliyorlar.
    Bu yazıdan bakınca Increase de aynı şekilde görünüyor; müşterinin neye ihtiyaç duyduğuna benzer derecede keskin bir odaklanmayla harika ürün tasarımı yönergeleri oluşturmuş gibi. Cesaret verici.

    • Stripe’ın API ve ekip oluşturma biçimi: https://www.youtube.com/watch?v=IEe-5VOv0Js
    • Stripe API’de de “bunu potansiyel olarak evrensel yapalım” ile “bunun muhtemelen tek bir pazardaki tek bir ödeme yöntemine uygulanacağını kabul edelim” arasındaki gerilimin görüldüğü yerler var.
      Kişisel olarak ikincisi olduğunda daha çok hoşuma gidiyor, ama bunun içinde estetik bir karar da var.
  • Bu, alan odaklı tasarımın yaygın dil tasarım kalıbına benziyor. Uygulamada alan uzmanlarının gerçek dünyada kullandığı terimleri aynen kullandıran bir yaklaşım.
    https://thedomaindrivendesign.io/developing-the-ubiquitous-l...

    • DDD ortaya çıkmadan çok önce de benzer bir kavram duymuştum. Koddaki isimler ve fiiller problem alanıyla uyuşmuyorsa bunun bir empedans uyumsuzluğu olduğu ve eninde sonunda sorun çıkaracağı söylenirdi.
      Bu yazı bana bir tür utançtan kaçınma tepkisi gibi geliyor. İnsanlar patolojik biçimde “ben yanıldım” ya da “biz yanıldık” demekten hoşlanmıyor; bu yüzden tabağındaki sebzeleri yemiş gibi görünmek için oradan oraya iten bir çocuk gibi metaforları sağa sola sürüklüyorlar.
      Hoare’un Turing Ödülü konuşmasındaki “bariz kusur yok” sözü de aklıma geliyor.
  • Bu, alan odaklı tasarımın yaygın dil kavramını iyi gösteren bir örnek.
    Alan uzmanının anladığı dili kullanmalısınız. Kullanıcı NACHA dosyasını biliyorsa, farklı bir terim kullandığınız anda zihninde bir eşleme tutmak zorunda kalır.
    Buna karşılık Stripe söz konusu olduğunda kullanıcı alan uzmanı değildir; bu yüzden anlaşılabilir olan ve gereksiz ayrıntıları gizleyen bir soyutlama oluşturmak değerlidir. Kullanıcıya dili öğretmeniz gerekiyorsa bunu olabildiğince basit hâle getirmelisiniz.

    • Başka bir deyişle, onlar gerçekleştirmek istedikleri işlem türünün alan uzmanıdır; finansal sistemlerde işlemin nasıl uygulandığının uzmanı değil.
  • POSIX gibi soyutlamalar olmasaydı uygulamaların destekledikleri her dosya sistemi için ayrı adaptör yazması gerekirdi.

  • İlginç.
    Bu kavramın başlığı yanıltıcı. Buradaki “soyutlama yok”, kelimenin tam anlamıyla soyutlama olmadığı anlamına değil, “bu belirli soyutlama kümesini kullanıyoruz, diğerlerini kullanmıyoruz” anlamına geliyor. Açıkladıkları belirli alt küme tartışmaya değer, ama sonuçta elbette bir soyutlama kümesi.
    Örneğin “ACH transferini API’ye dönüştürürken açığa çıkarılan parametre adlarını Nacha spesifikasyonundaki alan adlarına göre veriyoruz” denmiş; spesifikasyonun kendisi bir soyutlamadır.
    “Ağ terimlerini kullanmaya benzer şekilde, kaynakları gerçekleşmiş eylemler ya da gönderilmiş mesajlar gibi gerçek olaylara göre modellemeye çalışıyoruz. Bunun sonucunda daha fazla API kaynağı değişmez oluyor ve durum makinesi ‘yaşam döngüsü nesneleri’ altında gruplanıyor” denmiş; bu anlamdaki değişmezlik ve “yaşam döngüsü nesnesi” de soyutlamadır.
    “Belirli bir API kaynağında kullanıcıların her örnek için gerçekleştirebileceği eylem kümesi çok farklılaşırsa bunu birden çok kaynağa bölme eğilimindeyiz” ifadesi de başka bir soyutlamadır. Yalnızca Stripe API’den farklı bir düzeyde bölüyorlar.
    Sonuçta bu, bir dizi tasarım kararı ve soyutlamadan ibaret; “soyutlama yok” ilkesi değil. En önemli karar mümkün olduğunca az genelleme yapmak gibi görünüyor; genelleme de bir soyutlama türüdür. Muhtemelen “daha az genelleme” daha doğru bir başlık olurdu.

  • “Increase üzerinde oluşturulan kullanıcı başına aylık ücretler, kullanım senaryosuna göre değişir” kısmını gördüm
    Şu anda RAG destekli AI text-to-SQL endpoint’ine herkese açık API erişimi ekliyorum; en büyük sorun fiyatlandırma. Kabaca hangi fiyat aralığından söz edildiğini bilen var mı? Fiyata OpenAI token’ları ya da kullanıcının kendi OpenAI token’ını girmesine izin veren bir yöntem, veritabanı kullanımı ve ileride önbellekleme ile hız sınırı ayarları da yansıtılmalı.

    • Temelde fiyat, maliyete değil değere göre belirlenmeli[1]; bu yüzden müşteri için hangi değeri yarattığını düşünüp oradan başlamak gerekir.
      Örneğin Gong’un birçok kuruluştan yılda 100 bin doların üzerinde ücret aldığını biliyorum; depolama, CPU ve diğer operasyonel giderleri hesaba katsak bile maliyetinin hesaplama maliyetine yakın olması mümkün değil. Muhtemelen arada en az birkaç kat fark vardır. Ancak satış ekipleri geliri çok doğrudan getirdiği için, Gong gibi bir araç biçiminde satın alınabilen kaldıraç anında ve açık biçimde değerlidir.
      [1]: Maliyet artı fiyatlandırmadan kaçınma ilkesinin istisnası, emtia satıyor olmanızdır. Ama sizin durumunuz bu değil!