Hacker DirectoryHacker Directory
Öğrenmeye devam et
ENHTML kaydet ↓

WEB / WEB-17.02

Schemas, serialization ve API evolution

Geçerli JSON, uyumlu contract ve izinli resource erişimi nasıl ayrılır?

CORE · RED · BLUE · ARTIFACT · PRACTICE

1. Core · Mekanizma

Hareket olmadan bütün senaryoları oku

Alan korunur

  1. Request hazır

    Reader public liste için pageSize=2 request’ini hazırladı. Server henüz almadı; parse/schema kararı, tenant filtresi veya response sonucu oluşmadı.

    API reader
    pageSize=2 hazır; response yok
    Atlas API
    Contract v1; request yok
  2. Request geldi

    Request server’a ulaştı; parse ve schema kararı henüz verilmedi.

    API reader
    Request hazır; UI yok
    Atlas API
    Request geldi; validation bekliyor
  3. Local contract

    pageSize=2 integer ve 1–3 aralığındadır; örnekte permission ayrıca kabul edilmiştir. Server bu compatibility dalı için id=book-A, title=Atlas Notları, subtitle=Web rehberi response’unu hazırlar; reader henüz almadı.

    API reader
    Request hazır; UI yok
    Atlas API
    Request kabul edildi; response hazır
  4. Response geldi

    JSON response reader’a ulaştı; old-reader schema ve alan korunumu henüz değerlendirilmedi.

    API reader
    Response geldi; old reader bekliyor
    Atlas API
    Request kabul edildi; response hazır
  5. Old reader

    Old reader required id/title string koşullarını sağlar. Bilmediği subtitle=Web rehberi alanını preserve policy’siyle yorumlamadan korur; bu yerel contract kararı gerçek DOM render ölçümü değildir.

    API reader
    title/subtitle korundu; UI hazır
    Atlas API
    Request kabul edildi; response hazır

Rename kırılır

  1. Request hazır

    Reader public liste için pageSize=2 request’ini hazırladı. Server henüz almadı; parse/schema kararı, tenant filtresi veya response sonucu oluşmadı.

    API reader
    pageSize=2 hazır; response yok
    Atlas API
    Contract v1; request yok
  2. Request geldi

    Request server’a ulaştı; parse ve schema kararı henüz verilmedi.

    API reader
    Request hazır; UI yok
    Atlas API
    Request geldi; validation bekliyor
  3. Local contract

    pageSize=2 integer ve 1–3 aralığındadır; örnekte permission ayrıca kabul edilmiştir. Server bu compatibility dalı için id=book-A, heading=Atlas Notları; title yok response’unu hazırlar; reader henüz almadı.

    API reader
    Request hazır; UI yok
    Atlas API
    Request kabul edildi; response hazır
  4. Response geldi

    JSON response reader’a ulaştı; old-reader schema ve alan korunumu henüz değerlendirilmedi.

    API reader
    Response geldi; old reader bekliyor
    Atlas API
    Request kabul edildi; response hazır
  5. Old reader

    Response JSON olarak okunabilir ama required title yoktur; heading onu yerine getirmez. Old reader sonucu reddeder ve yeni book UI verisi hazırlamaz; eski title başarı gibi tekrar kullanılmaz.

    API reader
    title eksik; UI yok
    Atlas API
    Request kabul edildi; response hazır

Unknown reddi

  1. Request hazır

    Reader public liste için pageSize=2, display=wide request’ini hazırladı. Server henüz almadı; parse/schema kararı, tenant filtresi veya response sonucu oluşmadı.

    API reader
    pageSize=2, display=wide hazır; response yok
    Atlas API
    Contract v1; request yok
  2. Request geldi

    Request server’a ulaştı; parse ve schema kararı henüz verilmedi.

    API reader
    Request hazır; UI yok
    Atlas API
    Request geldi; validation bekliyor
  3. Closed request

    Closed request kuralı bilinmeyen display alanını açıkça reddeder; business read çalışmaz.

    API reader
    Request hazır; UI yok
    Atlas API
    Schema reddi; error hazır
  4. Hata geldi

    Schema rejection reader’a ulaştı; valid response listesi veya UI book oluşmadı.

    API reader
    Schema error geldi; UI yok
    Atlas API
    Schema reddi; error hazır

İkinci sayfa

  1. Continuation hazır

    İlk sayfa A/B daha önce alınmıştır; reader cursor-A ile aynı snapshot’ın ikinci sayfasını ister.

    API reader
    cursor-A request hazır
    Atlas API
    snapshot-A; C kalan; request yok
  2. Cursor geldi

    Server continuation request’ini teslim aldı; cursor bağı ve resource policy henüz pending.

    API reader
    cursor-A request hazır
    Atlas API
    cursor-A geldi; binding bekliyor
  3. Local paginator

    Snapshot/filter/order bağı ile örnekteki permission koşulu sağlanır; C içeren terminal response hazırlanır.

    API reader
    cursor-A request hazır
    Atlas API
    C hazır; nextCursor yok
  4. Son sayfa geldi

    C response’u reader’a ulaştı; nextCursor yokluğu bu seçilmiş contract’ta liste sonunu bildirir.

    API reader
    C alındı; nextCursor yok; tamam
    Atlas API
    C hazır; nextCursor yok

ROLLER · STATE · VERİ AKIŞI

JSON bir değer modeli taşır

Atlas kitap listesi önce uygulamada bir object’tir, sonra UTF-8 JSON text olarak serialize edilir; karşı uç text’i parse ederek yeni bir değer ağacı oluşturur. Bu iki object aynı bellek nesnesi değildir. JSON object alanları anlamsal olarak sıralı değildir; array sırası ise anlam taşır. Top-level yalnız object olmak zorunda değildir. String, number, boolean ve null farklı türlerdir: "2" ile 2 aynı schema sonucunu vermez. Date, class metodu veya database bağlantısı JSON’un değer türlerinden değildir.

Alan yokluğu ile alanın null olması özellikle migration sırasında ayrılır. Seçtiğimiz response’ta subtitle optional ve string-or-null’dır; yoksa client bu kitabın subtitle bilgisinin gönderilmediğini bilir, null ise bilinen boş değeri görür. Native JavaScript serializer object içindeki undefined alanı atabilir; bu, JSON’un her dilde böyle davrandığı anlamına gelmez. Büyük integer ID’leri string tutuyoruz: number üzerinden gidip sonradan string yapmak kaybolmuş hassasiyeti geri getirmez. Aynı isimli JSON member’ların davranışı parser’a göre değişebildiğinden üretici benzersiz isim üretir.

Schema hangi soruyu cevaplar?

Request schema pageSize’ın integer ve 1–3 arasında olduğunu, cursor’ın optional string olduğunu söyler. Response schema books array’inin id ve title gerektirdiğini tanımlar. Bunlar parsing’den sonraki structural kontrollerdir; kullanıcı hangi tenant’ın kitabını okuyabilir sorusu burada çözülmez. JSON Schema’da properties yazmak o alanı required yapmaz; additionalProperties varsayılanı da otomatik kapalı değildir. Fixture’ın request’i unknown alanı reddeder, response okuyucusu unknown alanı korur. Bu iki yönde aynı reject kuralını kullanmak şart değildir.

OpenAPI request/response media ve schema’ları bir arada tarif eder; belgeyi yayınlamak runtime validator çalıştırmak değildir. Burada incelenen current edition 3.2.1’dir ve schema dialect 2020-12 tabanlıdır; older 3.0 nullable alışkanlığını sessizce taşımıyoruz. openapi spec sürümünü, info.version belge sürümünü, /v1 business contract’ını ve deploy build numarasını ayrı okuruz. Artifact yalnız seçilmiş schema fragment’ları gösterir; full OpenAPI description veya bütün JSON Schema keyword’lerini yürüttüğünü iddia etmez.

Evolution ile pagination birlikte düşünülür

v1 title:string alanını koruyarak v1.1 response’a subtitle eklemek, unknown response alanını koruyan reader için uyumludur. Aynı v1’de title’ı heading olarak yeniden adlandırmak remove-and-add etkisidir; eski reader’ın title beklentisi kırılır. Bu fixture’ın major version’ı URI ile seçilir; bu bir ürün kararıdır, HTTP bütün API’leri bu yönteme zorlamaz. Uyumluluk yalnız JSON’un parse olması değildir: eski client derleniyor mu, wire türü aynı mı, eski kullanıcı aynı iş anlamını görüyor mu diye üç ayrı soru sorulur.

Liste baştan paginated tanımlanır. Tenant filtresinden sonra sabit snapshot’taki A, B, C kitapları id sırasına alınır; pageSize=2 ilk iki kitabı ve opaque cursor-A’yı döndürür, ikinci sayfa C ile biter. Cursor bu seçilmiş snapshot, filter ve order’a bağlı yerel continuation referansıdır; kitap okuma izni değildir. Az item dönmesi tek başına son sayfa kanıtı sayılmaz; bu contract’ta nextCursor yokluğu bitiştir. Canlı listede insert/delete, stable tie-breaker, snapshot ömrü ve cursor expiry ayrı tasarlanır; üç sabit kayıt bunları otomatik çözmez.

2. Red · Güven ve etki

Hareket olmadan bütün senaryoları oku

Request field

  1. Üretici ve reader rolü ayrıldı

    Request profili pageSize ve cursor dışında alan kabul etmez. display=wide içeren request row’u bu closed contract’ın negatif örneğidir.

    Contract değişikliği
    Unknown request=reject
    Schema ve reader policy
    Schema gate bekliyor
    Consumer kararı
    Consumer kabul etmedi
  2. Syntax ile schema koşulu ayrıldı

    pageSize=2 uygun olsa da display alanı additionalProperties=false ile uyuşmaz. Bir doğru alan diğer alanların kabulünü sağlamaz.

    Contract değişikliği
    pageSize=2
    Schema ve reader policy
    Shape inceleniyor
    Consumer kararı
    Kabul bekliyor
  3. Contract uyuşmazlığı belirlendi

    requestRows unknown-field için expected=false bildirir. Policy reject’tir; bilinmeyen alanı sessiz silip aynı request’i kabul etme olarak gösterilmez.

    Contract değişikliği
    Known contract mismatch
    Schema ve reader policy
    Expected=false
    Consumer kararı
    Consumer gate kapalı
  4. Dönüşüm policy’si açık bırakıldı

    Bu contract request unknown alanlarını reddederken response unknown alanlarını korur. İki yönün aynı normalization policy’sine sahip olduğu varsayılmaz.

    Contract değişikliği
    Request/response policy ayrı
    Schema ve reader policy
    Sessiz veri kaybı yok
    Consumer kararı
    Uyumsuz branch ayrı
  5. Version etiketi kararı değiştirmedi

    contractVersion=v1 ve documentVersion=1.1 belgeleri tanımlar. Bu etiketler reddedilen shape için otomatik kabul veya business permission oluşturmaz.

    Contract değişikliği
    Versions: v1 / 1.1
    Schema ve reader policy
    Shape mismatch retained
    Consumer kararı
    Permission ayrı
  6. Consumer beklentisi sınırda korundu

    Ret gerekçesi closed request contract’tır. Bu sonuç actor’a ait tenant izninin reddedildiğini veya normal iki sayfanın bozulduğunu tek başına söylemez.

    Contract değişikliği
    Contract failure known
    Schema ve reader policy
    Ret nedeni sınırlı
    Consumer kararı
    Yanlış kabul yok

Title rename

  1. Üretici ve reader rolü ayrıldı

    Eski reader id ve title alanlarını zorunlu bekler. renamed response, id=book-A ve heading=Atlas Notları içerir; title alanı bulunmaz.

    Contract değişikliği
    Required: id, title
    Schema ve reader policy
    Schema gate bekliyor
    Consumer kararı
    Consumer kabul etmedi
  2. Syntax ile schema koşulu ayrıldı

    heading string olabilir ve JSON okunabilir. Ancak yeni field’ın bulunması eski required title alanının eksikliğini gidermez.

    Contract değişikliği
    heading present; title absent
    Schema ve reader policy
    Shape inceleniyor
    Consumer kararı
    Kabul bekliyor
  3. Contract uyuşmazlığı belirlendi

    compatibilityRows renamed için expected=false bildirir. Unknown response field’larını preserve etmek, zorunlu title yokluğunu geçerli kılmaz.

    Contract değişikliği
    Known contract mismatch
    Schema ve reader policy
    Expected=false
    Consumer kararı
    Consumer gate kapalı
  4. Dönüşüm policy’si açık bırakıldı

    Normal response’ta subtitle eklenmesi title’ı silmediği için kabul edilebilir. Additive field ile required field rename aynı compatibility değişikliği değildir.

    Contract değişikliği
    Request/response policy ayrı
    Schema ve reader policy
    Sessiz veri kaybı yok
    Consumer kararı
    Uyumsuz branch ayrı
  5. Version etiketi kararı değiştirmedi

    contractVersion=v1 ve documentVersion=1.1 belgeleri tanımlar. Bu etiketler reddedilen shape için otomatik kabul veya business permission oluşturmaz.

    Contract değişikliği
    Versions: v1 / 1.1
    Schema ve reader policy
    Shape mismatch retained
    Consumer kararı
    Permission ayrı
  6. Consumer beklentisi sınırda korundu

    Ret gerekçesi required title’ın kaybıdır. Bu sonuç bütün yeni optional alanların yasak olduğu anlamına gelmez; preserve modu yalnız tanınmayan alanı yorumlamadan tutar.

    Contract değişikliği
    Contract failure known
    Schema ve reader policy
    Ret nedeni sınırlı
    Consumer kararı
    Yanlış kabul yok

VARSAYIM · SINIR · KONTROL

Format kabulünden yanlış sonuç çıkarmak

Bir request parse edildiğinde yalnız text’in seçilen parser tarafından okunabildiği öğrenilir. Schema geçtiğinde pageSize ve cursor şeklinin kabul edildiği öğrenilir. Bu iki olumlu sonuç tenant bağını, subject’in read action’ını veya cursor’ın hâlâ geçerli snapshot’a ait olduğunu kanıtlamaz. Riskli varsayım, bütün yeşil kutuları tek bir "valid request" etiketine sıkıştırmaktır. Artifact bu nedenle parse, schema, continuation ve business permission sonuçlarını ayrı ledger alanları olarak tutar; reddin hangi aşamada olduğunu reader görebilir.

Unknown field davranışı görünmeyen veri kaybı yaratabilir. Yeni response’ta subtitle bulunurken adapter yalnız id/title’ı kopyalayıp original’ı atarsa sonraki serialize işleminde subtitle yok olur. Bu veri kaybı JSON veya schema’nın zorunlu sonucu değildir; adapter’ın seçtiği dönüşümdür. Fixture’ın preserve modu extra alanı round trip’te tutar; closed request ise unknown alanı açık hata ile reddeder. Her iki policy de bilinçli olabilir, ama sessizce preserve yazıp discard davranmak compatibility sözleşmesini bozar.

Küçük değişiklik büyük iş anlamını bozabilir

Eski client boş books gördüğünde bütün arşivin boş olduğunu düşünüyorsa sonradan pagination eklemek yalnız yeni field eklemek değildir. Kullanıcı ilk sayfayı görüp eksik listeyi tam sanabilir. Title’ın türünü string’den object’e çevirmek de JSON geçerli kaldığı hâlde eski UI’ı bozar. Compatibility tablosunda bu yüzden required isim, wire türü, alan korunumu ve collection’ın tamamlandığı bilgisi ayrı değerlendirilir; hangi eski beklentinin kırıldığı açıkça gösterilir.

Opaque cursor okunamıyor diye authenticated sayılmaz. Continuation bilgisi doğru olsa bile her sayfanın resource erişimi güncel policy’ye göre değerlendirilmeli; cached result aynı tenant ve filter bağını taşımalıdır. Sadece page count veya nextCursor varlığına bakarak izin sonucu çıkarılmaz. Sabit fixture’daki başarılı iki sayfa yalnız seçilen üç kayıt için kanıttır; concurrent update, storage transaction veya cursor imza uygulaması hakkında sonuç üretmez.

3. Blue · Güvenli geliştirme ve savunma

Hareket olmadan bütün senaryoları oku

Null ve absence

  1. Alan sözleşmesi incelendi

    Book schema’sı subtitle için string veya null kabul eder; alan required değildir. title ise required string’dir. Optional olmak ve null kabul etmek farklı koşullardır.

    Contract gözlemi
    subtitle: string | null
    Reader/page karşılaştırması
    title: required string
    İnceleme sonucu
    Reader sonucu bekliyor
  2. Null response row’u seçildi

    null-subtitle response id ve title’ı korur, subtitle=null içerir. Bu açık null değeri, producer’ın alanı hiç göndermediği durumla aynı representation değildir.

    Contract gözlemi
    subtitle present: null
    Reader/page karşılaştırması
    Required fields present
    İnceleme sonucu
    Round trip incelenmedi
  3. Uyum sonucu schema’ya bağlandı

    Bu row için expected=true’dur. Null kabulü schema’da açık olduğu için sonuç olumlu; reader’ın bütün bilinmeyen alanları anladığı sonucu çıkarılmaz.

    Contract gözlemi
    Expected=true
    Reader/page karşılaştırması
    Null allowed explicitly
    İnceleme sonucu
    Interpretation scope sınırlı
  4. Eski reader preserve modu okundu

    oldReaderSchema subtitle’ı tanımlamaz ama additionalProperties=true kullanır. oldReaderUnknownFields alanı, subtitle’ın yorumlanmadan korunacağını söyler.

    Contract gözlemi
    Old reader: id/title
    Reader/page karşılaştırması
    subtitle preserved
    İnceleme sonucu
    Subtitle meaning not inferred
  5. Sessiz field kaybı kontrol edildi

    Reader yalnız id/title’ı yeni object’e kopyalayıp original alanları atarsa preserve sözleşmesi bozulur. Kabul edilen unknown field ile round trip’te korunan field ayrı gözlenmelidir.

    Contract gözlemi
    Acceptance ≠ preservation
    Reader/page karşılaştırması
    Round trip evidence gerekli
    İnceleme sonucu
    Silent discard kabul edilmez
  6. İnceleme sonucu açık tutuldu

    Contract, null ve absence ayrımını tanımlar; eski reader subtitle’ı yorumlamaz. Bu bilgi runtime’da tam schema validator veya bütün client’ların çalıştığı kanıtı değildir.

    Contract gözlemi
    Null acceptance known
    Reader/page karşılaştırması
    Preserve contract known
    İnceleme sonucu
    All clients unobserved

Cursor bağı

  1. Sayfa context’i seçildi

    Profile snapshot-A, tenant-A ve id ASC sıralamasını seçer. records içinde tenant-B kaydı da vardır; continuation değerlendirmesi tenant filtresinden ayrı okunamaz.

    Contract gözlemi
    snapshot-A / tenant-A
    Reader/page karşılaştırması
    Order: id ASC
    İnceleme sonucu
    Page sonuçları bekliyor
  2. İlk sayfa kaydı okundu

    pageSize=2 isteğine book-A ve book-B gelir, nextCursor=cursor-A bulunur. İlk iki kayıt bütün collection’ın tamamı diye raporlanmaz.

    Contract gözlemi
    Page 1: A, B
    Reader/page karşılaştırması
    nextCursor=cursor-A
    İnceleme sonucu
    Collection incomplete
  3. Cursor ledger bağları eşleştirildi

    cursor-A snapshot-A, tenant-A, id ASC ve nextIndex=2 taşır. Cursor yalnız devam konumunu seçer; ayrıca resource permission vermez.

    Contract gözlemi
    Cursor context matched
    Reader/page karşılaştırması
    nextIndex=2
    İnceleme sonucu
    Cursor ≠ permission
  4. Terminal sayfa sonucu ayrıldı

    İkinci sayfada yalnız book-C vardır ve nextCursor yoktur. Bu bitiş sonucu verilen snapshot’ın üç tenant-A kaydı için geçerlidir, başka snapshot için değil.

    Contract gözlemi
    Page 2: C
    Reader/page karşılaştırması
    nextCursor absent
    İnceleme sonucu
    Selected snapshot complete
  5. Filter ve permission kanıtı ayrıldı

    tenant-B kaydının bu sayfalarda bulunmaması filter sonucudur. permissionDeclared=true ise ayrı bir önkoşuldur; cursor’ın opaque görünmesi authentication kanıtı değildir.

    Contract gözlemi
    Tenant filter known
    Reader/page karşılaştırması
    Permission declared separately
    İnceleme sonucu
    Opaque ≠ authenticated
  6. Snapshot kapsamı korundu

    Bu iki sayfa sabit snapshot üzerinde sonuç verir. Concurrent update, cursor signature veya storage transaction davranışı için yeni kanıt gerekir; terminal sayfa bunları kanıtlamaz.

    Contract gözlemi
    Fixed snapshot only
    Reader/page karşılaştırması
    Concurrent storage unobserved
    İnceleme sonucu
    Completion bounded

UYGULAMA · GÜVENLİK KONTROLÜ · DOĞRULAMA

Üretici ve reader policy’sini birlikte yaz

Her field için isim, wire type, required/optional, null kabulü, default ve unknown-field policy yazılır. Producer title’ı hâlâ üretirken reader subtitle’ı tanımadan koruyabilir; bu round-trip contract açıkça test edilir. Request’te closed schema tercih edildiğinde bilinmeyen alan için anlaşılır hata dönülür; normalize ederken sessiz silme default değildir. Number domain’i, string boyutu ve collection limit’i parser sınırlarıyla birlikte belirlenir; JSON’un number grammar’ı business ölçü birimini veya hassasiyeti kendiliğinden garanti etmez.

Regression listesi eski request/new server ile yeni response/old reader’ı ayrı çalıştırır. Aynı normal book kaydıyla subtitle ekleme başarılı, title rename başarısız ve null subtitle açık kabul olmalıdır. Document version artması bu sonuçların yerine geçmez. Schema keyword’lerini anlayan validator seçilir ve supported dialect pin edilir; code generator çıktısı ile gerçek runtime response karşılaştırılır. Generated client’ın enum veya strict deserializer tercihleri de compatibility review’e girer.

Sayfanın sınırı ile iznin sınırını ayır

Pagination contract’ında default/max page size, sıralama tie-breaker’ı, nextCursor bitiş anlamı ve cursor’a bağlı filter/order açıklanır. Seçtiğimiz fixture in-memory sabit snapshot’tır; tenant filter pagination’dan önce uygulanır ve cursor-A sadece ikinci sayfanın başlangıcını seçer. Aynı continuation ile filter değişirse listeden tahminî yeni sayfa üretmek yerine bağ uyuşmazlığı açık hata olur. Token expiry ve snapshot kaybı client’a yeniden başlama davranışıyla anlatılır; gerçek storage implementation burada verilmiş sayılmaz.

Blue evidence yalnız schema-pass sayısı değildir. Round trip’te alanların korunması, terminal cursor’ın yokluğu, eski UI’ın title okumaya devam etmesi ve her sayfa için ayrı policy sonucu gözlenir. Daha büyük sistemlerde cache key, cancellation, timeout ve authorization değişikliğinin continuation’a etkisi de incelenir. Sahne request ve response arrival anlarını server’daki local validator/paginator kararlarından ayırır. JSON text’in parse edilmesi ise continuation bağının ve business permission’ın doğrulandığı anlamına gelmez.

ŞİMDİ VAKAYA UYGULA

Yeni alan veya cursor hangi eski beklentiyi korumak zorunda?

Request unknown alanlarını reddeder, response unknown alanlarını korur. Pagination sabit tenant/snapshot context’inde ilerler; shape ve continuation permission yerine geçmez.

Vakadaki API contract’ı ile browser’ın okuyabildiği representation’ı ayrı incele. Envelope, data, error ve resource permission için hangi gözlemin yeterli olduğunu tartış.

Bağlı vaka dosyasını aç ↗

ÖRNEĞİ BİRLİKTE OKUYALIM

Atlas API contract ve continuation ledger

Bir alan seç; hangi sonucu desteklediğini ve nerede yetersiz kaldığını incele.

Ham kaydı ve alan açıklamalarını oku
{
  "profile": {
    "specEdition": "OpenAPI 3.2.1",
    "schemaDialect": "https://json-schema.org/draft/2020-12/schema",
    "contractVersion": "v1",
    "documentVersion": "1.1",
    "snapshot": "snapshot-A",
    "tenant": "tenant-A",
    "order": "id ASC",
    "unknownRequest": "reject",
    "unknownResponse": "preserve",
    "permissionDeclared": true
  },
  "requestSchema": {
    "type": "object",
    "properties": {
      "pageSize": {
        "type": "integer",
        "minimum": 1,
        "maximum": 3
      },
      "cursor": {
        "type": "string"
      }
    },
    "required": [
      "pageSize"
    ],
    "additionalProperties": false,
    "$schema": "https://json-schema.org/draft/2020-12/schema"
  },
  "bookSchema": {
    "type": "object",
    "properties": {
      "id": {
        "type": "string"
      },
      "title": {
        "type": "string"
      },
      "subtitle": {
        "type": [
          "string",
          "null"
        ]
      }
    },
    "required": [
      "id",
      "title"
    ],
    "additionalProperties": true,
    "$schema": "https://json-schema.org/draft/2020-12/schema"
  },
  "records": [
    {
      "id": "book-A",
      "title": "Atlas Notları",
      "tenant": "tenant-A"
    },
    {
      "id": "book-B",
      "title": "Ağ Defteri",
      "tenant": "tenant-A"
    },
    {
      "id": "book-C",
      "title": "HTTP Notları",
      "tenant": "tenant-A"
    },
    {
      "id": "other-book",
      "title": "Başka Arşiv",
      "tenant": "tenant-B"
    }
  ],
  "cursorLedger": {
    "cursor-A": {
      "snapshot": "snapshot-A",
      "tenant": "tenant-A",
      "order": "id ASC",
      "nextIndex": 2
    }
  },
  "pages": [
    {
      "request": {
        "pageSize": 2
      },
      "response": {
        "books": [
          {
            "id": "book-A",
            "title": "Atlas Notları"
          },
          {
            "id": "book-B",
            "title": "Ağ Defteri"
          }
        ],
        "nextCursor": "cursor-A"
      }
    },
    {
      "request": {
        "pageSize": 2,
        "cursor": "cursor-A"
      },
      "response": {
        "books": [
          {
            "id": "book-C",
            "title": "HTTP Notları"
          }
        ]
      }
    }
  ],
  "compatibilityRows": [
    {
      "case": "normal",
      "response": {
        "id": "book-A",
        "title": "Atlas Notları",
        "subtitle": "Web rehberi"
      },
      "expected": true,
      "unknownMode": "preserve"
    },
    {
      "case": "renamed",
      "response": {
        "id": "book-A",
        "heading": "Atlas Notları"
      },
      "expected": false,
      "unknownMode": "preserve"
    },
    {
      "case": "null-subtitle",
      "response": {
        "id": "book-A",
        "title": "Atlas Notları",
        "subtitle": null
      },
      "expected": true,
      "unknownMode": "preserve"
    }
  ],
  "requestRows": [
    {
      "case": "normal",
      "value": {
        "pageSize": 2
      },
      "expected": true
    },
    {
      "case": "unknown-field",
      "value": {
        "pageSize": 2,
        "display": "wide"
      },
      "expected": false
    },
    {
      "case": "wrong-type",
      "value": {
        "pageSize": "2"
      },
      "expected": false
    },
    {
      "case": "integer-number",
      "value": {
        "pageSize": 2.0
      },
      "expected": true
    },
    {
      "case": "fractional",
      "value": {
        "pageSize": 2.5
      },
      "expected": false
    }
  ],
  "jsonFixtures": {
    "text": "{\"id\":\"book-A\",\"title\":\"Atlas Notları\",\"subtitle\":null}",
    "largeId": "9007199254740993",
    "nullDistinctFromAbsent": true,
    "duplicateNamesProducerForbidden": true
  },
  "boundaries": {
    "schemaGrantsPermission": false,
    "cursorGrantsPermission": false,
    "actualFullValidator": false,
    "concurrentStorage": false
  },
  "oldReaderSchema": {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "properties": {
      "id": {
        "type": "string"
      },
      "title": {
        "type": "string"
      }
    },
    "required": [
      "id",
      "title"
    ],
    "additionalProperties": true
  },
  "oldReaderUnknownFields": "preserve without interpreting subtitle"
}
requestSchema
Seçilmiş closed request kuralları; bütün validator değil.
bookSchema
Current producer’ın book schema’sı; subtitle string veya null olabilir.
pages
Tenant filtresi sonrası sabit snapshot’ın iki sayfası.
compatibilityRows
Alan ekleme, rename ve null için eski reader beklentisi.
cursorLedger
Opaque alias’ın local snapshot/filter/order continuation bağı.
oldReaderSchema
Eski reader yalnız id/title bilir; unknown subtitle’ı yorumlamadan korur.

Inert schema fragment’ları ve sabit in-memory kayıtlar kullanılır. JSON serialization ve contract kararları, tam OpenAPI/JSON Schema validator veya gerçek storage implementation kanıtı değildir.

İki yanlış kanı

JSON parse olduysa request geçerlidir.

Parse yalnız syntax/değer üretir; schema, continuation ve resource policy ayrı kararlardır.

Yeni response alanı her eski client için uyumludur.

Reader’ın reject/preserve/discard davranışı ve round-trip contract kontrol edilmeden bu sonuç çıkarılmaz.

SIRA SENDE

Bu hikâyeyi sen çöz.

Sonucu tahmin et, kanıtın sınırını bul ve nedenini kendi cümlenle anlat.

Request içindeki pageSize değeri "2" string’i, fakat sözleşme integer istiyor. Input kabul edilir mi?

  1. Evet. Sayı gibi görünen bütün JSON string’leri integer’dır.
  2. Hayır. wrong-type sonucu expected=false.
Gerekçeli cevabı oku

Hayır. wrong-type sonucu expected=false. — Bu schema "2" string’ini sessizce integer’a dönüştürmez. Hem type hem size sınırları sağlanmalıdır.

Eski reader yeni subtitle alanıyla karşılaşınca bu compatibility policy’si ne ister?

Dersten seçilen artifact alanları; öğretim örneği

oldReaderSchema.additionalProperties
true
oldReaderUnknownFields
preserve without interpreting subtitle
compatibilityRows[0]
{"case":"normal","response":{"id":"book-A","title":"Atlas Notları","subtitle":"Web rehberi"},"expected":true,"unknownMode":"preserve"}
  1. Alan korunur; eski reader ona yeni bir anlam yüklemez.
  2. Bütün unknown alanlar her yönde otomatik silinir.
Gerekçeli cevabı oku

Alan korunur; eski reader ona yeni bir anlam yüklemez. — additionalProperties=true ve preserve policy’si sessiz veri kaybını önler. Request tarafındaki strict policy ayrı bir sözleşmedir.

cursor-A snapshot, tenant ve order taşıyor. Neden bu continuation bilgisi resource erişim izni değildir? subtitle=null neden alanın bulunmamasıyla aynı değildir?

Gerekçeli cevabı oku

Cursor hangi kayıt sırasından devam edileceğini belirler. Güncel tenant/resource izni yine ayrıca doğrulanmalıdır. Book schema subtitle=null değerine izin verir; absent ise alanın hiç bulunmamasıdır. Consumer bu farkı sözleşmesine göre korumalıdır. Bu örnek tam JSON Schema validator veya concurrent storage çalıştırmaz.

□ Cursor’ı erişim izni sağlayan capability saymadım.

□ Null değeri ile alanın bulunmamasını ayırdım.

Uygulama konsolunu aç · request / response / browser
Request, URL, cookie ve DOM örneklerini ayrıca aç

Kaynaklar ve kapsam