---
title: 'Sürekli Tümleştirme ve Teslimat'
source: 'https://academia.sh/tr/kurslar/ci-cd'
language: tr
updated: '2026-08-17T04:40:31+00:00'
license: 'CC BY-SA 4.0'
lessons: 21
outcomes:
    - 'Yapı, test ve yayın adımlarını tek bir hatta bağlar'
    - 'Dağıtım stratejilerini risk düzeyine göre seçer'
    - 'Hattın kendi güvenliğini ve çıktı bütünlüğünü sayıya bağlar'
---

# Sürekli Tümleştirme ve Teslimat

İstenen durumu kimin, neye bakarak ve hangi denetimden geçirerek değiştirdiği: sık birleştirme ve hızlı geri bildirim, teslimat ile dağıtımın ayrımı, derleme, test, paketleme ve yayın kapılarından oluşan hat aşamaları, yapı çıktısı sürümleme ve saklama, önbellek ve paralellikle hat süresinin kısaltılması, çalıştırıcı seçimi, hattın kendisinin bildirimsel tanımı, tetikleyiciler, dal stratejisi, ortam terfisi, aşamalı yayın, iki ortamlı dağıtım, özellik bayraklarıyla dağıtım ve yayının ayrılması, geri alma planı, veri göçüyle eşgüdüm, depo tabanlı istenen durum, çekme tabanlı dağıtım, sır yönetimi, bağımlılık envanteri, yapı çıktısı imzalama ve hat güvenliği.

## Hat Tasarımı

Değişikliğin üretime giden yolundaki kapıların kurulması: sık birleştirmenin çatışma ve geri bildirim üzerindeki etkisi, sürekli teslimat ile sürekli dağıtımın ayrımı, derleme, test, paketleme ve yayın kapılarının tuttuğu ve kaçırdığı kusur, yapı çıktısının sürümlenmesi ve saklanması, önbellek ve paralellikle hat süresinin kısaltılmasının sınırı, yönetilen ve kendi barındırılan çalıştırıcıların bedeli, hattın bildirimsel tanımı ve olay, zaman ile elle tetikleme.

### Sürekli Tümleştirme

Source: https://academia.sh/tr/kurslar/ci-cd/surekli-tumlestirme

Birleştirme aralığının taranması: aralık büyüdükçe çatışan dosya, çözüm turu ve çözümde doğan hata nasıl büyüyor, sıklığın kendi bedeli kaç koşum ve kaç çalıştırıcı dakikası tutuyor, birleştirme kapısı neyi yalnız kendisi yakalıyor ve kaç temiz değişikliği boşuna durduruyor.

Önceki kurs bir küme bıraktı: istenen durum bir yerde yazılı, uzlaştırıcı onu sürekli
uyguluyor, gerçek durum sapınca geri çekiliyor. Kapanışta duran soru şuydu — o istenen
durumu **kim, neye bakarak ve hangi denetimden geçirerek** değiştiriyor? Bu kurs o yolu
kurar. Değişikliğin geliştiricinin makinesinden üretime giden hattı üzerinde duran, geçmesine
izin veren ya da vermeyen her adım bir **kapı**dır ve bu kursun ölçü birimi kapıdır.

Yoldaki ilk kapı en eskisidir: bir değişikliğin ana dala girmesi. **Sürekli tümleştirme**
(continuous integration), her geliştiricinin işini ana dala sık aralıklarla döndürmesi ve her
dönüşte otomatik bir denetimden geçirmesidir. "Sık" bir sayı değil, bir eksendir; bu ders o
ekseni tarar. Taranan büyüklük **birleştirme aralığı**dır: bir dalın ana daldan ayrı yaşadığı
süre. Aralık büyüdükçe iki şey birlikte büyür — çatışan dosya sayısı ve çatışmayı çözerken
doğan hata. Aralık küçüldükçe başka bir şey büyür — koşum sayısı ve çalıştırıcı zamanı.
Sıklığın bedava olmadığı yer burasıdır. Süreç Modelleri konusunda ölçülen **hata bulma
gecikmesi** burada yeniden ölçülmez; bu dersin ölçtüğü şey çatışmanın kendisidir.

#### Depo Modeli

Ölçüm, bölgesel ölçüm ağının deposu üzerinde yapılır: su sayacı okumalarını toplayan servis,
okumaları doğrulayan servis, faturalama ve iş emri servisleri. **Bu ağ kurgudur; aşağıdaki
depo da bir modeldir, gerçek bir depo taranmamıştır.** Süreler model dakikasıdır ve
ölçümün koştuğu makineden bağımsızdır.

**HT1 — depo dört servise bölünmüş 120 dosyadır; sekiz geliştirici yirmi iş günü çalışır,
gün 480 model dakikasıdır.** Her servisin ilk üç dosyası **arayüz dosyası**dır: başka
servisin beklentisini taşır.

**HT2 — bir geliştirici saatte 0,5 dosyaya dokunur.** Toplam iş aralıktan bağımsızdır:
her tarama satırında 640 dosya dokunuşu vardır, değişen yalnızca bu işin kaç birleştirmeye
bölündüğüdür.

**HT3 — dosyaların beşte biri sıcaktır ve sekiz kat sık dokunulur; geliştirici kendi
servisini yüzde 75 tercih eder.** Üreteç kendi yazılmıştır, tohum görünürdür.

**HT4 — çatışan dosya başına çözüm 20 model dakikasıdır.** Çözüm sürerken ana dal ilerler;
o sırada aynı dosyalara dokunan yeni bir birleşme olursa yeni bir tur açılır, en çok beş tur.

**HT5 — çatışan dosya başına çözümde hata doğma olasılığı 0,09'dur.** Çözümde doğan bir hata
ile kapının kaçırdığı bir arayüz uyuşmazlığının sonradan bulunup düzeltilmesi 180 model
dakikasıdır.

**HT6 — bir hat koşumu 14 model dakikası çalıştırıcı zamanı harcar; koşumların yüzde ikisi
kararsız kırmızı verir** ve temiz bir değişikliği boşuna durdurup bir koşum daha ister.

```js
// depo.mjs — kurgu bolgesel olcum agi deposunun modeli: servis, dosya, gelistirici
// Tohumlu ureteg: 32 bitlik dogrusal esleme; disaridan kutuphane kullanilmaz.
export const uretec = (tohum) => () => {
  tohum = (tohum * 1664525 + 1013904223) >>> 0;
  return tohum / 4294967296;
};

// HT1: dort servis, servis basina dosya sayisi; ilk uc dosya arayuz dosyasidir.
export const servisler = [
  { ad: 'toplayici', dosya: 38 },
  { ad: 'dogrulayici', dosya: 26 },
  { ad: 'faturalama', dosya: 31 },
  { ad: 'is-emri', dosya: 25 },
];

export const dosyalar = servisler.flatMap((s, si) =>
  Array.from({ length: s.dosya }, (_, i) => ({
    ad: `${s.ad}/${i}`, servis: si, arayuz: i < 3,
    // HT3: dosyalarin bese biri sicaktir ve sekiz kat sik dokunulur.
    agirlik: i % 5 === 0 ? 8 : 1,
  })));

// Agirliga gore dosya secimi: gelistirici kendi servisini yuzde 75 tercih eder.
export const dosyaSec = (r, gelistirici) => {
  const kendi = r() < 0.75;
  const havuz = kendi ? dosyalar.filter((d) => d.servis === gelistirici % 4) : dosyalar;
  const top = havuz.reduce((a, d) => a + d.agirlik, 0);
  let x = r() * top;
  for (const d of havuz) { x -= d.agirlik; if (x <= 0) return d; }
  return havuz[havuz.length - 1];
};

export const GELISTIRICI = 8;
export const GUN = 20, GUN_DK = 480;           // HT1: yirmi is gunu, gunde 480 model dakikasi
export const HIZ = 0.5;                        // HT2: saatte 0,5 dosya dokunusu
export const COZUM_DK = 20;                    // HT4: catisan dosya basina cozum suresi
export const HATA_OLASILIK = 0.09;             // HT5: cozumde hata dogma olasiligi
export const SONRAKI_DK = 180;                 // HT5: gec bulunan bir hatanin bedeli
export const KARARSIZ = 0.02;                  // HT6: kosumlarin yuzde ikisi yanlis tutar
export const KOSUM_DK = 14;                    // HT6: bir hat kosumunun calistirici zamani
```

#### Aralık Taraması

Tarama altı aralık için aynı işi koşturur: iki saat, dört saat, bir gün, iki gün, dört gün ve
on iş günü. Her satır aynı tohumla başlar, yani karşılaştırılan şey aralıktır.

```js
// aralik.mjs — birlestirme araligi tarandiginda catisma ve kapinin bedeli
import * as D from './depo.mjs';

const kos = (saat, tohum = 7391) => {
  const r = D.uretec(tohum);                       // her satir ayni tohumla baslar
  const T = saat * 60, ufuk = D.GUN * D.GUN_DK, n = D.HIZ * saat;
  const govde = [], olay = [];                     // govde: birlesmis dosyalar {t, dosya, ...}
  for (let dev = 0; dev < D.GELISTIRICI; dev++) {
    for (let k = 0; k < ufuk / T; k++) olay.push({ dev, bas: k * T + dev * (T / D.GELISTIRICI) });
  }
  olay.sort((a, b) => a.bas - b.bas);
  let birlestirme = 0, catisan = 0, tur = 0, hata = 0, anlamsal = 0, yanlis = 0, kosum = 0, cozumDk = 0;

  for (const o of olay) {
    const kume = new Set(), arayuz = new Set();
    while (kume.size < n) {                        // dal ayri n dosyaya dokunur
      const d = D.dosyaSec(r, o.dev);
      if (kume.has(d.ad)) continue;
      kume.add(d.ad);
      if (d.arayuz) arayuz.add(d.servis);
    }
    birlestirme++;
    let t = o.bas + T, pencere = o.bas, adim = 0;
    while (true) {
      const rakip = govde.filter((g) => g.t > pencere && g.t <= t && g.dev !== o.dev);
      const c = new Set(rakip.filter((g) => kume.has(g.dosya)).map((g) => g.dosya)).size;
      kosum++;
      if (r() < D.KARARSIZ) { yanlis++; kosum++; }  // kararsiz kirmizi: temiz degisiklik durur
      if (adim === 0) {                             // metinsel catismasi olmayan arayuz uyusmazligi
        anlamsal += new Set(rakip.filter((g) => g.arayuz && arayuz.has(g.servis) && !kume.has(g.dosya))
          .map((g) => g.dosya)).size;
      }
      if (c === 0 || adim >= 5) break;
      catisan += c; tur++;
      for (let i = 0; i < c; i++) if (r() < D.HATA_OLASILIK) hata++;
      const sure = c * D.COZUM_DK;
      cozumDk += sure; pencere = t; t += sure; adim++;
    }
    for (const ad of kume) {
      const d = D.dosyalar.find((x) => x.ad === ad);
      govde.push({ t, dosya: ad, dev: o.dev, servis: d.servis, arayuz: d.arayuz });
    }
  }
  const sonrakiBedel = (hata + anlamsal) * D.SONRAKI_DK;
  return { saat, n, birlestirme, sonrakiBedel, catisan, tur, hata, anlamsal, yanlis, kosum,
    cozumDk, kosumDk: kosum * D.KOSUM_DK, dokunus: govde.length };
};

const s = (x, w) => String(x).padStart(w);
const satir = [2, 4, 8, 16, 32, 80].map((h) => kos(h));

const f = (x, w) => String(x.toFixed(2)).padStart(w);
console.log(`${'aralik'.padEnd(9)}${s('dal dosya', 10)}${s('birlestirme', 12)}${s('catisan', 9)}${s('catisan/blt', 12)}${s('tur/blt', 9)}${s('cozum hatasi', 14)}${s('kacan anlamsal', 16)}`);
for (const x of satir) {
  console.log(`${(x.saat + ' sa').padEnd(9)}${s(x.n, 10)}${s(x.birlestirme, 12)}${s(x.catisan, 9)}`
    + `${f(x.catisan / x.birlestirme, 12)}${f(x.tur / x.birlestirme, 9)}${s(x.hata, 14)}${s(x.anlamsal, 16)}`);
}
console.log(`\n${'aralik'.padEnd(9)}${s('kosum', 7)}${s('calistirici dk', 15)}${s('cozum dk', 10)}${s('gec bulma dk', 14)}${s('toplam dk', 11)}${s('yanlis tutma', 14)}`);
for (const x of satir) {
  console.log(`${(x.saat + ' sa').padEnd(9)}${s(x.kosum, 7)}${s(x.kosumDk, 15)}${s(x.cozumDk, 10)}${s(x.sonrakiBedel, 14)}`
    + `${s(x.kosumDk + x.cozumDk + x.sonrakiBedel, 11)}${s(x.yanlis, 14)}`);
}
const enIyi = satir.reduce((a, x) =>
  (x.kosumDk + x.cozumDk + x.sonrakiBedel < a.kosumDk + a.cozumDk + a.sonrakiBedel ? x : a));
console.log(`\ndokunulan dosya her satirda ${satir.map((x) => x.dokunus).join('/')} (is sabit); `
  + `tohum 7391 icin en dusuk toplam ${enIyi.saat} saatlik aralikta`);
const en = (tohum) => [2, 4, 8, 16, 32, 80].map((h) => kos(h, tohum))
  .reduce((a, x) => (x.kosumDk + x.cozumDk + x.sonrakiBedel < a.kosumDk + a.cozumDk + a.sonrakiBedel ? x : a));
console.log('baska tohumlar: ' + [20260801, 4517, 66049, 131].map((s) => `${s} -> ${en(s).saat} sa`).join(', '));
```

```
aralik    dal dosya birlestirme  catisan catisan/blt  tur/blt  cozum hatasi  kacan anlamsal
2 sa              1         640       74        0.12     0.12            12               3
4 sa              2         320       90        0.28     0.27             4              10
8 sa              4         160      205        1.28     0.81            22              17
16 sa             8          80      320        4.00     0.97            31              26
32 sa            16          40      484       12.10     1.52            41              32
80 sa            40          16      654       40.88     1.63            55              43

aralik     kosum calistirici dk  cozum dk  gec bulma dk  toplam dk  yanlis tutma
2 sa         731          10234      1480          2700      14414            17
4 sa         407           5698      1800          2520      10018             2
8 sa         297           4158      4100          7020      15278             8
16 sa        163           2282      6400         10260      18942             5
32 sa        103           1442      9680         13140      24262             2
80 sa         43            602     13080         17640      31322             1

dokunulan dosya her satirda 640/640/640/640/640/640 (is sabit); tohum 7391 icin en dusuk toplam 4 saatlik aralikta
baska tohumlar: 20260801 -> 4 sa, 4517 -> 8 sa, 66049 -> 4 sa, 131 -> 4 sa
```

#### Çatışma Doğrusal Büyümüyor

İlk tablonun asıl satırı `catisan/blt` sütunudur. Dal ömrü iki saatten kırk kat büyüdüğünde
birleştirme başına çatışan dosya 0,12'den 40,88'e çıkar — kat sayısı 354'tür. Büyüme dal
başına dosya sayısıyla orantılı değildir, çünkü çatışma iki kümenin **kesişimi**dir: hem
kendi dalı, hem de o sırada ana dala giren toplam iş büyür. İki uzun dal aynı sıcak dosyalara
dokunma olasılığını ikisi birden artırır.

`tur/blt` sütunu ilk bakışta bu büyümeye eşlik etmez: 0,12'den 1,63'e çıkar ve orada
doyar. Sebebi ölçümün içindedir — bir çözüm turu, çözüm sürerken ana dalın kımıldamasıyla
açılır. Dallar uzun olduğunda birleşmeler seyrektir, yani çözüm sürerken ana dal genellikle
kımıldamaz ve tek tur yeter. Uzun dalın bedeli tur sayısında değil, **turun uzunluğunda**
görünür: birleştirme başına çözüm süresi iki saatlik dalda 2,3 model dakikası, on iş günlük
dalda 818 model dakikasıdır.

Çözümde doğan hata da aralıkla büyür: 12'den 55'e. Bu hatalar birleştirmenin **kendi
ürettiği** kusurlardır; kimse yanlış kod yazmamıştır, iki doğru değişikliğin elle
uzlaştırılması sırasında doğmuşlardır. Dört saatlik satırdaki 4 değeri komşularının altında
kalır; bu sayılar küçük olduğu için tohumun gürültüsüdür, eğilim değildir.

#### Sıklığın Kendi Bedeli

İkinci tablo ters yönü verir. İki saatlik aralıkta hat 731 kez koşar ve 10 234 model dakikası
çalıştırıcı zamanı harcar; on iş günlük aralıkta 43 koşum ve 602 dakika. Sık birleştirme
makineye on yedi kat daha pahalıdır. Aynı sütunla birlikte okunması gereken sayı `yanlis
tutma`dır: kararsız kırmızı koşum başına sabit olasılıktadır, dolayısıyla koşum arttıkça
yanlış durdurma da artar — 17'ye karşı 1. Sık birleştiren bir ekipte kapının haksız
yere durdurduğu değişiklik sayısı, seyrek birleştiren bir ekiptekinden on yedi kat fazladır
ve kapıyı atlama isteği tam buradan doğar.

Toplam sütunu üç kalemi birleştirir: çalıştırıcı zamanı, çözüm süresi ve geç bulunan
hatanın bedeli. Eğri iki uçtan da yükselir ve en düşük değer dört saatte, 10 018 dakikadadır.
Beş tohumun dördü aynı aralığı, biri sekiz saati seçer. **Sonuç "ne kadar sık, o kadar iyi"
değildir**: iki saatlik aralıkta çalıştırıcı zamanı ve yanlış tutma toplamı 4 000 dakikadan
fazla şişirir, on iş günlük aralıkta çözüm ve geç bulma 30 000 dakikaya çıkar. Aralığın
optimumu, koşumun bedeli ile çatışmanın bedelinin kesiştiği yerdedir; koşum ucuzladıkça
optimum sola kayar, bu da sonraki derslerin hat süresini kısaltma çabasının asıl gerekçesidir.

#### Birleştirme Kapısının Üç Sayısı

Bu kapı ana dala giriş kapısıdır ve üç sayıyla yazılır. **Tuttuğu:** dört saatlik aralıkta
90 çatışan dosya birleştirme anında görünür hâle gelir. Bunun **yalnız bu kapıya ait**
olan kısmı tamamıdır — metinsel çatışmayı hattaki hiçbir sonraki kapı göremez. Derleme kapısı
kötü çözülmüş bir birleşmeyi ancak "bozuk" diye bildirir, hangi iki değişikliğin çakıştığını
söyleyemez; çatışma bilgisi birleştirme anında vardır, sonra kaybolur.

**Kaçırdığı:** arayüz uyuşmazlıkları. Bunlar iki geliştiricinin **farklı** dosyalara
dokunduğu, yani metinsel çatışması olmayan, ama birinin beklentisiyle ötekinin verdiği
biçimin ayrıştığı durumlardır. Dört saatlik aralıkta 10, on iş günlük aralıkta 43 tanesi
kapıdan geçer. Çözümde doğan hatalar da kapıdan geçer, çünkü kapı çözümün kendisini denetlemez.
**Yanlış tuttuğu:** dört saatlik aralıkta 2, iki saatlik aralıkta 17 temiz değişiklik.
**Eklediği süre:** koşum başına 14 dakika çalıştırıcı zamanı ve çatışan dosya başına 20 dakika
insan zamanı; dört saatlik aralıkta değişiklik başına 23 dakika.

Bu dört sayı birlikte okunmadığında kapı savunulamaz. Yalnız çalıştırıcı zamanına bakan bir
değerlendirme sık birleştirmeyi pahalı bulur ve aralığı büyütür; yalnız çatışma sayısına
bakan bir değerlendirme aralığı sıfıra indirmek ister. İkisi de yanlıştır, çünkü kapının
değeri tuttuğu ile kaçırdığının farkındadır ve bu fark aralığa bağlıdır. Aynı kapı iki
saatlik aralıkta 74 çatışma tutup 3 uyuşmazlık kaçırır, on iş günlük aralıkta 654 tutup 43
kaçırır: daha çok tutmak, daha iyi tuttuğu anlamına gelmez — tuttuğu şeyin çoğunu aralığın
kendisi üretmiştir.

Kaçırdığı iki kalem, bu kapının tek başına yetmediğini söyler ve hattın geri kalanının
gerekçesidir: arayüz uyuşmazlığını yakalayacak olan sözleşme sınaması, çözümde doğan hatayı
yakalayacak olan test kapısıdır. Kapıların sırası ve neyi yakaladığı, kursun ilerleyen
derslerinin konusudur.

#### Özet

- Birleştirme aralığı iki saatten on iş gününe çıktığında birleştirme başına çatışan dosya
  0,12'den 40,88'e, çözümde doğan hata 12'den 55'e, kapının kaçırdığı arayüz uyuşmazlığı
  3'ten 43'e çıktı.
- Çözüm turu sayısı 1,63'te doydu; uzun dalın bedeli tur sayısında değil tur uzunluğundadır
  — birleştirme başına çözüm süresi 2,3 dakikadan 818 model dakikasına çıktı.
- Sıklığın kendi bedeli sayıldı: iki saatlik aralık 731 koşum ve 10 234 çalıştırıcı dakikası,
  on iş günlük aralık 43 koşum ve 602 dakika ister.
- Yanlış tutma koşum sayısıyla orantılıdır: 17'ye karşı 1; kapıyı atlama isteği bu sayıdan
  doğar.
- Üç kalem toplandığında en düşük değer dört saatlik aralıktadır (10 018 dakika) ve beş
  tohumun dördü aynı yeri gösterir; koşum ucuzladıkça optimum daha sık birleştirmeye kayar.

#### Sonraki Adım

Bu dersin kapısı ana dala giriştedir ve kararı tek bir sorunun cevabıdır: bu değişiklik
girsin mi? Ana dal yeşile döndükten sonra ne olacağı sorusuna hiç dokunulmadı. Yeşil bir ana
dal, üretime konabilecek bir şey midir, yoksa yalnızca üretime konmaya **aday** bir şey mi?
İki cevap iki ayrı düzen kurar ve aralarındaki fark tek bir kapıda toplanır: yeşil koşumla
üretim arasında duran insan onayı. Sonraki ders aynı hattı iki kipte koşturur ve o onayın
yakaladığı gerçek kusuru, beklettiği süreyle birlikte sayar.

### Sürekli Teslimat ve Sürekli Dağıtım

Source: https://academia.sh/tr/kurslar/ci-cd/surekli-teslimat-ve-dagitim

Aynı hattın iki kipte koşturulması: her yeşil koşum yayına hazır (teslimat) ile her yeşil koşum yayında (dağıtım) arasındaki fark üretime giden değişiklik, karar veren insan sayısı, bekleme süresi ve elle onayın yakaladığı gerçek kusurla ölçülür; onayın marjinal değeri yığın büyüklüğüne bağlanır.

Önceki ders ana dala giriş kapısını ölçtü ve tek soruya cevap verdi: bu değişiklik girsin mi?
Yeşil bir ana dalın ardından ne olacağı sorusuna dokunmadı. İki cevap vardır ve aradaki fark
tek bir kapıdadır.

**Sürekli teslimat** (continuous delivery), her yeşil koşumun **yayına hazır** bir çıktı
üretmesidir: çıktı paketlenmiş, sınanmış ve üretime konabilir durumdadır, ama konması bir
insan kararına bağlıdır. **Sürekli dağıtım** (continuous deployment), her yeşil koşumun
**yayında** olmasıdır: kapıların hepsi otomatiktir, aralarında insan yoktur. İkisi arasındaki
tek yapısal fark, hattın sonundaki **elle onay kapısı**dır. Bu ders o kapıyı ölçer: kaç
gerçek kusur tutuyor, tuttuklarının kaçını otomatik kapılar zaten görüyordu, kaç temiz
değişikliği boşuna durduruyor ve ne kadar bekletiyor.

#### Akış ve Kusur Sınıfları

Ölçüm bölgesel ölçüm ağının hattı üzerinde yapılır: sayaç okuma toplayıcı, doğrulayıcı,
faturalama ve iş emri servisleri. **Ağ da hat da kurgudur; aşağıdaki akış bir modeldir.**
Süreler model dakikasıdır.

**HT7 — yirmi iş gününde 320 değişiklik ana dala girer, otuz dakikada bir.** Bu sıklık önceki
dersin en düşük toplamı verdiği dört saatlik aralıktan gelir: sekiz geliştirici, dörder
saatte bir. Yedi kusur sınıfının yüz değişiklikteki sayıları ve dört olasılığı aşağıdadır —
otomatik kapının durdurma olasılığı, gecelik takımın bir koşumda yakalama olasılığı,
onaylayanın küçük yığında görme olasılığı ve otomatik kapının **durdurmadan uyarı üretme**
olasılığı.

**HT8 — hattın kendi süresi değişiklik başına 26 model dakikasıdır.**

**HT9 — onaylayan altı değişikliği tam görür; yığın bundan büyükse dikkat 6/n oranında
seyrelir.** Onay bir yığını inceler, tek tek değişiklikleri değil. İncelemenin süresi
15 + 4n model dakikasıdır ve **kararı iki kişi verir**: onaylayan ile yayın sahibi.

**HT10 — temiz bir yığının boşuna geri çevrilme olasılığı 0,08'dir.** Uyarıyı sert kapıya
çeviren kipte temiz bir değişikliğin boş uyarı üretme olasılığı 0,06'dır.

**HT11 — üretime kaçan bir kusurun teşhisi 30 + 12×(yığın−1) model dakikasıdır.** Yığın
büyüdükçe şüpheli değişiklik sayısı artar ve kusuru hangisinin getirdiği aranır.

**HT12 — gecelik takım her gün sonunda bir kez koşar.** Bekleme süresine sığan her koşum,
sınıfın gecelik olasılığıyla bağımsız bir çekiliş yapar.

```js
// akis.mjs — degisiklik akisi ve kusur siniflari; ayni cekilisler her kipte kullanilir
const uretec = (tohum) => () => {
  tohum = (tohum * 1664525 + 1013904223) >>> 0;
  return tohum / 4294967296;
};

// HT7: yuz degisiklikte ortaya cikma sayisi. kapi = her kosumda calisan otomatik kapilarin
// kirmiziya dondurme olasiligi, gecelik = gecelik takimin bir kosumda yakalama olasiligi,
// elle = onaylayanin kucuk yiginda gorme olasiligi, uyari = otomatik kapilarin durdurmadan
// uyari uretme olasiligi.
export const siniflar = [
  { ad: 'birim kapsami disi kosul', sayi: 9, kapi: 0.90, gecelik: 0.50, elle: 0.05, uyari: 0.30 },
  { ad: 'sema uyusmazligi', sayi: 6, kapi: 0.75, gecelik: 0.60, elle: 0.10, uyari: 0.40 },
  { ad: 'yapilandirma kaymasi', sayi: 5, kapi: 0.15, gecelik: 0.35, elle: 0.60, uyari: 0.55 },
  { ad: 'goc ile yayin sirasi', sayi: 4, kapi: 0.10, gecelik: 0.20, elle: 0.75, uyari: 0.45 },
  { ad: 'is kurali yanlisligi', sayi: 4, kapi: 0.20, gecelik: 0.15, elle: 0.30, uyari: 0.10 },
  { ad: 'esik degeri', sayi: 3, kapi: 0.10, gecelik: 0.30, elle: 0.12, uyari: 0.35 },
  { ad: 'es zamanlilik yarisi', sayi: 3, kapi: 0.05, gecelik: 0.10, elle: 0.00, uyari: 0.05 },
];

export const DEGISIKLIK = 320, ARA = 30;    // HT7: yirmi is gunu, 30 dakikada bir birlestirme
export const GUN_DK = 480, HAFTA_DK = 2400;

// Her degisikligin butun cekilisleri onceden uretilir: kipler ayni sayilarla karsilastirilir.
export const akisUret = (tohum) => {
  const r = uretec(tohum);
  return Array.from({ length: DEGISIKLIK }, (_, i) => {
    let x = r() * 100, sinif = null;
    for (const s of siniflar) { x -= s.sayi; if (x <= 0) { sinif = s; break; } }
    return { t: i * ARA, sinif, kapiCekilis: r(), elleCekilis: r(), uyariCekilis: r(),
      yanlisCekilis: r(), gecelikCekilis: Array.from({ length: 8 }, () => r()) };
  });
};
```

#### Altı Kip

Karşılaştırılan altı kip iki ekseni ayırır: yayın penceresi (bekleme) ve onay (insan). Üçüncü
satır bu ayrımın anahtarıdır — günlük pencere vardır ama onay yoktur, yani beklemenin kendi
kazancı insanınkinden ayrı ölçülür.

```js
// kip.mjs — ayni hat alti kipte: her yesil kosum yayinda (dagitim) ya da yayina hazir (teslimat)
import { akisUret, DEGISIKLIK, GUN_DK, HAFTA_DK } from './akis.mjs';

const HAT_DK = 26;                                       // HT8: hattin kendi suresi
const DIKKAT = 6;                                        // HT9: tam gorulen yigin buyuklugu
const ONAY_TABAN = 15, ONAY_BIRIM = 4, ONAY_KISI = 2;    // HT9: onay suresi ve karar veren kisi
const YANLIS_RET = 0.08, BOS_UYARI = 0.06;               // HT10: bosuna durdurma olasiliklari
const TESHIS_TABAN = 30, TESHIS_BIRIM = 12;              // HT11: uretimdeki kusurun teshis suresi

const kipler = [
  { ad: 'dagitim', pencere: 0, onay: false },
  { ad: 'teslimat, her kosum', pencere: 0, onay: true },
  { ad: 'gunluk pencere, onaysiz', pencere: GUN_DK, onay: false },
  { ad: 'teslimat, gunluk onay', pencere: GUN_DK, onay: true },
  { ad: 'teslimat, haftalik onay', pencere: HAFTA_DK, onay: true },
  { ad: 'dagitim, uyari sert kapi', pencere: 0, onay: false, sert: true },
];

const olc = (kip, akis) => {
  const yigin = new Map();
  let sertTuttu = 0, yanlis = 0;
  for (const d of akis) {
    if (d.sinif && d.kapiCekilis < d.sinif.kapi) continue;      // otomatik kapi kirmiziya dondurdu
    let ek = 0;
    if (kip.sert) {
      if (d.sinif && d.uyariCekilis < d.sinif.uyari) { sertTuttu++; continue; }
      if (!d.sinif && d.yanlisCekilis < BOS_UYARI) { yanlis++; ek = HAT_DK; }
    }
    const hazir = d.t + HAT_DK + ek;
    const yayin = kip.pencere === 0 ? hazir : Math.ceil(hazir / kip.pencere) * kip.pencere;
    (yigin.get(yayin) ?? yigin.set(yayin, []).get(yayin)).push({ d, hazir });
  }
  let giden = 0, karar = 0, bekleme = 0, enKotu = 0, tuttu = 0, uyarili = 0;
  let onayDk = 0, kacan = 0, teshisDk = 0, yiginToplam = 0, yayinSayisi = 0;
  for (const [yayin, kayitlar] of [...yigin.entries()].sort((a, b) => a[0] - b[0])) {
    const n = kayitlar.length;
    yayinSayisi++; yiginToplam += n;
    const dikkat = Math.min(1, DIKKAT / n);                     // HT9: yigin buyudukce seyrelir
    let ret = false;
    if (kip.onay) { karar += ONAY_KISI; onayDk += (ONAY_TABAN + ONAY_BIRIM * n) * ONAY_KISI; }
    for (const { d, hazir } of kayitlar) {
      // HT12: bekleme sirasinda kac gecelik kosum sigiyor
      const kosum = Math.floor(yayin / GUN_DK) - Math.floor(hazir / GUN_DK);
      let durdu = false;
      if (d.sinif) {
        for (let k = 0; k < Math.min(kosum, 8); k++) {
          if (d.gecelikCekilis[k] < d.sinif.gecelik) { durdu = true; break; }
        }
        if (!durdu && kip.onay && d.elleCekilis < d.sinif.elle * dikkat) {
          durdu = true; ret = true; tuttu++;
          if (d.uyariCekilis < d.sinif.uyari) uyarili++;         // otomatik kapi zaten uyarmisti
        }
      }
      if (durdu) continue;
      if (d.sinif) { kacan++; teshisDk += TESHIS_TABAN + TESHIS_BIRIM * (n - 1); }
      giden++; bekleme += yayin - hazir; enKotu = Math.max(enKotu, yayin - hazir);
    }
    if (kip.onay && !ret && kayitlar[0].d.yanlisCekilis < YANLIS_RET) yanlis += n;
  }
  return { yayinSayisi, yigin: yiginToplam / yayinSayisi, giden, karar, bekleme: bekleme / giden,
    enKotu, tuttu, uyarili, yanlis, onayDk, kacan, teshisDk, sertTuttu };
};

const akis = akisUret(51301);
const sonuc = kipler.map((k) => ({ ad: k.ad, r: olc(k, akis) }));
const bul = (ad) => sonuc.find((x) => x.ad === ad).r;
const s = (x, w) => String(x).padStart(w);
const f = (x, w) => String(x.toFixed(1)).padStart(w);

console.log(`degisiklik ${DEGISIKLIK}, kusur tasiyan ${akis.filter((d) => d.sinif).length}\n`);
console.log(`${'kip'.padEnd(25)}${s('yayin', 7)}${s('ort yigin', 11)}${s('uretime giden', 15)}${s('insan karari', 14)}${s('ort bekleme', 13)}${s('en kotu', 9)}`);
for (const { ad, r } of sonuc) {
  console.log(`${ad.padEnd(25)}${s(r.yayinSayisi, 7)}${f(r.yigin, 11)}${s(r.giden, 15)}`
    + `${s(r.karar, 14)}${f(r.bekleme, 13)}${s(r.enKotu, 9)}`);
}
console.log(`\n${'kip'.padEnd(25)}${s('onay tuttu', 12)}${s('zaten uyarili', 15)}${s('uretime kacan', 15)}${s('yanlis tuttu', 14)}${s('insan dk', 10)}${s('teshis dk', 11)}`);
for (const { ad, r } of sonuc) {
  console.log(`${ad.padEnd(25)}${s(r.tuttu, 12)}${s(r.uyarili, 15)}${s(r.kacan, 15)}`
    + `${s(r.yanlis, 14)}${s(r.onayDk, 10)}${s(r.teshisDk, 11)}`);
}
const sert = bul('dagitim, uyari sert kapi');
const ozet = (tohum) => {
  const a = akisUret(tohum), h = (ad) => olc(kipler.find((k) => k.ad === ad), a);
  return { tek: h('dagitim').kacan - h('teslimat, her kosum').kacan,
    gun: h('gunluk pencere, onaysiz').kacan - h('teslimat, gunluk onay').kacan,
    sert: h('dagitim, uyari sert kapi').kacan, cip: h('dagitim').kacan };
};
console.log(`\nonayin marjinali: pencere yokken ${bul('dagitim').kacan - bul('teslimat, her kosum').kacan} kusur, `
  + `gunluk pencerede ${bul('gunluk pencere, onaysiz').kacan - bul('teslimat, gunluk onay').kacan} kusur`);
console.log(`gunluk pencerenin onaysiz kazanci ${bul('dagitim').kacan - bul('gunluk pencere, onaysiz').kacan} kusur, insan dakikasi 0`);
console.log(`sert kapi ${sert.sertTuttu} degisikligi durdurdu, ${sert.yanlis} tanesi bosunaydi; uretime kacan ${sert.kacan}`);
console.log('tohum taramasi (onay marjinali tek/gunluk, sert kapi kacan, onaysiz dagitim kacan):');
for (const tohum of [51301, 8802, 33107, 6449]) {
  const o = ozet(tohum);
  console.log(`  ${tohum}: ${o.tek}/${o.gun}, ${o.sert}, ${o.cip}`);
}
```

```
degisiklik 320, kusur tasiyan 116

kip                        yayin  ort yigin  uretime giden  insan karari  ort bekleme  en kotu
dagitim                      264        1.0            264             0          0.0        0
teslimat, her kosum          264        1.0            242           528          0.0        0
gunluk pencere, onaysiz       20       13.2            247             0        224.8      454
teslimat, gunluk onay         20       13.2            242            40        226.5      454
teslimat, haftalik onay        4       66.0            239             8       1152.0     2374
dagitim, uyari sert kapi     236        1.0            236             0          0.0        0

kip                        onay tuttu  zaten uyarili  uretime kacan  yanlis tuttu  insan dk  teshis dk
dagitim                             0              0             60             0         0       1800
teslimat, her kosum                22             14             38            16     10032       1140
gunluk pencere, onaysiz             0              0             43             0         0       7890
teslimat, gunluk onay               5              3             38            26      2712       6972
teslimat, haftalik onay             2              2             35             0      2232      28662
dagitim, uyari sert kapi            0              0             32             3         0        960

onayin marjinali: pencere yokken 22 kusur, gunluk pencerede 5 kusur
gunluk pencerenin onaysiz kazanci 17 kusur, insan dakikasi 0
sert kapi 28 degisikligi durdurdu, 3 tanesi bosunaydi; uretime kacan 32
tohum taramasi (onay marjinali tek/gunluk, sert kapi kacan, onaysiz dagitim kacan):
  51301: 22/5, 32, 60
  8802: 20/6, 45, 60
  33107: 14/6, 50, 64
  6449: 21/6, 48, 69
```

#### Beklemenin Kazancı Kimin

Birinci tablo iki kipin akış farkını verir. Dağıtım kipinde yirmi iş gününde 264 yayın olur,
üretime giden değişiklik 264'tür ve hiç insan kararı yoktur. Haftalık onaylı teslimatta yayın
sayısı dörttür, yığın ortalama 66 değişikliktir, insan kararı sekizdir ve hazır olup
yayınlanmayan bir değişiklik ortalama 1152, en kötü durumda 2374 model dakikası bekler —
yaklaşık beş iş günü. Aynı hat, aynı çıktı, aynı sınamalar; fark yalnız sondaki kapıdadır.

İkinci tablonun `uretime kacan` sütunu ilk bakışta beklemeyi haklı çıkarır: dağıtımda 60,
haftalıkta 35. Kritik satır üçüncüdür. **Günlük pencere, onaysız** kipi kaçan kusuru 60'tan
43'e indirir ve bunu sıfır insan dakikasıyla yapar. Bu 17 kusuru insan bulmamıştır; bekleme
süresine sığan gecelik koşumlar bulmuştur. Elle onayın hanesine yazılan iyileşmenin büyük
kısmı, aslında **makineye tanınan ek zamanın** kazancıdır — ve o zaman insansız da tanınabilir.

Beklemenin faturası ise `teshis dk` sütunundadır. Dağıtımda 60 kusurun teşhisi 1800 dakika,
kusur başına 30 dakikadır: yayında tek değişiklik vardır, şüpheli bellidir. Haftalık kipte 35
kusurun teşhisi 28 662 dakika, kusur başına 819 dakikadır — yirmi yedi kat. Yığın büyüdükçe
üretimdeki her arıza için 66 aday arasından seçim yapmak gerekir. Bekleme kaçan kusuru
azaltır, kaçanın bedelini büyütür.

#### Elle Onayın Marjinal Değeri

Onay kapısının marjinali, aynı çekilişlerle onaylı ve onaysız koşumun kaçan kusur farkıdır.
Yığın bir değişiklikken bu fark 22 kusurdur — onay ciddi bir kapıdır. Günlük pencerede aynı
fark 5 kusura düşer. Kapının kendisi değişmemiştir; değişen, incelediği yığındır. Onay
kapısı **kendi beklemesini üretir**, bekleme yığını büyütür, yığın dikkati seyreltir: 13,2
değişikliklik bir yığında dikkat 6/13,2, yani 0,45'tir. Kapı kendi varlığıyla kendi
etkisini yüzde 77 azaltır. Haftalık pencerede 66'lık yığında marjinal 2 kusurdur.

İkinci sayı daha da keskindir. `zaten uyarili` sütunu, onayın tuttuğu kusurlardan kaçı için
otomatik kapıların hâlihazırda bir uyarı ürettiğini sayar: yığın bir iken 22'nin 14'ü, günlük
pencerede 5'in 3'ü. Bu kusurlar için insan, makinenin zaten yazdığı satırı okumuştur. Uyarı
sert kapıya çevrilseydi onay olmadan da durdurulacaklardı. Onayın **yalnız kendisine ait**
katkısı iki durumda 8 ve 2 kusurdur.

Son satır bu düşünceyi sonuna götürür. Uyarıların sert kapıya çevrildiği dağıtım kipinde 28
değişiklik durdurulur, üçü boşuna; kaçan kusur 32'ye iner. Bu, bekleme ve onay içeren her
kipten daha iyidir: sıfır insan dakikası, sıfır bekleme, kusur başına 30 dakikalık teşhis.
Tohum taraması sayının oynadığını gösterir — sert kapıda kaçan 32 ile 50 arasında, onaysız
dağıtımda 60 ile 69 arasında değişir — ama sıralama dört tohumda da aynıdır.

Elle onay kapısının üç sayısı böylece yazılabilir. **Tuttuğu:** günlük pencerede 5 kusur,
yalnız kendisinin tuttuğu 2. **Kaçırdığı ve yanlış tuttuğu:** 38 kusur üretime gider, 26
temiz değişiklik boşuna durdurulur. **Eklediği süre:** 2712 insan dakikası ve değişiklik
başına 226,5 dakika bekleme. Bu üç satır yan yana yazıldığında kapı savunulamaz hâle gelir;
tek başına "onaylayan biri olsun" cümlesi ise hiçbir sayı taşımaz.

#### Özet

- Sürekli teslimat ile sürekli dağıtım arasındaki tek yapısal fark, yeşil koşum ile üretim
  arasında duran elle onay kapısıdır; ölçüm bu kapıya yapıldı.
- Elle onayın marjinali yığına bağlıdır: yığın bir değişiklikken 22 kusur, günlük pencerede
  5, haftalık pencerede 2. Kapı kendi beklemesini üretip kendi etkisini yüzde 77 azaltır.
- Onayın tuttuğu kusurların çoğu için otomatik kapılar zaten uyarı üretmişti (22'nin 14'ü,
  5'in 3'ü); onaya ait eşsiz katkı 8 ve 2 kusurdur.
- Beklemenin kazancı insanın değil makinenindir: günlük pencere onaysız hâlde kaçan kusuru
  60'tan 43'e indirdi, insan dakikası sıfırdı.
- Bekleme kaçanın bedelini büyütür: teşhis kusur başına 30 dakikadan 819 dakikaya çıktı,
  çünkü 66'lık yığında şüpheli sayısı 66'dır.
- Uyarıyı sert kapıya çeviren onaysız dağıtım kaçanı 32'ye indirdi; sıfır insan dakikası ve
  sıfır beklemeyle her onaylı kipten iyi sonuç verdi.

#### Sonraki Adım

Bu dersin ölçtüğü kapı hattın sonundaydı ve ondan önceki her şey tek bir kutu olarak, "otomatik
kapılar" adıyla anıldı. O kutunun içinde birden çok kapı vardır: derleme, test, paketleme ve
yayın. Sondaki onayın marjinali hesaplanabildiğine göre içerideki her kapının marjinali de
hesaplanabilir — hangi kusuru **yalnız** o kapı tutuyor, hangisini kaçırıyor, kaç temiz
değişikliği boşuna durduruyor ve hatta kaç dakika ekliyor. Sonraki ders bilinen bir kusur
kümesini bir hat modelinden geçirir, her kusurun hangi kapıda yakalandığını izler ve
kapılardan birinin kaldırılmasını dener.

### Hat Aşamaları

Source: https://academia.sh/tr/kurslar/ci-cd/hat-asamalari

Derleme, test, paketleme ve yayın kapılarının bilinen bir kusur kümesiyle ölçülmesi: her kapının tuttuğu, yalnız kendisinin tuttuğu, geçirdiği ve boşuna durdurduğu sayılır, eklediği süre çalıştırıcı dakikasıyla yazılır ve sekiz kapının her biri tek tek kaldırılarak kaldırılması daha iyi olan kapı bulunur.

Önceki ders hattın sonundaki elle onay kapısını ölçtü ve ondan önceki her şeyi tek bir kutu
olarak, "otomatik kapılar" diye andı. O kutunun içinde birden çok kapı vardır ve her birinin
kendi hesabı vardır. Bu ders kutuyu açar: sekiz kapılı bir hat kurulur, bilinen bir kusur
kümesi hattan geçirilir ve her kusurun hangi kapıda yakalandığı izlenir.

Sorulan şey her kapı için aynıdır. Kaç kusur tutuyor? Bunların kaçını **yalnız kendisi**
tutuyor, yani kapı kaldırılsa kaçı üretime kaçardı? Kaç kusuru geçiriyor, kaç temiz
değişikliği boşuna durduruyor? Ve kaç dakika ekliyor? Dördüncü soru üçüncüsünü değersiz
kılabilir: çok tutan bir kapı, tuttuklarının hepsini sonraki kapı da tutuyorsa hiçbir şey
katmıyordur. Dersin sonunda sekiz kapının her biri tek tek kaldırılır ve **kaldırılması daha
iyi olan kapı** sayıyla gösterilir.

#### Hat ve Kusur Kümesi

Model, bölgesel ölçüm ağının hattıdır: sayaç okuma toplayıcı, doğrulayıcı, faturalama ve iş
emri servisleri; geliştirme, hazırlık ve üretim ortamları. **Ağ da hat da kurgudur; aşağıdaki
kusur kümesi bir modeldir.** Süreler model dakikasıdır, ölçümün koştuğu makineden bağımsızdır.

**HT13 — hat sekiz kapıdır ve sırası sabittir:** biçim çözümlemesi, derleme, birim sınamaları,
sözleşme sınamaları, tümleşim sınamaları, paketleme, hazırlık ortamında duman testi ve
üretimde yayın sağlığı penceresi. Erken kesme açıktır: kırmızı kapıdan sonraki kapılar
koşmaz.

**HT14 — bilinen kusur kümesi on dört sınıftır.** Her sınıfın yüz değişiklikte ortaya çıkma
sayısı, üretime kaçtığında ödettiği model dakikası ve **kapı başına yakalanma olasılığı**
yazılıdır. Tabloda yazılmayan kapı o sınıfı hiç görmez. Bir değişiklik birden çok kusur
taşıyabilir.

**HT15 — kırmızı bir kapıdan sonra yakalanan kusur düzeltilir ve değişiklik baştan koşar**,
en çok altı deneme. Düzeltmenin kendisi yeni kusur doğurmaz.

**HT16 — her kapının bir yanlış tutma olasılığı vardır**; yanlış tutulan değişiklik yeniden
koşar ve 35 model dakikası insan zamanı harcar.

**HT17 — toplam bedel üç kalemin toplamıdır:** çalıştırıcı dakikası, yanlış tutma sayısı × 35
ve üretime kaçan kusurların sınıf bedeli. Kapı kararı bu toplamla verilir.

**HT18 — 400 değişiklik koşturulur; bütün çekilişler önceden üretilir**, böylece bir kapı
kaldırıldığında kalan kapılar aynı sayıları görür ve karşılaştırma kapıyı yalıtır.

```js
// hat.mjs — sekiz kapili hat modeli ve bilinen kusur kumesi
const uretec = (tohum) => () => {
  tohum = (tohum * 1664525 + 1013904223) >>> 0;
  return tohum / 4294967296;
};

// HT13: kapi sirasi, kapi basina model dakikasi, kapi basina yanlis tutma olasiligi.
export const kapilar = [
  { ad: 'bicim', dk: 1, yanlis: 0.005 },
  { ad: 'derleme', dk: 4, yanlis: 0.004 },
  { ad: 'birim', dk: 3, yanlis: 0.012 },
  { ad: 'sozlesme', dk: 11, yanlis: 0.040 },
  { ad: 'tumlesim', dk: 14, yanlis: 0.030 },
  { ad: 'paketleme', dk: 6, yanlis: 0.008 },
  { ad: 'duman', dk: 9, yanlis: 0.045 },
  { ad: 'saglik', dk: 20, yanlis: 0.035 },
].map((k, gi) => ({ ...k, gi }));   // gi: kapinin tam hattaki sabit sirasi

// HT14: bilinen kusur kumesi. sayi = yuz degisiklikte ortaya cikma, bedel = uretime kacarsa
// model dakikasi, gor = kapi basina yakalama olasiligi (yazilmayan kapi gormez).
export const siniflar = [
  { ad: 'bicim ihlali', sayi: 14, bedel: 40, gor: { bicim: 1.0 } },
  { ad: 'tur uyusmazligi', sayi: 8, bedel: 300, gor: { derleme: 0.95, birim: 0.40 } },
  { ad: 'eksik bagimlilik', sayi: 5, bedel: 500, gor: { derleme: 0.80, paketleme: 0.90 } },
  { ad: 'sinir kosulu', sayi: 7, bedel: 900, gor: { birim: 0.85, tumlesim: 0.25 } },
  { ad: 'bos deger cozumleme', sayi: 6, bedel: 800, gor: { birim: 0.60, tumlesim: 0.35, duman: 0.40 } },
  { ad: 'alan adi degisikligi', sayi: 4, bedel: 1600, gor: { sozlesme: 0.90, tumlesim: 0.85 } },
  { ad: 'istek bicimi kaymasi', sayi: 3, bedel: 1400, gor: { sozlesme: 0.85, tumlesim: 0.75, duman: 0.35 } },
  { ad: 'sema uyusmazligi', sayi: 4, bedel: 1800, gor: { tumlesim: 0.80, duman: 0.50 } },
  { ad: 'goc sirasi', sayi: 2, bedel: 2600, gor: { tumlesim: 0.35, duman: 0.55, saglik: 0.40 } },
  { ad: 'yapilandirma eksigi', sayi: 5, bedel: 1200, gor: { paketleme: 0.45, duman: 0.80 } },
  { ad: 'paket butunlugu', sayi: 2, bedel: 1000, gor: { paketleme: 0.90 } },
  { ad: 'gecikme gerilemesi', sayi: 3, bedel: 1500, gor: { duman: 0.15, saglik: 0.70 } },
  { ad: 'bellek sizintisi', sayi: 2, bedel: 2300, gor: { saglik: 0.55 } },
  { ad: 'is kurali yanlisligi', sayi: 3, bedel: 2000, gor: { birim: 0.20, tumlesim: 0.15, duman: 0.10 } },
];

export const DEGISIKLIK = 400, DENEME = 6;   // HT15: en cok alti deneme
export const YANLIS_DK = 35;                 // HT16: yanlis tutulan degisikligin insan bedeli

// HT18: butun cekilisler onceden uretilir; kapi kaldirilinca ayni sayilar kullanilir.
export const akisUret = (tohum) => {
  const r = uretec(tohum);
  return Array.from({ length: DEGISIKLIK }, () => ({
    kusur: siniflar.filter((s) => r() < s.sayi / 100)
      .map((s) => ({ s, cek: Array.from({ length: DENEME * kapilar.length }, () => r()) })),
    yanlisCek: Array.from({ length: DENEME * kapilar.length }, () => r()),
  }));
};
```

#### Kapı Başına Dört Sayı

Ölçüm iki kez koşar: önce tam hat, sonra sekiz kez birer kapı eksik. `marjinal` sütunu
doğrudan kaldırma denemesinden okunur — kapı çıkarıldığında üretime kaçan kusurun ne kadar
arttığı.

```js
// kapi.mjs — her kusurun hangi kapida yakalandigi, kapi basina uc sayi ve kaldirma denemesi
import { kapilar, siniflar, akisUret, DEGISIKLIK, DENEME, YANLIS_DK } from './hat.mjs';

const kosumBir = (dizilim, akis) => {
  const say = Object.fromEntries(dizilim.map((k) => [k.ad,
    { tuttu: 0, gecti: 0, yanlis: 0, dk: 0 }]));
  let kacan = 0, kacanBedel = 0, kosum = 0;
  for (const d of akis) {
    let kalan = d.kusur.slice();
    for (let deneme = 0; deneme < DENEME; deneme++) {
      kosum++;
      let durduran = null;
      for (const k of dizilim) {
        const i = deneme * kapilar.length + k.gi;   // cekilis kapinin sabit sirasina bagli
        say[k.ad].dk += k.dk;
        if (d.yanlisCek[i] < k.yanlis) {                       // HT16: yanlis tutma
          say[k.ad].yanlis++; durduran = { k, gercek: false }; break;
        }
        const goren = kalan.filter((x) => x.cek[i] < (x.s.gor[k.ad] ?? 0));
        say[k.ad].gecti += kalan.length - goren.length;
        if (goren.length > 0) {
          say[k.ad].tuttu += goren.length;
          kalan = kalan.filter((x) => !goren.includes(x));     // HT15: duzeltilip yeniden kosar
          durduran = { k, gercek: true }; break;
        }
      }
      if (durduran === null) break;                            // yesil: kalanlar uretime gider
    }
    kacan += kalan.length;
    kacanBedel += kalan.reduce((a, x) => a + x.s.bedel, 0);
  }
  const dk = dizilim.reduce((a, k) => a + say[k.ad].dk, 0);
  const yanlis = dizilim.reduce((a, k) => a + say[k.ad].yanlis, 0);
  return { say, kacan, kacanBedel, kosum, dk, yanlis,
    toplam: dk + yanlis * YANLIS_DK + kacanBedel };
};

const akis = akisUret(88123);
const tam = kosumBir(kapilar, akis);
const kusurToplam = akis.reduce((a, d) => a + d.kusur.length, 0);
const s = (x, w) => String(x).padStart(w);

console.log(`degisiklik ${DEGISIKLIK}, kusur ${kusurToplam}, kusur sinifi ${siniflar.length}, kapi ${kapilar.length}`);
console.log(`tam hat: kosum ${tam.kosum}, calistirici ${tam.dk} dk, yanlis tutma ${tam.yanlis}, `
  + `uretime kacan ${tam.kacan} kusur / ${tam.kacanBedel} dk, toplam ${tam.toplam} dk\n`);

console.log(`${'kapi'.padEnd(11)}${s('dk', 4)}${s('tuttu', 7)}${s('marjinal', 10)}${s('gecirdi', 9)}${s('yanlis', 8)}${s('ekledigi dk', 13)}`);
const kaldir = {};
for (const k of kapilar) {
  const y = kosumBir(kapilar.filter((x) => x.ad !== k.ad), akis);
  kaldir[k.ad] = y;
  console.log(`${k.ad.padEnd(11)}${s(k.dk, 4)}${s(tam.say[k.ad].tuttu, 7)}${s(y.kacan - tam.kacan, 10)}`
    + `${s(tam.say[k.ad].gecti, 9)}${s(tam.say[k.ad].yanlis, 8)}${s(tam.say[k.ad].dk, 13)}`);
}

console.log(`\n${'dizilim'.padEnd(18)}${s('calistirici dk', 15)}${s('yanlis', 8)}${s('kacan', 7)}${s('kacan dk', 10)}${s('toplam dk', 11)}${s('fark', 8)}`);
console.log(`${'tam hat'.padEnd(18)}${s(tam.dk, 15)}${s(tam.yanlis, 8)}${s(tam.kacan, 7)}${s(tam.kacanBedel, 10)}${s(tam.toplam, 11)}${s('-', 8)}`);
for (const k of kapilar) {
  const y = kaldir[k.ad];
  console.log(`${(k.ad + ' yok').padEnd(18)}${s(y.dk, 15)}${s(y.yanlis, 8)}${s(y.kacan, 7)}${s(y.kacanBedel, 10)}`
    + `${s(y.toplam, 11)}${s(y.toplam - tam.toplam, 8)}`);
}
const iyi = kapilar.filter((k) => kaldir[k.ad].toplam < tam.toplam);
console.log(`\nkaldirilmasi toplami dusuren kapi: ${iyi.length ? iyi.map((k) => k.ad).join(', ') : 'yok'}`);
console.log('tohum taramasi (kaldirilmasi toplami dusuren kapi):');
for (const tohum of [88123, 4402, 71905, 3311]) {
  const a = akisUret(tohum), t0 = kosumBir(kapilar, a);
  const d = kapilar.filter((k) => kosumBir(kapilar.filter((x) => x.ad !== k.ad), a).toplam < t0.toplam);
  console.log(`  ${tohum}: ${d.length ? d.map((k) => k.ad).join(', ') : 'yok'} (kacan ${t0.kacan})`);
}
```

```
degisiklik 400, kusur 256, kusur sinifi 14, kapi 8
tam hat: kosum 713, calistirici 34140 dk, yanlis tutma 89, uretime kacan 27 kusur / 39600 dk, toplam 76855 dk

kapi         dk  tuttu  marjinal  gecirdi  yanlis  ekledigi dk
bicim         1     51        51      284       7          713
derleme       4     46         8      213       3         2620
birim         3     42        27      150       7         1818
sozlesme     11     18         1      119      17         6138
tumlesim     14     25        14       85      15         7336
paketleme     6     19        13       63       1         2910
duman         9     17        15       40      28         4185
saglik       20     11        12       28      11         8420

dizilim            calistirici dk  yanlis  kacan  kacan dk  toplam dk    fark
tam hat                     34140      89     27     39600      76855       -
bicim yok                   33688      87     78     40440      77173     318
derleme yok                 32422      87     35     43700      79167    2312
birim yok                   32350      76     54     67500     102510   25655
sozlesme yok                28180      74     28     40800      71570   -5285
tumlesim yok                26571      75     41     62100      91296   14441
paketleme yok               30906      90     40     51100      85156    8301
duman yok                   28216      51     42     59800      89801   12946
saglik yok                  24520      76     39     60400      87580   10725

kaldirilmasi toplami dusuren kapi: sozlesme
tohum taramasi (kaldirilmasi toplami dusuren kapi):
  88123: sozlesme (kacan 27)
  4402: sozlesme (kacan 16)
  71905: bicim, sozlesme (kacan 27)
  3311: yok (kacan 25)
```

#### Tutmak ile Yalnız Tutmak

`tuttu` ve `marjinal` sütunları birbirini takip etmez. Derleme kapısı 46 kusur tutar, ama
kaldırıldığında üretime kaçan yalnız 8 artar: tuttuklarının çoğunu birim ve paketleme
kapıları da görüyordur. Sözleşme kapısı 18 tutar, marjinali 1'dir. Buna karşılık biçim
kapısı 51 tutar ve 51'i marjinaldir — biçim ihlallerini hattaki hiçbir başka kapı görmez.

Sıralama üçüncü bir sayıyla tersine döner: marjinal kusurun **bedeli**. Biçim ihlalinin
sınıf bedeli 40 dakikadır, bellek sızıntısınınki 2 300, göç sırasınınki 2 600. Biçim kapısı
kaldırıldığında kaçış bedeli 840 dakika artar; sağlık kapısı kaldırıldığında 20 800 dakika,
yani on iki kusur için kusur başına 1 733 dakika. Bu yüzden **en çok tutan kapı en değerli
kapı değildir**: biçim kapısının kaldırılması toplamı 318 dakika artırır, sağlık kapısınınki
10 725. Kapının değeri tuttuğu kusur sayısında değil, yalnız kendisinin tuttuğu kusurların
bedelindedir.

`gecirdi` sütunu sıranın doğal sonucudur ve tek başına bir suçlama değildir. Biçim kapısı
284 kusur geçirir, çünkü ilk sırada durur ve hattaki her kusuru görür. Sağlık kapısı 28
geçirir, çünkü ona ulaşan kusur zaten azdır. Bu sütun ancak kapının **görmesi beklenen**
sınıfla birlikte okunduğunda anlam taşır.

`ekledigi dk` sütunu kapı süresinin kaç koşumla çarpıldığına bağlıdır ve erken kesme bu
çarpanı aşağı çeker. Sütun kapı süresine bölündüğünde kapı başına koşum sayısı çıkar:
biçim 713, derleme 655, birim 606, sözleşme 558, tümleşim 524, paketleme 485, duman 465,
sağlık 421. Hat boyunca ilerledikçe koşum sayısı düşer, çünkü önceki kapılardan birinde
duran değişiklik sonrakilere hiç ulaşmaz. Sağlık kapısı koşum başına en pahalı kapıdır
(20 dakika) ama en az koşar; hattın toplam çalıştırıcı yükünün dörtte biri yine de ona aittir.

Yanlış tutma sütunu bağımsız bir eksendir. Duman testi kapısı 28 kez temiz bir değişikliği
boşuna durdurur, hattaki en yüksek sayıdır; buna rağmen marjinali 15 kusurdur ve kaldırılması
toplamı 12 946 dakika artırır. Kararsızlığı yüksek bir kapı, marjinali yeterse yerinde
kalır — çözüm onu kaldırmak değil, kararsızlığını düşürmektir.

#### Kaldırılması Daha İyi Olan Kapı

İkinci tablo sekiz kaldırma denemesini toplam bedelle sıralar. Yedi kapı için `fark` sütunu
artıdır: kaldırma pahalıya gelir. Birim kapısı en pahalı kayıptır, 25 655 dakika — çünkü
sınır koşulu ve boş değer çözümlemesi sınıflarını asıl gören kapıdır ve 3 dakikaya mal olur.
Kapı başına en iyi takas hattın başındadır.

Bir kapı ayrılır. **Sözleşme kapısı kaldırıldığında toplam 5 285 dakika düşer.** Kapının
kendi hesabı bunu açıklar: 18 kusur tutar, ama kaldırıldığında bunların 16'sını tümleşim kapısı
yakalar, ikisi üretime kaçar; kaldırma denemenin akışını da değiştirdiği için başka bir kusur
duman kapısında yakalanmaya başlar ve marjinal 1 kusurda kalır. Karşılığında 6 138 çalıştırıcı
dakikası ekler ve 17 temiz değişikliği boşuna durdurur. Kaldırıldığında üretime kaçan 27'den 28'e
çıkar — **bir kusur karşılığında 5 285 dakika**. Bu kapı yanlış kurulmuş değildir; hattaki yeri
yanlıştır. Alan adı değişikliğini 0,90, tümleşim ise 0,85 olasılıkla görüyorsa, ikisini birlikte
tutmanın
kazancı ölçülebilir biçimde küçüktür.

Tohum taraması sonucun ne kadar sağlam olduğunu gösterir: dört tohumun üçünde sözleşme
kapısı çıkar, birinde hiçbir kapı çıkmaz, birinde biçim kapısı da listeye girer. Yani karar
"sözleşme sınaması gereksizdir" değildir; **bu kusur kümesinde, bu hat sırasında ve bu
kapı süreleriyle** sözleşme kapısının marjinali maliyetinin altındadır. Kusur kümesi
değişirse — örneğin servisler arası sözleşmeler daha sık kırılırsa — aynı hesap kapıyı geri
getirir. Kararı taşıyan şey kapının adı değil, tablodaki dört sayısıdır.

#### Özet

- Sekiz kapılı hat 400 değişiklik ve 256 kusurla koşturuldu: 713 koşum, 34 140 çalıştırıcı
  dakikası, 89 yanlış tutma, üretime kaçan 27 kusur ve 39 600 dakikalık kaçış bedeli.
- Tuttuğu ile yalnız kendisinin tuttuğu ayrıdır: derleme 46 tutar, marjinali 8; sözleşme 18
  tutar, marjinali 1; biçim 51 tutar ve 51'i marjinaldir.
- En çok tutan kapı en değerli kapı değildir: biçim kapısının kaldırılması toplamı 318,
  sağlık kapısının kaldırılması 10 725 dakika artırır.
- Yanlış tutma tek başına kapıyı düşürmez: duman testi kapısı 28 temiz değişikliği boşuna
  durdurur, ama marjinali 15 kusur olduğu için yerinde kalır.
- Sözleşme kapısının kaldırılması toplamı 5 285 dakika düşürdü; bir kusur karşılığında 6 138
  çalıştırıcı dakikası ve 17 yanlış tutma ödenmekteydi.
- Sonuç kapının adına değil dört sayısına bağlıdır: dört tohumun üçü aynı kapıyı, biri
  hiçbirini gösterdi.

#### Sonraki Adım

Hat 713 koşum boyunca bir şey üretti ve bu ders o şeyi hiç saymadı: paketleme kapısının
çıkardığı **yapı çıktısı**. Yeşil biten her koşumun sonunda ortada bir paket vardır ve o
paket üretime giden şeydir. İki soru cevapsızdır. Bu paketin kimliği neye bağlıdır — hangi
işlemeye, hangi dala, kaçıncı koşuma — ve o kimlikten geriye, paketi üreten kaynağa
dönülebilir mi? İkincisi, aynı paket üretimde bir arıza çıkardığında bir önceki sürüme
dönmek için o sürümün hâlâ duruyor olması gerekir; saklama kuralı sıkılaştıkça geri
alınabilir sürüm penceresi daralır, gevşedikçe depo büyür. Sonraki ders bu iki sayıyı
birlikte ölçer.

### Yapı Çıktısı Yönetimi

Source: https://academia.sh/tr/kurslar/ci-cd/yapi-ciktisi-yonetimi

Hattın ürettiği çıktının sürüm kimliği ve saklama kuralı: altı kimlik şemasının benzersizliği ve kaynağa geri dönebilme oranı ölçülür, yedi saklama kuralı taranarak geri alınabilir sürüm penceresi, kesintisiz derinlik ve boşuna saklanan çıktı birlikte sayılır.

Önceki ders sekiz kapıyı ölçtü ve 713 koşumun ürettiği şeyi hiç saymadı: paketleme kapısının
çıkardığı **yapı çıktısı**. Yeşil biten her koşumun sonunda ortada bir paket vardır ve üretime
giden şey odur. Bu paketin iki sorusu vardır ve ikisi de bir kapı sorusudur.

Birincisi **kimlik**: paket neye göre adlandırılıyor, o addan geriye paketi üreten kaynağa
dönülebiliyor mu? İkincisi **saklama**: paket ne kadar süre duruyor, üretimde bir arıza
çıktığında dönülecek sürüm hâlâ orada mı? İkisi bağlıdır: silinen bir çıktı ancak kimliğinden
kaynağa dönülebiliyorsa yeniden üretilebilir. Yapı çıktısı deposunun bayt cinsinden büyümesi
Temel Kavramlar konusunda, taşınabilir etiketin çözümü İmajlar konusunda ölçüldü; burada o
ölçümler tekrarlanmaz, depo boyu **saklanan çıktı sayısı** olarak sayılır.

#### Çıktı Geçmişi

Model, bölgesel ölçüm ağının hattının yirmi iş gününde ürettiği çıktı geçmişidir. **Ağ da
geçmiş de kurgudur.** Süreler model dakikasıdır.

**HT19 — yirmi iş gününde 400 yapı çıktısı üretilir, 24 dakikada bir.** Koşumların yüzde 70'i
ana daldan, kalanı özellik dallarındandır; özellik dallarının yüzde 85'i iş bitince silinir.
Koşumların yüzde 6'sı aynı işlemenin yeniden koşturulmasıdır. Her on beşinci koşum bir sürüm
etiketi taşır.

**HT20 — altı kimlik şeması karşılaştırılır.** Şemanın taşıdığı bilgi tek bir alanla yazılır:
kimlik metni işleme özetini içeriyor mu.

**HT21 — bir kimlikten kaynağa dönebilmek için üç koşul birlikte gerekir:** kimlik benzersiz
olmalı, işlemeyi taşımalı ya da hat kaydı hâlâ bulunmalı (0,92 olasılık) ve işleme depoda
erişilebilir olmalı. Silinmiş dalın işlemesi erişilemez.

**HT22 — yirmi iş gününde 40 geri alma olayı olur.** Hedefin kaç üretim çıktısı geride olduğu
şu ağırlıklarla çekilir: 1 adım geri 50, 2 adım 18, 3 adım 10, 5 adım 7, 10 adım 6, 25 adım 5,
60 adım 4.

**HT23 — saklanan bir çıktıdan geri alma 6 model dakikasıdır**; çıktı silinmişse kaynaktan
yeniden üretim 55 dakikadır ve yalnız kimlik kaynağa çözülüyorsa mümkündür. Çözülmüyorsa
istek karşılanamaz.

**HT24 — depo boyu saklanan çıktı sayısıyla ölçülür**, bayt cinsinden değil.

```js
// gecmis.mjs — hattin yirmi is gununde urettigi yapi ciktilarinin kurgu gecmisi
const uretec = (tohum) => () => {
  tohum = (tohum * 1664525 + 1013904223) >>> 0;
  return tohum / 4294967296;
};

export const CIKTI = 400, ARA = 24;        // HT19: 24 dakikada bir cikti, 9600 dakikalik pencere
export const KAYIT = 0.92;                 // HT21: hat kaydinin hala bulunma olasiligi

const onalti = (r, n) => Array.from({ length: n }, () => '0123456789abcdef'[Math.floor(r() * 16)]).join('');

export const gecmisUret = (tohum) => {
  const r = uretec(tohum);
  const dallar = new Map();
  const liste = [];
  let isleme = onalti(r, 12), surum = [1, 4, 0];
  for (let i = 0; i < CIKTI; i++) {
    const yeniden = r() < 0.06;            // HT19: ayni islemenin yeniden kosturulmasi
    if (!yeniden) isleme = onalti(r, 12);
    const anaDal = r() < 0.70;
    const dal = anaDal ? 'ana' : `ozellik-${Math.floor(r() * 40)}`;
    if (!dallar.has(dal)) dallar.set(dal, { silindi: anaDal ? false : r() < 0.85 });
    if (i % 15 === 0 && i > 0) surum = [surum[0], surum[1], surum[2] + 1];
    liste.push({ no: i + 1, isleme, dal, anaDal, t: i * ARA,
      surum: surum.join('.'), etiketli: i % 15 === 0,
      erisilebilir: !dallar.get(dal).silindi,   // silinmis dalin islemesi depoda kalmaz
      kayit: r() < KAYIT });
  }
  return liste;
};

// HT20: kimlik semalari. isleme = kimlik islemeyi tasiyor mu, uret = kimlik metni.
export const semalar = [
  { ad: 'tasinabilir etiket', isleme: false, uret: () => 'son' },
  { ad: 'kosum numarasi', isleme: false, uret: (c) => `${c.no}` },
  { ad: 'anlamsal surum', isleme: false, uret: (c) => c.surum },
  { ad: 'dal + kosum numarasi', isleme: false, uret: (c) => `${c.dal}-${c.no}` },
  { ad: 'anlamsal surum + isleme', isleme: true, uret: (c) => `${c.surum}+${c.isleme.slice(0, 7)}` },
  { ad: 'isleme + kosum numarasi', isleme: true, uret: (c) => `${c.isleme.slice(0, 7)}-${c.no}` },
];

// HT21: kaynaga donus icin kimlik benzersiz olmali, islemeyi tasimali ya da hat kaydi
// bulunmali, ve isleme depoda erisilebilir olmali.
export const coz = (sema, gecmis) => {
  const sayac = new Map();
  for (const c of gecmis) sayac.set(sema.uret(c), (sayac.get(sema.uret(c)) ?? 0) + 1);
  let benzersiz = 0, kaynak = 0, kaynakAna = 0, ana = 0;
  const cozulur = new Set();
  for (const c of gecmis) {
    const tek = sayac.get(sema.uret(c)) === 1;
    if (tek) benzersiz++;
    const donus = tek && (sema.isleme || c.kayit) && c.erisilebilir;
    if (donus) { kaynak++; cozulur.add(c.no); }
    if (c.anaDal) { ana++; if (donus) kaynakAna++; }
  }
  return { benzersiz, kaynak, kaynakAna, ana, cozulur,
    cakisan: gecmis.length - benzersiz, uzunluk: sema.uret(gecmis[gecmis.length - 1]).length };
};
```

```js
// kimlik.mjs — sema basina benzersizlik ve kimlikten kaynaga geri donebilme orani
import { gecmisUret, semalar, coz, CIKTI } from './gecmis.mjs';

const gecmis = gecmisUret(30719);
const s = (x, w) => String(x).padStart(w);
const y = (a, b) => `${(100 * a / b).toFixed(1)}`;
const silinen = gecmis.filter((c) => !c.erisilebilir).length;

console.log(`cikti ${CIKTI}, ana dal ${gecmis.filter((c) => c.anaDal).length}, `
  + `silinmis dalda kalan ${silinen}, yeniden kosum ${CIKTI - new Set(gecmis.map((c) => c.isleme)).size}`);
console.log(`\n${'sema'.padEnd(26)}${s('uzunluk', 9)}${s('cakisan', 9)}${s('kaynaga donus', 15)}${s('ana dalda', 11)}`);
for (const sema of semalar) {
  const c = coz(sema, gecmis);
  console.log(`${sema.ad.padEnd(26)}${s(c.uzunluk, 9)}${s(c.cakisan, 9)}`
    + `${s(`${c.kaynak} (${y(c.kaynak, CIKTI)}%)`, 15)}${s(`${y(c.kaynakAna, c.ana)}%`, 11)}`);
}
```

```
cikti 400, ana dal 274, silinmis dalda kalan 103, yeniden kosum 28

sema                        uzunluk  cakisan  kaynaga donus  ana dalda
tasinabilir etiket                3      400       0 (0.0%)       0.0%
kosum numarasi                    3        0    269 (67.3%)      90.9%
anlamsal surum                    6      400       0 (0.0%)       0.0%
dal + kosum numarasi              7        0    269 (67.3%)      90.9%
anlamsal surum + isleme          14       50    261 (65.3%)      87.6%
isleme + kosum numarasi          11        0    297 (74.3%)     100.0%
```

#### Kimlik Neyi Taşıyor

İlk iki satır kimliğin **benzersizlik** koşulunu ayırır. Taşınabilir etiket her koşumda
yeniden yazılır: 400 çıktının 400'ü aynı kimliği paylaşır ve hiçbirinden kaynağa dönülemez.
Anlamsal sürüm de aynı durumdadır: her on beşinci koşumda artan bir sayı, aradaki on dört
çıktıyı ayırt edemez.

Koşum numarası benzersizdir ve kaynağa dönüş yüzde 67,3'e çıkar. Kalan boşluğun iki kaynağı
vardır: kimlik işlemeyi taşımadığı için bakılan hat kaydı yüzde 92 olasılıkla durur ve
silinmiş dallardaki 103 çıktının işlemesi depoda erişilemez. İkinci kısıt ana dal satırında
elenir — koşum numarası için ana dalda dönüş yüzde 90,9'dur, yani yüzde 100'e kalan 9,1 puan
tamamen kaybolan hat kaydından gelir.

İşleme özetini taşıyan son şema bu boşluğu kapatır: ana dalda yüzde 100. Kimlik kaynağı
kendi içinde taşıdığında dışarıdaki bir kayda ihtiyaç kalmaz. Genel oranın yine de yüzde
74,3'te kalması silinmiş özellik dallarındandır ve bu bir kimlik sorunu değildir. Beşinci
şema bir ara durumdur: anlamsal sürüm ile işleme özeti birleştirildiğinde 50 çıktı çakışır,
çünkü aynı işlemenin yeniden koşturulması aynı kimliği üretir. Kimlik kaynağı gösterir ama
**hangi koşumun** ürettiğini göstermez; koşum numarasının eklenmesi tam da bunu çözer.

#### Saklama Kuralı Bir Kapıdır

Saklama kuralı hangi çıktının geri alınabilir kaldığına karar verir, yani bir kapıdır ve
aynı üç sayıyla ölçülür. Aşağıdaki tarama yedi kuralı iki kimlik şeması altında koşturur.

```js
// saklama.mjs — saklama kurali tarandiginda geri alinabilir surum penceresi ve depo boyu
import { gecmisUret, semalar, coz, CIKTI, ARA } from './gecmis.mjs';

const GUN_DK = 480, DOGRUDAN_DK = 6, YENIDEN_DK = 55;   // HT23: geri alma ve yeniden uretim
const uretec = (tohum) => () => {
  tohum = (tohum * 1664525 + 1013904223) >>> 0;
  return tohum / 4294967296;
};

// HT22: yirmi is gununde 40 geri alma olayi; hedefin kac uretim ciktisi geride oldugu.
const DERINLIK = [[1, 50], [2, 18], [3, 10], [5, 7], [10, 6], [25, 5], [60, 4]];
const olaylar = (tohum, uretimler) => {
  const r = uretec(tohum), top = DERINLIK.reduce((a, d) => a + d[1], 0);
  return Array.from({ length: 40 }, () => {
    const T = 1200 + r() * (CIKTI * ARA - 1200);
    let x = r() * top, k = 1;
    for (const [d, w] of DERINLIK) { x -= w; if (x <= 0) { k = d; break; } }
    const gecmisi = uretimler.filter((c) => c.t < T);
    return { T, k, hedef: gecmisi[gecmisi.length - k] ?? null };
  });
};

const kurallar = [
  { ad: 'son 10 kosum', tut: (c, n) => c.no > n - 10 },
  { ad: 'son 25 kosum', tut: (c, n) => c.no > n - 25 },
  { ad: 'son 50 kosum', tut: (c, n) => c.no > n - 50 },
  { ad: 'son 100 kosum', tut: (c, n) => c.no > n - 100 },
  { ad: 'son 7 gun', tut: (c, n, T) => T - c.t <= 7 * GUN_DK },
  { ad: 'etiketli + son 25', tut: (c, n) => c.etiketli || c.no > n - 25 },
  { ad: 'hepsi', tut: () => true },
];

const gecmis = gecmisUret(30719);
const uretimler = gecmis.filter((c) => c.anaDal);
const olay = olaylar(60417, uretimler);
const son = CIKTI * ARA;

const olc = (kural, cozulur) => {
  let dogrudan = 0, yeniden = 0, olmaz = 0, dk = 0;
  for (const o of olay) {
    if (o.hedef === null) { olmaz++; continue; }
    const n = gecmis.filter((c) => c.t < o.T).length;
    if (kural.tut(o.hedef, n, o.T)) { dogrudan++; dk += DOGRUDAN_DK; }
    else if (cozulur.has(o.hedef.no)) { yeniden++; dk += YENIDEN_DK; }
    else olmaz++;
  }
  const saklanan = gecmis.filter((c) => kural.tut(c, CIKTI, son)).length;
  const kalanUretim = uretimler.filter((c) => kural.tut(c, CIKTI, son));
  // kesintisiz derinlik: en son uretim ciktisindan geriye dogru bosluksuz saklanan sayisi
  let kesintisiz = 0;
  for (let i = uretimler.length - 1; i >= 0 && kural.tut(uretimler[i], CIKTI, son); i--) kesintisiz++;
  // bosuna saklanan: hicbir geri alma olayinin hedefi olmayan cikti
  const hedefler = new Set(olay.filter((o) => o.hedef).map((o) => o.hedef.no));
  const bosuna = gecmis.filter((c) => kural.tut(c, CIKTI, son) && !hedefler.has(c.no)).length;
  return { dogrudan, yeniden, olmaz, dk, saklanan, kesintisiz, bosuna,
    pencere: kalanUretim.length ? (son - kalanUretim[0].t) / GUN_DK : 0 };
};

const s = (x, w) => String(x).padStart(w);
console.log(`uretim ciktisi ${uretimler.length}, geri alma olayi ${olay.length}, `
  + `en derin hedef ${Math.max(...olay.map((o) => o.k))} surum geride`);
console.log(`\n${'saklama kurali'.padEnd(20)}${s('saklanan', 10)}${s('pencere gun', 13)}${s('kesintisiz derinlik', 21)}${s('bosuna saklanan', 17)}`);
for (const k of kurallar) {
  const m = olc(k, new Set());
  console.log(`${k.ad.padEnd(20)}${s(m.saklanan, 10)}${s(m.pencere.toFixed(2), 13)}${s(m.kesintisiz, 21)}${s(m.bosuna, 17)}`);
}
for (const ad of ['tasinabilir etiket', 'isleme + kosum numarasi']) {
  const sema = semalar.find((x) => x.ad === ad);
  const { cozulur } = coz(sema, gecmis);
  console.log(`\nkimlik semasi: ${ad} (kaynaga donen ${cozulur.size} cikti)`);
  console.log(`${'saklama kurali'.padEnd(20)}${s('dogrudan', 10)}${s('yeniden uretim', 16)}${s('karsilanamaz', 14)}${s('kurtarma dk', 13)}`);
  for (const k of kurallar) {
    const m = olc(k, cozulur);
    console.log(`${k.ad.padEnd(20)}${s(m.dogrudan, 10)}${s(m.yeniden, 16)}${s(m.olmaz, 14)}${s(m.dk, 13)}`);
  }
}
```

```
uretim ciktisi 274, geri alma olayi 40, en derin hedef 60 surum geride

saklama kurali        saklanan  pencere gun  kesintisiz derinlik  bosuna saklanan
son 10 kosum                10         0.50                    8               10
son 25 kosum                25         1.25                   17               23
son 50 kosum                50         2.50                   31               47
son 100 kosum              100         5.00                   67               89
son 7 gun                  140         7.00                   98              125
etiketli + son 25           50        20.00                   17               44
hepsi                      400        20.00                  274              365

kimlik semasi: tasinabilir etiket (kaynaga donen 0 cikti)
saklama kurali        dogrudan  yeniden uretim  karsilanamaz  kurtarma dk
son 10 kosum                35               0             5          210
son 25 kosum                37               0             3          222
son 50 kosum                38               0             2          228
son 100 kosum               40               0             0          240
son 7 gun                   40               0             0          240
etiketli + son 25           37               0             3          222
hepsi                       40               0             0          240

kimlik semasi: isleme + kosum numarasi (kaynaga donen 297 cikti)
saklama kurali        dogrudan  yeniden uretim  karsilanamaz  kurtarma dk
son 10 kosum                35               5             0          485
son 25 kosum                37               3             0          387
son 50 kosum                38               2             0          338
son 100 kosum               40               0             0          240
son 7 gun                   40               0             0          240
etiketli + son 25           37               3             0          387
hepsi                       40               0             0          240
```

#### İlan Edilen Pencere ile Kesintisiz Derinlik

İkinci tablonun en öğretici satırı `etiketli + son 25`'tir. Bu kural 50 çıktı saklar ve
**20 günlük** bir pencere ilan eder: en eski saklanan üretim çıktısı pencerenin başındadır.
Aynı 50 çıktıyı saklayan `son 50 kosum` kuralının penceresi yalnız 2,5 gündür. Sekiz kat
geniş pencere daha iyi görünür. Geri alma olayları tersini söyler: etiketli kural 37 isteği
doğrudan karşılar, `son 50 kosum` ise 38'ini.

Farkı `kesintisiz derinlik` sütunu açıklar. Etiketli kuralın penceresi deliklidir; geriye
doğru boşluksuz saklanan üretim çıktısı yalnız 17 tanedir, `son 50 kosum` kuralında 31.
Geri alma isteği "yirmi gün öncesine dön" demez, "üç sürüm geriye dön" der; aradaki sürüm
silinmişse pencerenin uzunluğu işe yaramaz. **Saklama kuralı pencereyle değil, kesintisiz
derinlikle savunulur.**

`bosuna saklanan` sütunu kapının yanlış tutmasıdır. `hepsi` kuralı 400 çıktı saklar, 365'i
hiçbir geri alma olayının hedefi olmaz ve `son 100 kosum` kuralına göre tek bir istek fazla
karşılamaz: dört kat depo, sıfır marjinal kazanç. `son 100 kosum` ile `son 50 kosum`
arasındaki fark ise 2 istektir.

#### Kimlik ile Saklamanın Kesiştiği Yer

Son iki tablo iki kapıyı birleştirir. Taşınabilir etiket şemasında `son 10 kosum` kuralı beş
isteği **karşılayamaz**: çıktı silinmiştir ve kimlikten kaynağa dönülemediği için yeniden de
üretilemez. Aynı saklama kuralı, işleme özeti taşıyan kimlikle koşturulduğunda karşılanamayan
istek sıfıra iner; beş istek 55'er dakikalık yeniden üretimle karşılanır ve kurtarma süresi
210 dakikadan 485 dakikaya çıkar.

Takas açıktır. Sıkı saklama kuralı depoyu 400'den 10'a indirir ve 245 dakika ek kurtarma
süresi ödetir — **ama yalnız kimlik şeması buna izin veriyorsa.** Kimlik çözülmüyorsa aynı
sıkılık beş isteği tamamen kaybettirir. Kimlik şeması, saklama kuralının ne kadar
sıkılabileceğinin üst sınırını belirler.

Saklama kapısının dört sayısı böylece yazılabilir. `son 50 kosum` kuralı için: **tuttuğu**
38 geri alma isteği, **yalnız kendisinin tuttuğu** — `son 25` yerine seçildiğinde — 1 istek,
**kaçırdığı** 2 istek, **yanlış tuttuğu** 47 çıktı, **eklediği** 50 çıktılık depo ve 338
dakikalık kurtarma süresi.

#### Özet

- Taşınabilir etiket ve anlamsal sürüm şemalarında 400 çıktının tamamı çakışır ve kaynağa
  dönüş oranı sıfırdır; koşum numarası benzersizdir ve yüzde 67,3, işleme özeti taşıyan şema
  yüzde 74,3 verir.
- Ana dal satırı kısıtı ayırır: koşum numarası yüzde 90,9'da kalır (kayıp hat kaydı), işleme
  taşıyan kimlik yüzde 100'e çıkar. Genel orandaki boşluk silinmiş özellik dallarındandır ve
  hiçbir kimlik şeması onu kapatmaz.
- İlan edilen pencere yanıltıcıdır: `etiketli + son 25` kuralı 20 günlük pencere gösterir ama
  kesintisiz derinliği 17'dir ve aynı depoyla 2,5 günlük pencere veren kuraldan bir istek az
  karşılar.
- Depoyu dört katına çıkarmanın marjinali sıfır olabilir: `hepsi` kuralı 400 çıktı saklar,
  365'i hiç kullanılmaz ve `son 100 kosum` ile aynı 40 isteği karşılar.
- Kimlik şeması saklamanın üst sınırını belirler: `son 10 kosum` kuralı çözülebilir kimlikle
  sıfır karşılanamayan istek ve 485 dakika, çözülemeyen kimlikle beş karşılanamayan istek
  üretir.

#### Sonraki Adım

Buraya kadarki dört ders hattın **neyi tuttuğunu** saydı ve süreyi hep bir bedel kalemi
olarak yazdı: kapının eklediği dakika, beklemenin ödettiği dakika, yeniden üretimin aldığı
dakika. Sürenin kendisi ise hiç hedef alınmadı. Oysa ilk dersin tablosu bir şey söylemişti —
koşum ucuzladıkça en iyi birleştirme aralığı daha sık olana kayıyor, yani hattın süresini
kısaltmak yalnız bekleyeni rahatlatmakla kalmıyor, kapıların ekonomisini de değiştiriyor.
Sonraki ders bu süreye doğrudan bakar: aşamalar arasında yeniden kullanılabilen iş nedir,
paralel koşturmanın sınırı nerededir ve hangi noktadan sonra kaynak eklemek hattı
kısaltmamaya başlar.

### Önbellek ve Paralellik

Source: https://academia.sh/tr/kurslar/ci-cd/onbellek-ve-paralellik

Hattın kendi süresi ölçülür: aşama önbelleği gerçek dosyaların içerik özetinden anahtar üretir, on iki koşumluk bir dizide önbellekli ve önbelleksiz süre, isabet oranı ve isabetin değişiklik türüne göre dağılımı sayılır, ardından paralellik uygulanıp kritik yolun koyduğu sınır gösterilir.

Önceki ders hattın ürettiği çıktıyı sürümledi ve sakladı: her koşumun çıktısı kendi kimliğiyle
duruyor, hangi değişiklikten doğduğu okunabiliyor. Bir şey hiç sayılmadı — o çıktının kaç dakikada
üretildiği. Hat her koşumda bütün aşamaları baştan koşturuyor, oysa aşamaların çoğunun girdisi iki
koşum arasında hiç değişmiyor.

Bu ders hattın kendi süresini ölçer. Elde iki kaldıraç var: aynı girdiyle aynı işi yapan aşamayı
bir daha koşturmamak — **aşama önbelleği** — ve birbirine bağlı olmayan aşamaları aynı anda
koşturmak — **paralellik**. İkisinin de sınırı vardır ve sınırlar aynı yerde durmaz. Kapı ölçüsü
burada iki uçludur: bir aşama önbellekten geldiğinde o kapı **hiç koşmaz** ve kapının kaçırdığı
tam olarak orada doğar.

**HT25.** Hat `node` ile kurulan bir modeldir. Aşama süreleri modelin bildirdiği nominal
değerlerdir, koşum makinesinden ölçülmez; ortamdan bağımsız nicelikler koşan aşama sayısı, isabet
sayısı ve önbellek girdisidir. **HT26.** Aşama anahtarı gerçek dosyalardan hesaplanır: dosyalar
diske yazılır, okunur, özetlenir. **HT27.** Değişiklik dizisi kurgu bölgesel ölçüm ağının iki
haftalık düzeltme akışını temsil eder; rastgelelik yoktur, dizi elle yazılmıştır. **HT28.** Kusur
kümesi sekiz kusurdur ve her kusurun hangi kapılarda yakalanabileceği bildirilmiştir; koşan kapı
kendi kusurunu yakalar, koşmayan kapı yakalamaz. **HT29.** Önbellek girdileri silinmez, saklama
kuralı uygulanmaz. **HT30.** Çalıştırıcılar özdeştir ve kuyrukta bekleme sıfırdır — o kalem sonraki
derste ölçülür.

İmaj katmanlarının önbelleği bu kursun dışında ölçüldü ve tekrarlanmaz; burada ölçülen birim
**hat aşamasıdır** — kendi girdisi, kendi süresi ve kendi çıktısı olan bir iş adımı.

#### Aşama Anahtarı

Önbelleğin tek kuralı vardır: **bir aşamanın anahtarı, o aşamanın sonucunu değiştirebilecek her
şeyi kapsamalıdır.** Kapsanmayan bir girdi değiştiğinde anahtar aynı kalır, aşama koşmaz ve hat
eski sonucu doğru sanır. Modelde her aşama girdilerini kendisi **bildirir**.

```js
// olcum-agi/hat.mjs — hat asamalari ve asama onbellegi (model)
import { mkdirSync, writeFileSync, readFileSync, readdirSync, statSync, appendFileSync, rmSync } from "node:fs";
import { createHash } from "node:crypto";

const KOK = "proje";
const ozet = (s) => createHash("sha256").update(s).digest("hex").slice(0, 12);
const metin = (ad, n) => Array.from({ length: n }, (_, i) => `// ${ad} ${i}\n`).join("");
const AGAC = {
  "bagimlilik-listesi": "olcum-istemcisi 3.1\nkuyruk-baglayici 1.9\ncsv-cozumleyici 2.4\n",
  "kaynak/toplayici.js": metin("toplayici", 260), "kaynak/dogrulayici.js": metin("dogrulayici", 180),
  "kaynak/faturalama.js": metin("faturalama", 240), "sinama/birim/toplayici.js": metin("birim", 300),
  "sinama/tumlesim/akis.js": metin("tumlesim", 220), "sinama/sozlesme/api.js": metin("sozlesme", 90),
  "yapilandirma/ortam.json": '{"zaman-asimi":30}\n', "kurallar/statik.json": '{"kural-sayisi":48}\n',
  "sozlesmeler/api.json": '{"alan-sayisi":17}\n',
};

// sure: bildirilmis nominal asama suresi (saniye). taban: onbellege yazilan cikti (bayt).
// girdi: asamanin BILDIRDIGI girdiler — kurallar/ ve sozlesmeler/ hicbir asamada bildirilmemistir.
export const ASAMALAR = [
  { ad: "bagimlilik-cozumu", girdi: ["bagimlilik-listesi"], onceki: [], sure: 42, taban: 41e6 },
  { ad: "derleme", girdi: ["kaynak"], onceki: ["bagimlilik-cozumu"], sure: 65, taban: 8.6e6 },
  { ad: "statik-cozumleme", girdi: ["kaynak"], onceki: ["bagimlilik-cozumu"], sure: 27, taban: 12e4 },
  { ad: "birim-sinamasi", girdi: ["sinama/birim"], onceki: ["derleme"], sure: 38, taban: 9e4 },
  { ad: "tumlesim-sinamasi", girdi: ["sinama/tumlesim", "yapilandirma"], onceki: ["derleme"], sure: 96, taban: 26e4 },
  { ad: "paketleme", girdi: ["yapilandirma"], onceki: ["derleme"], sure: 18, taban: 9.1e6 },
  { ad: "sozlesme-sinamasi", girdi: ["sinama/sozlesme"], onceki: ["paketleme"], sure: 31, taban: 6e4 },
  { ad: "imaj-uretimi", girdi: [], onceki: ["paketleme"], sure: 24, taban: 12.3e6 },
];
export const GERI_YUKLEME = 2; // her isabette ciktinin onbellekten geri yuklenme suresi (saniye)
export const SERI = ASAMALAR.reduce((t, a) => t + a.sure, 0);

export const kurAgac = () => {
  rmSync(KOK, { recursive: true, force: true });
  for (const [y, i] of Object.entries(AGAC)) {
    mkdirSync(`${KOK}/${y}`.split("/").slice(0, -1).join("/"), { recursive: true });
    writeFileSync(`${KOK}/${y}`, i);
  }
};
export const degistir = (yol, no) => appendFileSync(`${KOK}/${yol}`, `// duzeltme ${no}\n`);
const gez = (alt) => readdirSync(`${KOK}/${alt}`, { withFileTypes: true })
  .flatMap((g) => (g.isDirectory() ? gez(`${alt}/${g.name}`) : [`${alt}/${g.name}`])).sort();
const yollari = (g) => (statSync(`${KOK}/${g}`).isDirectory() ? gez(g) : [g]);

// Asama anahtari: asama adi + bildirilen girdilerin gercek icerigi + onceki asamalarin anahtarlari.
export const kosturHat = (onbellek) => {
  const anahtar = {}, sonuc = { isabet: [], kosan: [], sure: 0 };
  for (const a of ASAMALAR) {
    const dosyalar = a.girdi.flatMap(yollari);
    anahtar[a.ad] = ozet(a.ad + dosyalar.map((y) => y + readFileSync(`${KOK}/${y}`)).join() +
      a.onceki.map((o) => anahtar[o]).join());
    if (onbellek.has(anahtar[a.ad])) { sonuc.isabet.push(a.ad); sonuc.sure += GERI_YUKLEME; continue; }
    onbellek.set(anahtar[a.ad], a.taban + dosyalar.reduce((t, y) => t + statSync(`${KOK}/${y}`).size, 0));
    sonuc.kosan.push(a.ad); sonuc.sure += a.sure;
  }
  return sonuc;
};
```

Anahtara önceki aşamaların anahtarlarının girmesi bir zincir kurar: bir aşama ıskaladığında ona
bağlı **her** aşama da ıskalar, kendi girdisi değişmemiş olsa bile. Bu önbelleğin doğru tarafıdır;
yanlış tarafı bildirim listesindedir — `kurallar/` ve `sozlesmeler/` hattın gerçek girdileridir ama
hiçbir aşama onları bildirmez.

#### On İki Koşum

Dizi on iki koşumda kurgu ağın bir düzeltme akışını yürütür: kaynak ve sınama değişiklikleri, bir
bağımlılık listesi, bir yapılandırma, hiçbir şeyin değişmediği bir koşum ve bildirilmemiş dizinlere
dokunan iki değişiklik. Sekiz kusur diziye serpiştirilmiştir.

```js
// olcum-agi/kosum.mjs — on iki koşumluk degisiklik dizisi, onbellekli ve onbelleksiz
import { kurAgac, degistir, kosturHat, ASAMALAR, SERI } from "./hat.mjs";

// Kurgu iki haftalik dizi: dokunulan dosya, degisiklik turu, tasidigi kusur, kusuru
// yakalayabilecek kapilar. Rastgelelik yok; dizi elle yazilmistir.
const DIZI = [
  ["", "ilk kosum"], ["kaynak/toplayici.js", "kaynak", "K1", ["birim-sinamasi", "tumlesim-sinamasi"]],
  ["kaynak/dogrulayici.js", "kaynak", "K2", ["statik-cozumleme"]], ["sinama/birim/toplayici.js", "sinama"],
  ["", "degisiklik yok"], ["bagimlilik-listesi", "bagimlilik", "K3", ["tumlesim-sinamasi"]],
  ["kurallar/statik.json", "kural", "K4", ["statik-cozumleme"]],
  ["kaynak/faturalama.js", "kaynak", "K5", ["birim-sinamasi", "statik-cozumleme"]],
  ["yapilandirma/ortam.json", "yapilandirma"], ["sozlesmeler/api.json", "sozlesme", "K6", ["sozlesme-sinamasi"]],
  ["sinama/tumlesim/akis.js", "sinama", "K7", ["tumlesim-sinamasi"]],
  ["kaynak/toplayici.js", "kaynak", "K8", ["birim-sinamasi"]],
];

kurAgac();
const onbellek = new Map(), tur = {}, kacan = [];
const kapi = Object.fromEntries(ASAMALAR.map((a) => [a.ad, { kosum: 0, sure: 0, tuttu: 0, yalniz: 0 }]));
let topIsabet = 0, topSure = 0;

console.log(" no  degisiklik".padEnd(32) + "tur".padEnd(15) + "isabet kosan sure(s)");
DIZI.forEach(([dosya, t, kusur, yakalayan], i) => {
  if (dosya) degistir(dosya, i);
  const s = kosturHat(onbellek);
  topIsabet += s.isabet.length; topSure += s.sure;
  tur[t] = tur[t] || { deneme: 0, isabet: 0 };
  tur[t].deneme += ASAMALAR.length; tur[t].isabet += s.isabet.length;
  for (const ad of s.kosan) { kapi[ad].kosum++; kapi[ad].sure += ASAMALAR.find((a) => a.ad === ad).sure; }
  if (kusur) {
    const tutan = yakalayan.filter((y) => s.kosan.includes(y));
    if (!tutan.length) kacan.push(kusur);
    else for (const y of tutan) { kapi[y].tuttu++; if (tutan.length === 1) kapi[y].yalniz++; }
  }
  console.log(`  ${String(i + 1).padStart(2)}  ${dosya || "(" + t + ")"}`.padEnd(32) + t.padEnd(15) +
    String(s.isabet.length).padStart(4) + String(s.kosan.length).padStart(6) + String(s.sure).padStart(7));
});

const deneme = DIZI.length * ASAMALAR.length, onbelleksiz = DIZI.length * SERI;
console.log(`\nonbelleksiz ${onbelleksiz} s, onbellekli ${topSure} s (%${(100 - (topSure * 100) / onbelleksiz).toFixed(1)} kisalma); ` +
  `isabet ${topIsabet}/${deneme} (%${((topIsabet * 100) / deneme).toFixed(1)}); ` +
  `onbellek ${onbellek.size} girdi ${([...onbellek.values()].reduce((a, b) => a + b, 0) / 1e6).toFixed(1)} MB`);
console.log("\nisabet / degisiklik turu" + " ".repeat(9) + "kapi          kosum  sure  tuttugu  yalniz");
const satir = Object.entries(tur).map(([t, d]) => `  ${t.padEnd(15)}${d.isabet}/${d.deneme} %${((d.isabet * 100) / d.deneme).toFixed(0)}`);
ASAMALAR.forEach((a, i) => {
  const k = kapi[a.ad];
  console.log((satir[i] || "").padEnd(33) + a.ad.padEnd(19) + String(k.kosum).padStart(3) +
    String(k.sure + "s").padStart(7) + String(k.tuttu).padStart(7) + String(k.yalniz).padStart(8));
});
console.log(`\nkusur kumesi 8, kacan ${kacan.length}: ${kacan.join(", ")} — bildirilmemis girdi, bayat isabet`);
```

```
 no  degisiklik                 tur            isabet kosan sure(s)
   1  (ilk kosum)               ilk kosum         0     8    341
   2  kaynak/toplayici.js       kaynak            1     7    301
   3  kaynak/dogrulayici.js     kaynak            1     7    301
   4  sinama/birim/toplayici.js sinama            7     1     52
   5  (degisiklik yok)          degisiklik yok    8     0     16
   6  bagimlilik-listesi        bagimlilik        0     8    341
   7  kurallar/statik.json      kural             8     0     16
   8  kaynak/faturalama.js      kaynak            1     7    301
   9  yapilandirma/ortam.json   yapilandirma      4     4    177
  10  sozlesmeler/api.json      sozlesme          8     0     16
  11  sinama/tumlesim/akis.js   sinama            7     1    110
  12  kaynak/toplayici.js       kaynak            1     7    301

onbelleksiz 4092 s, onbellekli 2273 s (%44.5 kisalma); isabet 46/96 (%47.9); onbellek 50 girdi 287.5 MB

isabet / degisiklik turu         kapi          kosum  sure  tuttugu  yalniz
  ilk kosum      0/8 %0          bagimlilik-cozumu    2    84s      0       0
  kaynak         4/32 %13        derleme              6   390s      0       0
  sinama         14/16 %88       statik-cozumleme     6   162s      2       1
  degisiklik yok 8/8 %100        birim-sinamasi       7   266s      3       1
  bagimlilik     0/8 %0          tumlesim-sinamasi    8   768s      3       2
  kural          8/8 %100        paketleme            7   126s      0       0
  yapilandirma   4/8 %50         sozlesme-sinamasi    7   217s      0       0
  sozlesme       8/8 %100        imaj-uretimi         7   168s      0       0

kusur kumesi 8, kacan 2: K4, K6 — bildirilmemis girdi, bayat isabet
```

#### İsabetin Okunması

Toplam süre **4 092 saniyeden 2 273 saniyeye** iner: yüzde 44,5 kısalma, 96 aşama denemesinde 46
isabet. Bu tek sayı hiçbir karar taşımaz; kararı türe göre dağılım verir.

Sınama değişikliklerinde isabet **yüzde 88'dir** — bir sınama dosyasına dokunmak yalnız o sınama
aşamasını koşturur. Kaynak değişikliklerinde isabet **yüzde 13'e** düşer: kaynak zincirin
dibindedir, ona dokunulduğunda yedi aşama yeniden koşar. Bağımlılık listesi değişikliğinde isabet
**sıfırdır**; o dosya en alttaki aşamanın girdisidir ve hattın tamamını geçersizleştirir. Kazanç
hattın hangi katmanına ne sıklıkla dokunulduğuna bağlıdır.

Yüzde 100 gösteren üç satırdan yalnız biri doğrudur. Hiçbir şeyin değişmediği koşumda sekiz
aşamanın sekizi isabet eder ve hat 16 saniyede biter; önbelleğin yapması gereken tam olarak budur.
Kural ve sözleşme değişikliklerinde de sekiz isabet görünür — ama orada bir şey değişmiştir.
`kurallar/statik.json` statik çözümlemenin kural kümesi, `sozlesmeler/api.json` sözleşme
sınamasının ölçütüdür; ikisi de hiçbir aşamanın bildirdiği girdi değildir. Anahtar değişmez, kapı
koşmaz, hat yeşil biter. **En yüksek isabet oranını veren iki değişiklik türü, önbelleğin
görmediği türdür.**

Kapı hesabı bunun bedelini yazar. Sekiz kusurdan altısı yakalanır, **ikisi kaçar**: K4 kural
kümesinin sıkılaştırılmasıyla görünür hâle gelen kusurdur, K6 sözleşme alanının değişmesinden
doğar; ikisi de yakalanabilecekleri kapı önbellekten geldiği için geçer. Marjinal değer sütunu
kapıları ayırır: tümleşim sınaması sekiz koşumda **768 saniye** harcar, üç kusur tutar ve bunların
**ikisini yalnız kendisi** yakalar — hattın en pahalı ve en gerekli kapısıdır. Birim sınaması 266
saniyede üç kusur tutar ama yalnız birini tek başına; öteki ikisinden birini tümleşim
sınaması, öbürünü statik çözümleme de yakalardı. Dört aşama — bağımlılık çözümü, derleme,
paketleme, imaj üretimi — hiç kusur tutmaz; onlar kapı değil üretim adımıdır ve bu hesaba
girmez.

Bildirim düzeltilirse bedel tablodan okunur: yedinci koşumda statik çözümleme 2 saniyelik isabet
yerine 27 saniye, onuncu koşumda sözleşme sınaması 2 yerine 31 saniye alır. Toplam **54 saniye**,
önbellekli sürenin yüzde 2,4'ü — karşılığında iki kusur kaçmaz.

Önbelleğin kendi bedeli üç kalemdir. Depo: on iki koşumda **50 girdi ve 287,5 MB** birikir,
hiçbiri silinmez. Geri yükleme: 46 isabetin her biri 2 saniye ödetir, toplam **92 saniye**.
Üçüncüsü sayıya gelmez ve en pahalısıdır — yukarıdaki iki kaçan kusur, geçersizleşme hatasının
faturasıdır.

#### Kritik Yol

Önbellek tekrarlanan işi kaldırdı; kalan iş hâlâ tek sırada koşuyor. Aşamalar bir bağımlılık
çizgesi kurar, birbirine bağlı olmayanlar aynı anda koşabilir ve sınırı çizgenin en uzun zinciri
belirler.

```js
// olcum-agi/paralel.mjs — asama cizgesi, calistirici sayisi ve kritik yol
import { ASAMALAR, GERI_YUKLEME } from "./hat.mjs";

const bul = (ad) => ASAMALAR.find((a) => a.ad === ad);
const ardil = (ad) => ASAMALAR.filter((b) => b.onceki.includes(ad));
// Bir asamadan hattin sonuna kalan en uzun sure. Kritik yol, basi en buyuk olan zincirdir.
const kalan = (a) => a.sure + Math.max(0, ...ardil(a.ad).map(kalan));
const zincir = (a) => (ardil(a.ad).length
  ? `${a.ad}(${a.sure}) -> ` + zincir(ardil(a.ad).reduce((x, y) => (kalan(x) >= kalan(y) ? x : y))) : `${a.ad}(${a.sure})`);

// Liste cizelgelemesi: oncekileri BITMIS asamalar arasindan kalan yolu en uzun olan,
// bosalan calistiriciya verilir. Calistirici sayisi n.
const cizelgele = (sure, n) => {
  const biten = new Set(), suren = [], bekleyen = ASAMALAR.map((a) => a.ad);
  let saat = 0;
  while (biten.size < ASAMALAR.length) {
    const hazir = bekleyen.filter((ad) => bul(ad).onceki.every((o) => biten.has(o)))
      .sort((x, y) => kalan(bul(y)) - kalan(bul(x)));
    while (suren.length < n && hazir.length) {
      const ad = hazir.shift();
      bekleyen.splice(bekleyen.indexOf(ad), 1);
      suren.push({ ad, biter: saat + sure(ad) });
    }
    saat = Math.min(...suren.map((s) => s.biter));
    for (let i = suren.length - 1; i >= 0; i--) if (suren[i].biter <= saat) { biten.add(suren[i].ad); suren.splice(i, 1); }
  }
  return saat;
};

const bas = ASAMALAR.filter((a) => !a.onceki.length).reduce((x, y) => (kalan(x) >= kalan(y) ? x : y));
const sinir = kalan(bas), seri = ASAMALAR.reduce((t, a) => t + a.sure, 0);
const soguk = (ad) => bul(ad).sure;
// Kaynak degisikliginde onbellek yalniz bagimlilik-cozumunde isabet eder (yukaridaki tablo, 2. kosum).
const isabetli = (ad) => (ad === "bagimlilik-cozumu" ? GERI_YUKLEME : bul(ad).sure);

console.log(`seri ${seri} s, kritik yol ${sinir} s: ${zincir(bas)}`);
console.log("\ncalistirici   soguk  kritik yola uzaklik  ek calistiricinin kazanci  onbellekli (kaynak degisikligi)");
let onceki = null;
for (let n = 1; n <= 6; n++) {
  const s = cizelgele(soguk, n);
  console.log(String(n).padStart(9) + String(s + " s").padStart(9) + String(s - sinir + " s").padStart(18) +
    (onceki === null ? "-" : onceki - s + " s").padStart(24) + String(cizelgele(isabetli, n) + " s").padStart(28));
  onceki = s;
}
```

```
seri 341 s, kritik yol 203 s: bagimlilik-cozumu(42) -> derleme(65) -> tumlesim-sinamasi(96)

calistirici   soguk  kritik yola uzaklik  ek calistiricinin kazanci  onbellekli (kaynak degisikligi)
        1    341 s             138 s                       -                       301 s
        2    218 s              15 s                   123 s                       178 s
        3    203 s               0 s                    15 s                       163 s
        4    203 s               0 s                     0 s                       163 s
        5    203 s               0 s                     0 s                       163 s
        6    203 s               0 s                     0 s                       163 s
```

İkinci çalıştırıcı **123 saniye** kazandırır, üçüncüsü **15 saniye**, dördüncüsü **sıfır**. Süre üç
çalıştırıcıda düzleşip 203 saniyede durur; bu sayı çalıştırıcı sayısından değil çizgeden gelir.
Sınırı koyan zincir bağımlılık çözümü, derleme ve tümleşim sınamasıdır: 42 + 65 + 96. Üçü birbirini
beklemek zorundadır, dolayısıyla hiçbir çalıştırıcı sayısı hattı 203 saniyenin altına indiremez. En
yüksek hızlanma **1,68 kattır** ve dördüncü çalıştırıcıdan sonrası ödenmiş boş kapasitedir.

Sınırı aşmanın tek yolu zinciri kısaltmaktır: tümleşim sınaması iki bağımsız parçaya bölünürse
kritik yol 42 + 65 + 48'e iner. Bu bir hat tasarımı kararıdır, çalıştırıcı satın alma kararı
değildir.

İki kaldıraç birbirini çarpar ama aynı yerden tutmaz. Son sütun kaynak değişikliği koşumunu
gösterir: önbellek zincirin başındaki 42 saniyeyi 2'ye indirir, üç çalıştırıcıyla süre **163
saniyeye** oturur. Önbellek zincirin **başını** kısaltır, paralellik zincirin **dışındaki** işi
kaldırır; ikisi de ortadaki 65 ve 96 saniyeye dokunamaz. Hattın gerçek tabanı orasıdır.

#### Özet

- Aşama anahtarı, aşamanın **bildirdiği** girdilerin içeriği ile önceki aşamaların anahtarlarından
  türer. Bildirilmeyen girdi değiştiğinde anahtar değişmez, kapı koşmaz ve hat yeşil biter.
- Önbellek on iki koşumda süreyi 4 092 saniyeden 2 273 saniyeye indirdi (yüzde 44,5), isabet 96
  denemede 46 oldu. Dağılım kararı taşır: sınamada yüzde 88, kaynakta yüzde 13, bağımlılık
  listesinde sıfır.
- Kural ve sözleşme değişikliklerinde görünen yüzde 100 isabet, önbelleğin körlüğüdür. İki kusur
  (K4, K6) yakalanabilecekleri kapı önbellekten geldiği için kaçtı; bildirimin düzeltilmesi on iki
  koşuma 54 saniye, yani yüzde 2,4 ekliyor.
- Kapıların marjinal değeri eşit değildir: tümleşim sınaması 768 saniyede üç kusur tuttu, ikisini
  yalnız kendisi; birim sınaması 266 saniyede üç kusur tuttu ama yalnız birini tek başına.
  Önbelleğin bedeli ise 50 girdi, 287,5 MB depo ve 92 saniyelik geri yüklemedir.
- Paralellik hattı 203 saniyenin altına indiremez. Sınırı bağımlılık çözümü → derleme → tümleşim
  sınaması zinciri koyar; üçüncü çalıştırıcıdan sonra ek çalıştırıcının kazancı sıfırdır.

#### Sonraki Adım

Bu ölçüm iki şeyi bedava saydı. Kırk altı isabetin her biri, önceki koşumun çıktısını hâlâ
bulabilen bir makine varsayar; HT30 de kuyrukta beklemeyi sıfır alıp üç çalıştırıcının her an
hazır olduğunu kabul etti. İki varsayım da aynı karara bağlıdır: hattın nerede koştuğu. Hat her
koşumda temiz kurulan bir makinede koşarsa önbellek her seferinde boştur ve buradaki 1 819
saniyelik kazanç buharlaşır; kendi tutulan makinelerde koşarsa önbellek kalır, ama makineler
bekler, eskir ve bakım ister. Sonraki ders aynı hattı iki çalıştırıcı türünde koşturup dört kalemi
sayar — kuyrukta bekleme, koşum başına maliyet, önbelleğin kalıcılığı ve bakım yükü — sonra eşiği
hesaplar: günde kaç koşumdan sonra hangisi ucuzluyor.

### Hat Çalıştırıcıları

Source: https://academia.sh/tr/kurslar/ci-cd/hat-calistiricilari

Aynı hat iki çalıştırıcı türünde koşturulur: kuyrukta bekleme, koşum başına maliyet kalemi, önbelleğin koşumlar arasında kalıp kalmaması ve bakım kalemi sayılır, günde kaç koşumdan sonra hangi türün ucuzladığı hesaplanır ve ölçünün göz ardı ettiği kalemler adıyla yazılır.

Önceki ders hattın süresini 4 092 saniyeden 2 273 saniyeye indirdi ve sınırın 203 saniyede
durduğunu gösterdi. O ölçümün altında iki sessiz varsayım vardı: önbellek koşumlar arasında
yerinde duruyordu ve çalıştırıcılar her an hazırdı. İkisi de bedava değildir, ikisi de aynı karara
bağlıdır — hattın **nerede** koştuğu.

Seçenek iki türdür. **Yönetilen çalıştırıcı** koşum başına ayrılan, işi bitince dağılan bir
makinedir; her koşum temiz bir diskte başlar. **Kendi barındırılan çalıştırıcı** sürekli duran,
sahibi olan bir makinedir; diski koşumlar arasında kalır. Aradaki fark bir tercih değil, dört
ayrı sütundur: kuyrukta bekleme, koşum başına ödenen kalem, önbelleğin kalıcılığı ve bakım yükü.

**HT31.** İki tür `node` ile kurulan bir modeldir; tür olarak anılır, ürün ya da sağlayıcı adı
geçmez. **HT32.** Koşum süreleri önceki dersten alınır: yönetilen tür her koşumda soğuk başladığı
için 203 saniye, kendi barındırılan tür önbelleğini koruduğu için 163 saniye. **HT33.** Geliş
anları kendi yazılan üreteçle üretilir, tohum **4831**; kurgu iş günü üç yığılmadan oluşur, çünkü
kurgu ölçüm ağının ekibi işlemelerini sabah, öğleden sonra ve gün kapanışında yolluyor. **HT34.**
Bedeller **birim** cinsindendir, para birimi yazılmaz. **HT35.** Ölçüm tek günü kapsar; makine
arızası, yedek kapasite ve başarısız biten koşumun yeniden koşturulması modele girmez. **HT36.**
Kapı hesabı önceki dersin sekiz kusurluk kümesini ve on iki koşumluk dizisini sürdürür.

#### İki Tür, Aynı Hat

Modelde bir tür dört sayıyla tanımlanır: kaç eşzamanlı yuvası olduğu, bir koşumun kaç saniye
sürdüğü, koşumun nasıl ücretlendirildiği ve kaç bakım kalemi taşıdığı.

```js
// olcum-agi/calistirici.mjs — iki calistirici turu ve bir gunun kuyrugu (model)
export const TOHUM = 4831;
export const uretec = (t) => () => ((t = (t * 48271) % 2147483647) / 2147483647);

// sure: 05'te olculen uc yuvali kosum suresi (saniye). yonetilen her kosumda soguk baslar (203 s),
// kendi barindirilan onbellegi koruyup sicak kosar (163 s). isabet: 05'in olctugu oran.
export const TUR = {
  yonetilen: { yuva: 8, sure: 203, isabet: 0, dakikaBedeli: 0.42, sabitGunluk: 0, kalem: 1, bakim: [] },
  barindirilan: {
    yuva: 2, sure: 163, isabet: 0.479, dakikaBedeli: 0, sabitGunluk: 2 * 11 + (6 * 28) / 30, kalem: 2,
    bakim: ["isletim sistemi yamasi", "disk dolmasi", "calistirici surumu", "ag ve sertifika", "kapasite/donanim"],
  },
};

// Kurgu is gunu: kosumlar uc yigilmaya (10:00, 14:00, 17:00) ucgen dagilimla dusuyor.
export const gelisler = (n, rnd) => Array.from({ length: n }, () => {
  const m = [10, 14, 17][Math.floor(rnd() * 3)] * 3600;
  return Math.round(m + (rnd() + rnd() - 1) * 1200); // ±20 dakika
}).sort((a, b) => a - b);

// Yuva cizelgesi: her gelis en erken bosalan yuvaya girer; bekleme = bosalma ani - gelis ani.
export const gun = (n, tur, rnd) => {
  const bos = Array(tur.yuva).fill(0), bekleme = [];
  for (const g of gelisler(n, rnd)) {
    const i = bos.indexOf(Math.min(...bos));
    const b = Math.max(0, bos[i] - g);
    bekleme.push(b); bos[i] = g + b + tur.sure;
  }
  return {
    ortBekleme: bekleme.reduce((a, b) => a + b, 0) / n,
    enUzun: Math.max(...bekleme),
    bekleyen: bekleme.filter((b) => b > 0).length,
    maliyet: tur.sabitGunluk + (n * tur.sure * tur.dakikaBedeli) / 60,
  };
};
```

Kendi barındırılan türün sabit günlük bedeli iki kalemden toplanır: iki makinenin günlük bedeli ve
ayda altı saatlik bakım emeğinin güne düşen payı. Yönetilen türde sabit bedel sıfırdır ve tek
kalem koşum süresidir. Bu, iki türün asıl farkıdır — biri **koşum** başına, öteki **zaman** başına
ödetir.

Yuva sayısı da aynı eksene oturur. Kendi barındırılan türde yuva satın alınan bir makinedir ve
sayısı iki ile sınırlıdır; yönetilen türde sekiz yuva modelin bildirdiği bir üst sınırdır, makine
değil. Bu sınırın kendisi bir kalem olarak sayılır — sekiz eşzamanlı koşum bedava değildir, o
sayının nereden geldiği modelde bildirilmiştir ve gerçek bir kurulumda pazarlık konusudur.

#### Bir Günün Kuyruğu

Aynı hat, aynı geliş anları, iki tür. Altı ayrı yoğunlukta koşturulur: günde 10 koşumdan 80 koşuma.

```js
// olcum-agi/esik.mjs — ayni hat iki calistirici turunde: bekleme, maliyet, esik
import { TUR, TOHUM, uretec, gun } from "./calistirici.mjs";

const dk = (s) => (s / 60).toFixed(1);
console.log(`tohum ${TOHUM}; yuva: yonetilen ${TUR.yonetilen.yuva}, barindirilan ${TUR.barindirilan.yuva}`);
console.log("\n              yonetilen (soguk, 203 s)        kendi barindirilan (sicak, 163 s)");
console.log("kosum/gun  bekleme  enuzun  bekleyen  bedel   bekleme  enuzun  bekleyen  bedel");
for (const n of [10, 20, 30, 40, 60, 80]) {
  const y = gun(n, TUR.yonetilen, uretec(TOHUM)), b = gun(n, TUR.barindirilan, uretec(TOHUM));
  console.log(String(n).padStart(7) + [dk(y.ortBekleme) + " dk", dk(y.enUzun) + " dk", `${y.bekleyen}/${n}`,
    y.maliyet.toFixed(1), dk(b.ortBekleme) + " dk", dk(b.enUzun) + " dk", `${b.bekleyen}/${n}`, b.maliyet.toFixed(1)]
    .map((h, i) => h.padStart(i === 4 ? 12 : 9)).join(""));
}

let esik = 0;
while (gun(++esik, TUR.yonetilen, uretec(TOHUM)).maliyet <= gun(esik, TUR.barindirilan, uretec(TOHUM)).maliyet);
const y = gun(esik, TUR.yonetilen, uretec(TOHUM)), b = gun(esik, TUR.barindirilan, uretec(TOHUM));
console.log(`\nesik: gunde ${esik} kosumda yonetilen ${y.maliyet.toFixed(1)} birime cikar, ` +
  `kendi barindirilan ${b.maliyet.toFixed(1)} birimde sabit kalir.`);
console.log(`kosum basina maliyet kalemi: yonetilen ${TUR.yonetilen.kalem} (calistirici-dakikasi), ` +
  `barindirilan ${TUR.barindirilan.kalem} (makine + bakim emegi)`);
console.log(`bakim kalemi: yonetilen ${TUR.yonetilen.bakim.length}, barindirilan ` +
  `${TUR.barindirilan.bakim.length} — ${TUR.barindirilan.bakim.join(", ")}`);

// Kapi hesabi: 05'te onbellek iki kusuru kacirmisti (K4, K6). Temiz baslayan calistirici
// her kapiyi her kosumda kosturur, o iki kusur kacmaz.
const dizi = 12, fark = TUR.yonetilen.sure - TUR.barindirilan.sure;
console.log(`\nonbellek isabeti: yonetilen %${TUR.yonetilen.isabet * 100}, barindirilan %${(TUR.barindirilan.isabet * 100).toFixed(1)}`);
console.log(`kapi hesabi (${dizi} kosumluk dizi): yonetilen 8/8 kusur, barindirilan 6/8 (K4, K6 bayat isabet)`);
console.log(`iki kusurun bedeli: kosum basina ${fark} s, dizide ${dizi * fark} s ` +
  `(${dk(dizi * fark)} dk); gunde 30 kosumda ${dk(30 * fark)} dk ve ` +
  `${(gun(30, TUR.yonetilen, uretec(TOHUM)).maliyet - gun(30, TUR.barindirilan, uretec(TOHUM)).maliyet).toFixed(1)} birim fark`);

// Yonetilen tur + uzak onbellek deposu: isabet geri gelir, aktarim suresi ve depo bedeli eklenir.
const AKTARIM = 24, DEPO = 3;
const uzak = { ...TUR.yonetilen, sure: TUR.barindirilan.sure + AKTARIM, sabitGunluk: DEPO };
console.log(`\nyonetilen tur + uzak onbellek deposu: kosum ${uzak.sure} s, gunde 30 kosumda ` +
  `${gun(30, uzak, uretec(TOHUM)).maliyet.toFixed(1)} birim — ama bayat isabet geri geldigi icin kusur 6/8`);
```

```
tohum 4831; yuva: yonetilen 8, barindirilan 2

              yonetilen (soguk, 203 s)        kendi barindirilan (sicak, 163 s)
kosum/gun  bekleme  enuzun  bekleyen  bedel   bekleme  enuzun  bekleyen  bedel
     10   0.0 dk   0.0 dk     0/10     14.2      0.1 dk   0.6 dk     1/10     27.6
     20   0.0 dk   0.0 dk     0/20     28.4      0.1 dk   1.1 dk     2/20     27.6
     30   0.0 dk   0.0 dk     0/30     42.6      1.0 dk   5.3 dk    12/30     27.6
     40   0.0 dk   0.0 dk     0/40     56.8      1.5 dk   8.0 dk    21/40     27.6
     60   0.0 dk   1.0 dk     2/60     85.3      4.3 dk  15.1 dk    44/60     27.6
     80   0.1 dk   1.0 dk     8/80    113.7      6.6 dk  20.9 dk    63/80     27.6

esik: gunde 20 kosumda yonetilen 28.4 birime cikar, kendi barindirilan 27.6 birimde sabit kalir.
kosum basina maliyet kalemi: yonetilen 1 (calistirici-dakikasi), barindirilan 2 (makine + bakim emegi)
bakim kalemi: yonetilen 0, barindirilan 5 — isletim sistemi yamasi, disk dolmasi, calistirici surumu, ag ve sertifika, kapasite/donanim

onbellek isabeti: yonetilen %0, barindirilan %47.9
kapi hesabi (12 kosumluk dizi): yonetilen 8/8 kusur, barindirilan 6/8 (K4, K6 bayat isabet)
iki kusurun bedeli: kosum basina 40 s, dizide 480 s (8.0 dk); gunde 30 kosumda 20.0 dk ve 15.0 birim fark

yonetilen tur + uzak onbellek deposu: kosum 187 s, gunde 30 kosumda 42.3 birim — ama bayat isabet geri geldigi icin kusur 6/8
```

#### Eşik ve Kuyruk

Para sütunu tek başına okunursa karar açık görünür: eşik **günde 20 koşumdur**. Altında yönetilen
tür ucuzdur — günde 10 koşumda 14,2 birime karşı 27,6 birim. Üstünde makas hızla açılır: günde 80
koşumda 113,7 birime karşı 27,6 birim, **4,1 kat**. Sabit bedel ödeyen taraf koşum sayısı arttıkça
kazanır, çünkü ödediği şey koşum değil zamandır.

Aynı sayılar koşum başına okunduğunda eğri görünür hâle gelir. Yönetilen türde koşum başına bedel
her yoğunlukta **1,42 birimdir** ve hiç değişmez — 14,2/10 ile 113,7/80 aynı sayıyı verir. Kendi
barındırılan türde koşum başına bedel 10 koşumda 2,76, 20 koşumda 1,38, 80 koşumda **0,35 birime**
iner. Bu, iki türün ekipte ürettiği davranışı da ayırır: sabit bedelde bir koşum daha koşturmak
bedavaya yakındır ve kimse koşum saymaz; koşum başına ödemede her ek koşumun bir fiyatı vardır ve
gereksiz koşumlar hesapta görünür. Aynı ölçü sonraki derslerde tetikleyici tasarımına da girer.

Kuyruk sütunu kararı geri çevirir. İki yuvalı kendi barındırılan tür günde 30 koşumda tıkanmaya
başlar: **30 koşumdan 12'si bekler**, ortalama bekleme 1 dakika, en uzun bekleme 5,3 dakika.
Günde 80 koşumda **63 koşum bekler**, ortalama 6,6 dakika ve en uzun bekleme 20,9 dakikadır. Sekiz
yuvalı yönetilen tür aynı gelişlerde 80 koşumun yalnız 8'ini bekletir ve en uzun bekleme 1
dakikadır. Beklemenin toplamı çarpıcıdır: 80 koşumda 6,6 × 80 = **528 dakika**, yani bir iş
gününden fazla bekleme, yönetilen türde toplam 5 dakikaya karşı.

Kuyruğun günlük ortalamadan doğmadığı ayrıca sayılır. Günde 30 koşumda toplam iş 30 × 163 = 4 890
saniyedir; iki yuva dokuz saatlik bir günde 64 800 saniyelik kapasite sunar, yani doluluk yüzde
8'dir. Buna karşın 12 koşum bekler. Sebep gelişlerin şeklidir: koşumlar üç yığılmada, her biri
yirmi dakikalık bir aralıkta geliyor ve yığılmanın ortasında iki yuva birden dolu. Kapasite
kararı **günlük toplama** göre verilirse kuyruk hiç görünmez; kararın bakması gereken şey en
yoğun yirmi dakikadır.

Bu bekleme kapı hesabına doğrudan girer. Önceki dersin ölçtüğü "kapının eklediği süre" yalnız hat
süresiydi; kuyruk onun önüne eklenir. Günde 80 koşumda kendi barındırılan türde bir değişikliğin
geri bildirimi ortalama 163 + 396 saniyeyi bulur — koşum süresinin **iki buçuk katı bekleme**.
Hattı 40 saniye kısaltmak için ödenen çaba, kuyrukta kaybedilenin yanında görünmez hâle gelir.

Kuyruk para ile kapatılabilir ve hesabı basittir. İki makine yerine dört makine tutulursa sabit
bedel 4 × 11 + 5,6 = **49,6 birime** çıkar, yani günde 80 koşumdaki 113,7 birimin hâlâ yarısından
azdır. Eşik böylece tek sayı olmaktan çıkar: karar, koşum sayısı ile kabul edilebilir bekleme
süresinin kesişiminde durur.

#### Temiz Başlamanın Kapı Değeri

Önbellek sütunu iki türü ayıran asıl yerdir. Kendi barındırılan çalıştırıcı önceki dersin ölçtüğü
**yüzde 47,9** isabeti korur; yönetilen çalıştırıcı her koşumda temiz başlar ve isabeti
**sıfırdır**. Koşum başına 40 saniyelik fark buradan gelir.

Kapı hesabı bu farkın öteki yüzünü gösterir. Önceki derste iki kusur (K4, K6) kaçmıştı, çünkü
bildirilmemiş bir girdi değiştiğinde ilgili kapı önbellekten gelip hiç koşmamıştı. Önbelleği
olmayan bir çalıştırıcıda böyle bir şey olamaz: her kapı her koşumda koşar ve on iki koşumluk
dizide **sekiz kusurun sekizi** yakalanır. İki kusurun bedeli koşum başına 40 saniye, dizide 480
saniye, günde 30 koşumda 20 dakika ve 15 birimdir.

Ara yol denenebilir: yönetilen tür, önbelleği uzak bir depodan geri yükleyerek koşabilir. Ölçüm bu
seçeneği de sayar — koşum 187 saniyeye iner, günde 30 koşumda 42,3 birim eder. Kazanç ilan
edildiği kadar değildir: 203 saniyeden 187 saniyeye inen sürenin karşılığında aktarım süresi ve
depo bedeli ödenir, günlük bedel 42,6'dan 42,3'e düşer. Daha önemlisi, **bayat isabet geri
gelir** — kusur sayımı 8/8'den 6/8'e döner. Yönetilen türün iki kusurluk üstünlüğü, hızından değil
tam olarak **önbelleksizliğinden** doğuyordu.

Bu üç sütun bir araya gelince karar kuralı tek cümleye inmez, ama sıraya girer. Koşum sayısı
eşiğin altındaysa yönetilen tür hem ucuz hem de kapı hesabında üstündür; seçim tartışmasızdır.
Eşiğin belirgin üstündeyse para kendi barındırılan türü gösterir, ama iki kalem geri ödenmelidir:
kuyruğu kapatacak ek yuva ve bayat isabetin kaçırdığı kusur. İkincisi para ile kapatılmaz;
kapatılma yolu önceki dersin gösterdiği yerdedir — kapıların girdilerini eksiksiz bildirmek. On iki
koşumluk dizide bunun bedeli 54 saniyeydi; aynı iki kusuru çalıştırıcı türünü değiştirerek
yakalamanın bedeli 480 saniye. **Aynı kusur, dokuz kat pahalı çözüm.**

Ölçünün göz ardı ettikleri adıyla yazılır. Yönetilen tarafta: eşzamanlılık üst sınırının aşıldığı
anlar, veri aktarım ücreti, sağlayıcı kesintisinde hattın tümüyle durması, hattın gizli
değerlerinin dışarıda çözülmesi ve çalıştırıcı sürümü değiştiğinde hat tanımının uyarlanması. Kendi
barındırılan tarafta: ilk kurulum emeği, yedek makine, arıza günü, güvenlik yamasının gecikmesi,
boşta duran kapasitenin elektrik ve alan bedeli, kapasite planlama hatası. İki tarafta da sayılmayan
iki kalem vardır: kuyrukta bekleyen kişinin bağlam kaybı ve başarısız biten koşumun yeniden
koşturulması. Beş bakım kalemi listelenmiştir ama emek olarak yalnız ayda altı saat üzerinden
fiyatlanmıştır; bakımın atlandığı bir ay bu sütunda hiç görünmez.

#### Özet

- Eşik günde **20 koşumdur**: altında yönetilen tür ucuz (10 koşumda 14,2'ye karşı 27,6 birim),
  üstünde kendi barındırılan tür ucuzdur (80 koşumda 27,6'ya karşı 113,7 birim, 4,1 kat).
- Eşik tek başına karar vermez. İki yuvalı kendi barındırılan tür günde 30 koşumda 12 koşumu
  bekletir; 80 koşumda 63 koşum bekler, ortalama 6,6 ve en uzun 20,9 dakika. Aynı gelişlerde sekiz
  yuvalı yönetilen tür 8 koşumu 1 dakikaya kadar bekletir.
- Kuyruk kapının eklediği sürenin önüne yazılır: günde 80 koşumda bekleme, 163 saniyelik koşumun
  iki buçuk katıdır. Dört makineye çıkmak sabit bedeli 49,6 birime taşır ve kuyruğu parayla
  kapatır.
- Maliyet kalemleri farklı eksendedir: yönetilen türde bir kalem (çalıştırıcı-dakikası, koşum
  başına), kendi barındırılan türde iki kalem (makine ve bakım emeği, zaman başına) ile beş bakım
  kalemi.
- Temiz başlayan çalıştırıcı önbellek isabetini sıfırlar (yüzde 47,9'a karşı 0) ve on iki koşumluk
  dizide 8/8 kusur yakalar; kendi barındırılan tür 6/8'de kalır. İki kusurun bedeli koşum başına
  40 saniye, günde 30 koşumda 20 dakika ve 15 birimdir.
- Uzak önbellek deposu ara yolu süreyi 187 saniyeye indirir ama bayat isabeti geri getirir ve kusur
  sayımı 6/8'e döner; yönetilen türün üstünlüğü hızından değil önbelleksizliğinden gelir.

#### Sonraki Adım

Bu ölçümde hat hep aynı hattı — sekiz aşama, aynı bağımlılıklar, aynı kapılar — koştu. Bir soru
sorulmadı: bu tanım nerede duruyor ve onu kim değiştiriyor? Yuva sayısını ikiden dörde çıkarmak,
bir kapıyı kaldırmak ya da tümleşim sınamasını bölmek hattın **tanımına** dokunmaktır. Tanım bir
arayüzde tıklanarak duruyorsa değişikliğin kimden geldiği, ne zaman ve neden yapıldığı hiçbir yerde
yazılı değildir; hat bozulduğunda geri dönülecek bir önceki hâl de yoktur. Tanım depoda bir dosya
olarak duruyorsa bunların hepsi vardır — ama beş servis için beş dosya, beş kez tekrarlanan aynı
satırlar demektir. Sonraki ders şemayı kendi tanımlar ve iki biçimi sayar: değişikliğin
izlenebilirliği, gözden geçirmeden geçen hat değişikliği oranı, hattın kendisi kırıldığında geri
alma adımı ve tanımın çoğaltılmasının satır bedeli.

### Hat Kodu Olarak Yapılandırma

Source: https://academia.sh/tr/kurslar/ci-cd/hat-kodu-olarak-yapilandirma

Aynı hat iki biçimde tanımlanır — arayüzden elle ve depoda dosya olarak: şema ders içinde tanımlanıp altı hatalı değişiklik gerçek bir doğrulayıcıdan geçirilir, izlenebilirlik, gözden geçirmeden geçen değişiklik oranı, geri alma adımı ve beş servis için tanımın çoğaltılması sayılır.

Önceki iki ders hattın süresini kısalttı ve nerede koşacağına karar verdi. İkisi de aynı nesneye
dokundu ama o nesnenin kendisi hiç sorgulanmadı: sekiz aşama, aralarındaki bağımlılıklar, hangi
aşamanın kapı olduğu ve kaç yuva kullanıldığı bir yerde **yazılıdır**. Yuva sayısını ikiden dörde
çıkarmak da, bir kapıyı kaldırmak da o yazılı şeye dokunmaktır.

Nerede yazılı olduğu iki seçenektir. Hat bir **arayüzden elle** yapılandırılabilir: alanlar
doldurulur, kaydedilir, o an yürürlüğe girer. Ya da **depoda bir dosya olarak** durur: değişiklik
bir işleme olarak gelir, gözden geçirilir, sürümlenir. Bu ders iki biçimi dört sayıda karşılaştırır
— izlenebilirlik, gözden geçirmeden geçen değişiklik oranı, geri alma adımı ve tanımın beş servise
çoğaltılması.

**HT37.** Şema, doğrulayıcı ve dosya biçimi bu derste tanımlanmıştır; `hat`, `yuva`, `asama`,
`komut`, `onceki`, `kapi` alan adları gerçek bir aracın alanları değildir. **HT38.** Yirmi hat
değişikliği kurgu ölçüm ağının bir çeyreğini temsil eder; altısı hatalıdır. **HT39.** Arayüz
biçiminin seçim listesi yapı gereği üç hata türünü engeller — bilinmeyen alan yazılamaz, olmayan
bir aşamaya bağlanılamaz, sayı alanına metin girilemez; buna karşılık arayüzde gözden geçirme adımı
yoktur. **HT40.** Depo biçiminde 3, 11 ve 17 numaralı değişiklikler acil gerekçesiyle gözden
geçirmeden geçmiştir. **HT41.** Gözden geçirme beklemesi 42 dakika, şema doğrulaması 4 saniyedir;
geri alma adımları sayılır, dakikaları kurgudur. **HT42.** Satır sayıları gerçekten üretilen
metinden sayılır.

#### Şemanın Kendisi

Bir hat tanımı dosya olarak durduğunda kazanılan ilk şey şudur: tanım artık **veridir** ve veri
okunabilir. Şema alanları ve türlerini bildirir; doğrulayıcı şemayı okuyup tanımı denetler.

```js
// olcum-agi/sema.mjs — hat tanimi semasi, dogrulayici ve yazici (alan adlari bu derste tanimlandi)
export const SEMA = { hat: "metin", yuva: "sayi", asamalar: "dizi" };
const ASAMA = { ad: "metin", komut: "metin", onceki: "dizi", kapi: "eh" };
const tip = { metin: (d) => typeof d === "string", sayi: (d) => Number.isInteger(d),
  dizi: (d) => Array.isArray(d), eh: (d) => d === "evet" || d === "hayir" };
const alanDenetle = (nesne, sema, on, h) => {
  for (const a of Object.keys(nesne)) if (!sema[a]) h.push(`bilinmeyen alan: ${on}${a}`);
  for (const [a, tp] of Object.entries(sema))
    if (!(a in nesne)) h.push(`eksik alan: ${on}${a}`);
    else if (!tip[tp](nesne[a])) h.push(`tur hatasi: ${on}${a}, ${tp} bekleniyor`);
};

export const dogrula = (t) => {
  const h = [];
  alanDenetle(t, SEMA, "", h);
  if (!Array.isArray(t.asamalar)) return h;
  const adlar = t.asamalar.map((s) => s.ad);
  for (const s of t.asamalar) {
    alanDenetle(s, ASAMA, `${s.ad}.`, h);
    for (const o of s.onceki || []) if (!adlar.includes(o)) h.push(`bilinmeyen onceki: ${s.ad} -> ${o}`);
  }
  const bak = (ad, iz) => (iz.includes(ad) ? true
    : (t.asamalar.find((s) => s.ad === ad)?.onceki || []).some((o) => bak(o, [...iz, ad])));
  for (const s of t.asamalar) if (bak(s.ad, [])) { h.push(`dongu: ${s.ad}`); break; }
  return h;
};

export const TABAN = { hat: "okuma-toplayici", yuva: 3, asamalar: [
  ["bagimlilik-cozumu", "coz", [], "hayir"], ["derleme", "derle", ["bagimlilik-cozumu"], "hayir"],
  ["statik-cozumleme", "coz-statik", ["bagimlilik-cozumu"], "evet"], ["birim-sinamasi", "sina-birim", ["derleme"], "evet"],
  ["tumlesim-sinamasi", "sina-tumlesim", ["derleme"], "evet"], ["paketleme", "paketle", ["derleme"], "hayir"],
  ["sozlesme-sinamasi", "sina-sozlesme", ["paketleme"], "evet"], ["imaj-uretimi", "uret-imaj", ["paketleme"], "hayir"],
].map(([ad, komut, onceki, kapi]) => ({ ad, komut, onceki, kapi })) };

// Tanimin dosya bicimi: her asama uc ya da dort satir.
export const yaz = (t) => [`hat ${t.hat}`, `yuva ${t.yuva}`, ...t.asamalar.flatMap((s) => [`asama ${s.ad}`,
  `  komut ${s.komut}`, ...(s.onceki.length ? [`  onceki ${s.onceki.join(" ")}`] : []), `  kapi ${s.kapi}`])];

// Kurgu olcum aginin bes servisi: taban hat + servise ozgu farklar.
export const SERVISLER = [
  { ad: "okuma-toplayici", fark: [] }, { ad: "dogrulayici", fark: [["yuva", 2]] },
  { ad: "faturalama", fark: [["sil", "sozlesme-sinamasi"]] },
  { ad: "is-emri", fark: [["ekle", { ad: "gecikme-sinamasi", komut: "sina-gecikme", onceki: ["paketleme"], kapi: "evet" }]] },
  { ad: "gecelik-toplu-is", fark: [["yuva", 4], ["sil", "statik-cozumleme"]] },
];
export const uygula = (s) => {
  const t = { ...TABAN, hat: s.ad, asamalar: TABAN.asamalar.map((a) => ({ ...a })) };
  for (const [tur, d] of s.fark) {
    if (tur === "yuva") t.yuva = d;
    if (tur === "sil") t.asamalar = t.asamalar.filter((a) => a.ad !== d);
    if (tur === "ekle") t.asamalar.push(d);
  }
  return t;
};
```

Doğrulayıcı dört şey denetler: bilinmeyen ve eksik alan, tür uyuşmazlığı, olmayan bir aşamaya
yapılan `onceki` atfı ve bağımlılıklarda döngü. Dördü de tanımın **metninden** kararlaştırılır;
hattı koşturmak gerekmez.

#### İki Biçim

Altı hatalı değişiklik gerçekten kurulur, gerçekten doğrulayıcıdan geçirilir ve her biri için iki
biçimin tuttuğu ayrı ayrı yazılır.

```js
// olcum-agi/bicimler.mjs — ayni hat iki bicimde: izlenebilirlik, gozden gecirme, kapi, geri alma
import { dogrula, TABAN } from "./sema.mjs";

const kopya = () => ({ ...TABAN, asamalar: TABAN.asamalar.map((a) => ({ ...a })) });
const N = 20, GECEN = [3, 11, 17]; // gozden gecirmeden gecen uc hat degisikligi
const IZ = ["kim", "ne zaman", "neden", "ne degisti"];

// Alti hatali hat degisikligi. no: kacinci degisiklikte geldigi. arayuz: bicim geregi arayuzde
// yazilamayan hata (secim listesi engeller). gozden: yalniz insanin gorebilecegi hata.
const HATALAR = [
  ["H1 bilinmeyen alan", 2, 1, 0, (t) => { t.asama = t.asamalar; delete t.asamalar; }],
  ["H2 dongu", 5, 0, 1, (t) => { t.asamalar[1].onceki = ["paketleme"]; }],
  ["H3 bilinmeyen onceki", 8, 1, 0, (t) => { t.asamalar[3].onceki = ["derleme-eski"]; }],
  ["H4 tur hatasi (yuva)", 11, 1, 0, (t) => { t.yuva = "uc"; }],
  ["H5 kapi kaldirildi", 14, 0, 1, (t) => { t.asamalar.splice(2, 1); }],
  ["H6 kapi devre disi", 17, 0, 1, (t) => { t.asamalar[4].kapi = "hayir"; }],
];

const s = { arayuz: 0, depo: 0, sema: 0, yalniz: 0 };
console.log("hata                  no  sema dogrulayicisinin dedigi                arayuz  depo");
for (const [ad, no, arayuz, gozden, boz] of HATALAR) {
  const t = kopya(); boz(t);
  const bulgu = dogrula(t), sema = bulgu.length > 0;
  const depo = sema || (gozden && !GECEN.includes(no)); // gecen degisiklige insan bakmaz
  s.sema += sema; s.arayuz += arayuz; s.depo += depo; s.yalniz += depo && !sema;
  console.log(ad.padEnd(22) + String(no).padStart(2) + "  " + (bulgu[0] || "— (sema gecti)").slice(0, 42).padEnd(44) +
    (arayuz ? "tutar" : "kacar").padEnd(8) + (depo ? "tutar" : "kacar"));
}

const BICIM = {
  arayuz: { iz: IZ.slice(0, 2), gecen: N, dakika: 35,
    geri: ["degisikligi bul", "eski degeri hatirla", "elle geri yaz", "kaydet", "elle tetikle", "dogrula"] },
  depo: { iz: IZ, gecen: GECEN.length, dakika: 4, geri: ["onceki surume don", "hat kendini kossun"] },
};
console.log(`\nizlenebilirlik (${N} degisiklik x ${IZ.length} alan):`);
for (const [ad, b] of Object.entries(BICIM))
  console.log(`  ${ad.padEnd(8)}${b.iz.length * N}/${IZ.length * N} alan — ${b.iz.join(", ")}`);
console.log(`gozden gecirmeden gecen: arayuz ${N}/${N} (%100), depo ${GECEN.length}/${N} (%${(GECEN.length * 100) / N})`);
console.log(`kapi hesabi (6 hata): arayuz ${s.arayuz}/6, depo ${s.depo}/6 (sema ${s.sema}, yalniz gozden gecirme ${s.yalniz})`);
console.log("\nhat kirildiginda geri alma:");
for (const [ad, b] of Object.entries(BICIM))
  console.log(`  ${ad.padEnd(8)}${b.geri.length} adim, ${b.dakika} dk — ${b.geri.join(" / ")}`);
console.log(`\nkapinin ekledigi sure: sema 4 s x ${N} = ${4 * N} s; gozden gecirme 42 dk x ${N - GECEN.length} = ${42 * (N - GECEN.length)} dk`);
```

```
hata                  no  sema dogrulayicisinin dedigi                arayuz  depo
H1 bilinmeyen alan     2  bilinmeyen alan: asama                      tutar   tutar
H2 dongu               5  dongu: derleme                              kacar   tutar
H3 bilinmeyen onceki   8  bilinmeyen onceki: birim-sinamasi -> derle  tutar   tutar
H4 tur hatasi (yuva)  11  tur hatasi: yuva, sayi bekleniyor           tutar   tutar
H5 kapi kaldirildi    14  — (sema gecti)                              kacar   tutar
H6 kapi devre disi    17  — (sema gecti)                              kacar   kacar

izlenebilirlik (20 degisiklik x 4 alan):
  arayuz  40/80 alan — kim, ne zaman
  depo    80/80 alan — kim, ne zaman, neden, ne degisti
gozden gecirmeden gecen: arayuz 20/20 (%100), depo 3/20 (%15)
kapi hesabi (6 hata): arayuz 3/6, depo 5/6 (sema 4, yalniz gozden gecirme 1)

hat kirildiginda geri alma:
  arayuz  6 adim, 35 dk — degisikligi bul / eski degeri hatirla / elle geri yaz / kaydet / elle tetikle / dogrula
  depo    2 adim, 4 dk — onceki surume don / hat kendini kossun

kapinin ekledigi sure: sema 4 s x 20 = 80 s; gozden gecirme 42 dk x 17 = 714 dk
```

#### Kapının Hesabı

Tablo beklenmedik bir yerden başlar: arayüz biçimi üç hatayı **yapı gereği** tutar. Bilinmeyen bir
alan yazılamaz çünkü alan listesi sabittir; olmayan bir aşamaya bağlanılamaz çünkü seçim listeden
yapılır; sayı alanına metin girilemez. Depo biçiminde bu üç hata **yazılabilir** ve ancak şema
doğrulayıcısı koşarsa yakalanır. Doğrulayıcı olmadan depo biçimi bu üç hatada arayüzden
**kötüdür**.

Doğrulayıcı koştuğunda tablo döner. Şema dört hatayı tutar; dördüncüsü **döngüdür** ve arayüzün
göremediği hatadır: `derleme` aşaması `paketleme`ye bağlanınca çizge kendine kapanır, ama her alan
tek tek geçerlidir. Bir arayüz alan denetler, **çizge** denetlemez.

Geriye iki hata kalır ve ikisi de kapının kendisine dokunur: H5 statik çözümleme aşamasını siler,
H6 tümleşim sınamasının `kapi` alanını `hayir` yapar. İkisi de şemaya göre kusursuz tanımlardır.
Yalnız bir insan "bu değişiklik neden bir kapıyı kaldırıyor" diye sorabilir. Depo biçimi H5'i
tutar; H6 kaçar, çünkü 17 numaralı değişiklik gözden geçirmeden geçen üçten biridir.

Toplam **arayüz 3/6, depo 5/6**. Kapının marjinal değeri ise ayrı okunur: şema dört hata tutar ve
20 değişiklikte toplam 80 saniye ekler. Gözden geçirme **yalnız bir hatayı** tek başına tutar ve
17 değişiklikte **714 dakika** bekletir. Bir kusur için on iki saat — bu kursun ölçüsüne göre
savunulması en zor kapı budur. Savunması tek bir yerdedir: tuttuğu hata, hattın **bir kapısını
kaldıran** değişikliktir; bu kursun bütün ölçümlerinin dayandığı şeyi sessizce silen türden. Buna
karşılık aynı hatayı çok daha ucuza tutmanın yolu görünüyor — şemaya "kapı sayısı azalamaz"
kuralını eklemek. Bu ders o kuralı ölçmez; ama ölçseydi gözden geçirmenin marjinal değeri sıfıra
inerdi ve 714 dakikalık kapı kaldırılabilir hâle gelirdi.

İzlenebilirlik iki alanda ayrılır. Arayüz **80 alanın 40'ını** kaydeder — kim ve ne zaman. Eksik
olan ikisi "neden" ve "ne değişti"dir, yani hat gece bozulduğunda sorulacak tam olarak iki soru.
Geri alma tablosu aynı boşluğun ikinci faturasıdır: arayüzde geri almanın ilk adımı "değişikliği
bul"dur ve cevabı hiçbir yerde yazılı değildir — altı adım, 35 dakika. Depoda önceki sürüme dönmek
iki adım ve 4 dakikadır, çünkü "ne değişti" zaten kayıtlıdır.

#### Beş Servis

Kurgu ölçüm ağının beş servisi neredeyse aynı hattı kullanır. Tanım her servise kopyalanabilir ya
da ortak parça ayrılıp farklar bildirilebilir. İki biçimin satırları gerçekten üretilip sayılır.

```js
// olcum-agi/cogaltma.mjs — bes servis: tam kopya ile ortak parca ayrilmis bicim
import { SERVISLER, uygula, yaz, TABAN } from "./sema.mjs";

const tam = SERVISLER.map((s) => ({ ad: s.ad, satir: yaz(uygula(s)) }));
const toplamTam = tam.reduce((t, s) => t + s.satir.length, 0);

// Ortak parca: hat adi disindaki her sey. Servis dosyasi: kalitim + ad + yalniz farklar.
const ortak = yaz(TABAN).filter((l) => !l.startsWith("hat "));
const ayrik = SERVISLER.map((s) => ({
  ad: s.ad,
  satir: ["kalitim ortak", `hat ${s.ad}`, ...s.fark.flatMap(([tur, d]) => tur === "yuva" ? [`yuva ${d}`]
    : tur === "sil" ? [`asama-sil ${d}`]
      : [`asama ${d.ad}`, `  komut ${d.komut}`, `  onceki ${d.onceki.join(" ")}`, `  kapi ${d.kapi}`])],
}));
const toplamAyrik = ortak.length + ayrik.reduce((t, s) => t + s.satir.length, 0);

// Bes dosyanin hepsinde birebir gecen satirlar (tekrar sayisiyla birlikte).
const say = (a) => a.reduce((m, l) => m.set(l, (m.get(l) || 0) + 1), new Map());
const sayimlar = tam.map((s) => say(s.satir));
let hepsinde = 0;
for (const [l, c] of sayimlar[0]) hepsinde += Math.min(c, ...sayimlar.slice(1).map((m) => m.get(l) || 0));

console.log("servis              tam kopya  ortak parca ile");
tam.forEach((s, i) => console.log(s.ad.padEnd(20) + String(s.satir.length).padStart(6) +
  String(ayrik[i].satir.length).padStart(12)));
console.log("ortak dosya".padEnd(20) + "     —" + String(ortak.length).padStart(12));
console.log("toplam".padEnd(20) + String(toplamTam).padStart(6) + String(toplamAyrik).padStart(12) +
  `   (%${(((toplamTam - toplamAyrik) * 100) / toplamTam).toFixed(1)} az satir)`);
console.log(`\nbes dosyanin hepsinde birebir gecen satir: ${hepsinde} — tam kopyada ` +
  `${hepsinde * 5} satirin ${hepsinde * 4}'u tekrardir`);

console.log("\nokunabilirlik bedeli");
console.log(`  bir servisin hattini okumak: tam kopya 1 dosya ${tam[1].satir.length} satir; ` +
  `ortak parca ile 2 dosya ${ortak.length + ayrik[1].satir.length} satir`);
console.log(`  ortak dosyada tek satir degisince etkilenen servis: ${SERVISLER.length}/${SERVISLER.length}`);
console.log(`  bes servise yeni bir kapi eklemek: tam kopya ${5 * 4} satir / 5 ayri degisiklik; ` +
  `ortak parca ile 4 satir / 1 degisiklik`);
```

```
servis              tam kopya  ortak parca ile
okuma-toplayici         33           2
dogrulayici             33           3
faturalama              29           3
is-emri                 37           6
gecelik-toplu-is        29           4
ortak dosya              —          32
toplam                 161          50   (%68.9 az satir)

bes dosyanin hepsinde birebir gecen satir: 24 — tam kopyada 120 satirin 96'u tekrardir

okunabilirlik bedeli
  bir servisin hattini okumak: tam kopya 1 dosya 33 satir; ortak parca ile 2 dosya 35 satir
  ortak dosyada tek satir degisince etkilenen servis: 5/5
  bes servise yeni bir kapi eklemek: tam kopya 20 satir / 5 ayri degisiklik; ortak parca ile 4 satir / 1 degisiklik
```

Kazanç büyüktür: **161 satır 50 satıra** iner, yüzde 68,9 azalma. Kaynağı da açıktır — beş dosyanın
hepsinde birebir aynı geçen **24 satır** vardır ve tam kopyada bu satırlar 120 kez yazılmıştır,
yani **96 satır tekrardır**.

Bedel satır sayısında görünmez. Bir servisin hattını okumak tam kopyada 33 satırlık **tek** dosya,
ortak parça ayrıldığında 35 satır ve **iki** dosya demektir. Fark iki satırdır ama o servisin
dosyasındaki `yuva 2` satırı tek başına hiçbir şey anlatmaz; neyin üstüne yazdığı ancak öteki
dosyada görünür ve etkin tanım hiçbir yerde bütün hâlde durmaz.

Asıl bedel etki alanıdır. Ortak dosyada değiştirilen tek satır **beş servisin beşini** birden
etkiler — ki bu aynı zamanda kazançtır: beş servise yeni bir kapı eklemek tam kopyada 20 satır ve
beş ayrı değişiklik, ortak parça ile 4 satır ve tek değişikliktir. Çoğaltma dağınıktır ama bir
servisi tek başına değiştirmeye izin verir; ortaklaştırma her değişikliği beş servisin ortak kararı
yapar. Beş hat gerçekten aynı kalmalıysa ortaklaştırma doğrudur; ayrışmaları bekleniyorsa 96
satırlık tekrar, ödenmiş bir bağımsızlık bedelidir.

#### Özet

- Tanım dosya olduğunda **veri** olur: şema alanları ve türleri bildirir, doğrulayıcı bilinmeyen
  alanı, tür hatasını, olmayan aşamaya atfı ve bağımlılık döngüsünü hattı koşturmadan yakalar.
- Arayüz biçimi üç hatayı **yapı gereği** tutar (alan listesi sabit, seçim listeden yapılır) ama
  çizgeyi denetlemez ve döngüyü kaçırır. Toplam: arayüz 3/6, depo 5/6.
- Kapının marjinal değeri: şema dört hata tutar ve 20 değişiklikte 80 saniye ekler; gözden geçirme
  yalnız bir hatayı tek başına tutar ve 17 değişiklikte 714 dakika bekletir. Tuttuğu hata bir kapıyı
  kaldıran değişikliktir; şemaya "kapı sayısı azalamaz" kuralı eklenirse bu kapı savunulamaz olur.
- İzlenebilirlik 40/80 alana karşı 80/80'dir. Arayüzde eksik olan iki alan "neden" ve "ne
  değişti"dir; geri almanın ilk adımı da tam olarak odur — 6 adım ve 35 dakikaya karşı 2 adım ve
  4 dakika.
- Beş servis için tam kopya 161, ortak parça ayrılmış biçim 50 satırdır (yüzde 68,9 az); beş
  dosyada birebir aynı geçen 24 satırın 96 tekrarı silinir. Bedeli okunabilirlik ve etki alanıdır:
  bir servisi okumak 2 dosya 35 satıra çıkar, ortak dosyadaki tek satır beş servisi birden etkiler.

#### Sonraki Adım

Hat artık tanımlı, doğrulanmış, sürümlenmiş ve geri alınabilir. Bir soru hâlâ dışarıda: bu hat
**ne zaman** koşuyor? Önceki derste günde 10 ile 80 arasında koşum sayısı verildi ve eşik o sayıdan
hesaplandı, ama sayının nereden geldiği hiç söylenmedi. Koşumu başlatan şey bir tetikleyicidir ve
tetikleyici de bir kapıdır: koşumun olup olmayacağına karar verir. Üç tür vardır — bir olaya
bağlanan, saate bağlanan ve elle çekilen — ve üçü aynı takvimde çok farklı sayılar üretir: kaç
koşum, kaçı hiçbir şey değişmemişken, değişiklikle koşum arasında ne kadar gecikme, kaç koşum
birbirinin üstüne biniyor. Sonraki ders üçünü aynı iki haftalık kurgu takvimde koşturur, sonra yol
tabanlı bir filtre ekleyip gereksiz koşumun düşüşünü ve filtrenin kaçırdığı gerçek değişikliği
birlikte sayar.

### Tetikleyiciler

Source: https://academia.sh/tr/kurslar/ci-cd/tetikleyiciler

Olay, zaman ve elle tetikleme aynı iki haftalık kurgu takvimde koşturulur: koşum sayısı, hiçbir şey değişmemişken ya da hattın çıktısını etkilemeyen bir değişiklikle yapılan gereksiz koşum, değişiklik ile koşum arasındaki gecikme ve çakışan koşum sayılır, ardından yol tabanlı filtre eklenip gereksiz koşumun düşüşü ile kaçırılan gerçek değişiklik birlikte ölçülür.

Hat tanımlı, doğrulanmış ve sürümlenmiş durumda. Önceki iki derste günde 10 ile 80 arasında koşum
sayıları kullanıldı ve eşikler o sayılardan hesaplandı, ama sayının nereden geldiği hiç sorulmadı.
Koşumu başlatan şey bir **tetikleyicidir** ve tetikleyici de bir kapıdır — tuttuğu şey koşumun
kendisidir. Koşmayan bir hat hiçbir kusur yakalamaz; gereksiz koşan bir hat yalnız para ve sıra
harcar.

Üç tür vardır: bir **olaya** bağlanan (depoya bir değişiklik geldiğinde), **saate** bağlanan (belirli
aralıklarla) ve **elle** çekilen (biri düğmeye bastığında). Üçü aynı takvimde çok farklı sayılar
üretir. Bu ders dördünü sayar: koşum sayısı, gereksiz koşum, gecikmiş geri bildirim ve çakışan
koşum — sonra yol tabanlı bir filtre ekleyip aynı sayıları yeniden okur.

**HT43.** Takvim kurgu ölçüm ağının iki haftasıdır ve `node` ile üretilir; üreteç kendi yazılmıştır,
tohum **9173**. On iş günü vardır, hafta sonunda değişiklik yoktur. **HT44.** Yol grupları ve
oranları modelde bildirilmiştir; `belgeler/` hattın çıktısını etkilemez, ötekiler etkiler.
**HT45.** Koşum süresi 203 saniyedir — önceki derslerde ölçülen kritik yol; çakışan koşum, önceki
koşum bitmeden başlayan koşumdur. **HT46.** Gecikme, değişiklik anı ile onu kapsayan ilk koşumun
**başlangıcı** arasındaki süredir; koşumun kendi süresi her tetikleyicide aynı olduğu için
gecikmeye katılmaz. **HT47.** Elle tetikleme her iş günü 17:30'da modellenir ve dörtte bir
olasılıkla atlanır. **HT48.** Yol filtresi elenen koşumda ilerlemez; kapsanmamış değişiklikler
sonraki koşuma devreder.

#### Üç Tetikleyici, Aynı Takvim

```js
// olcum-agi/takvim.mjs — iki haftalik kurgu degisiklik takvimi ve tetikleyiciler (model)
export const TOHUM = 9173;
export const uretec = (t) => () => ((t = (t * 48271) % 2147483647) / 2147483647);
export const GUN = 86400, KOSUM = 203; // 05'te olculen kritik yol suresi (saniye)

// Yol gruplari: etkili olan hattin ciktisini degistirir, etkisiz olan degistirmez.
export const YOLLAR = [["kaynak/", 45, 1], ["sinama/", 20, 1], ["belgeler/", 15, 0],
  ["yapilandirma/", 10, 1], ["ortak-kitaplik/", 10, 1]];
export const FILTRE = ["kaynak/", "sinama/", "yapilandirma/"]; // ortak-kitaplik/ listeye eklenmedi

export const takvim = () => {
  const rnd = uretec(TOHUM), d = [];
  for (let g = 0; g < 14; g++) {
    if (g % 7 >= 5) continue; // hafta sonu
    for (let i = 3 + Math.floor(rnd() * 6); i > 0; i--) {
      const p = rnd() * 100;
      let t = 0, yol = YOLLAR[YOLLAR.length - 1];
      for (const y of YOLLAR) { t += y[1]; if (p < t) { yol = y; break; } }
      d.push({ an: g * GUN + Math.round((9 + rnd() * 9) * 3600), yol: yol[0], etkili: yol[2] });
    }
  }
  return d.sort((a, b) => a.an - b.an);
};

export const TETIKLEYICI = {
  olay: (d) => d.map((x) => x.an),
  "zaman-4s": () => Array.from({ length: 84 }, (_, i) => Math.floor(i / 6) * GUN + (i % 6) * 4 * 3600),
  "zaman-gecelik": () => Array.from({ length: 14 }, (_, g) => g * GUN + 2 * 3600),
  elle: () => { const rnd = uretec(TOHUM + 1);
    return [...Array(14).keys()].filter((g) => g % 7 < 5 && rnd() > 0.25).map((g) => g * GUN + 17.5 * 3600); },
};

// Bos kosum: onceki kosumdan beri hic degisiklik yok. Ilgisiz kosum: kapsadigi degisikliklerin
// hicbiri hattin ciktisini etkilemiyor. Cakisan: onceki kosum bitmeden baslayan kosum.
export const olc = (kosum, degisiklik) => {
  let bos = 0, ilgisiz = 0, cakisan = 0, onceki = -Infinity;
  kosum.forEach((k, i) => {
    const kapsanan = degisiklik.filter((d) => d.an > onceki && d.an <= k);
    if (!kapsanan.length) bos++;
    else if (!kapsanan.some((d) => d.etkili)) ilgisiz++;
    if (i > 0 && k - kosum[i - 1] < KOSUM) cakisan++;
    onceki = k;
  });
  const gecikme = [];
  let kapsanmayan = 0;
  for (const d of degisiklik.filter((x) => x.etkili)) {
    const k = kosum.find((t) => t >= d.an);
    if (k === undefined) kapsanmayan++; else gecikme.push(k - d.an);
  }
  return { kosum: kosum.length, bos, ilgisiz, cakisan, kapsanmayan,
    ort: gecikme.reduce((a, b) => a + b, 0) / (gecikme.length || 1), enUzun: Math.max(0, ...gecikme) };
};

// Yol tabanli filtre: kosum, ancak kapsadigi degisikliklerden biri listeye uyarsa yapilir.
// Elenen kosumda "onceki" ilerlemez; kapsanmamis degisiklikler sonraki kosuma devreder.
export const filtrele = (kosum, degisiklik, liste = FILTRE) => {
  const tut = [];
  let onceki = -Infinity;
  for (const k of kosum)
    if (degisiklik.some((d) => d.an > onceki && d.an <= k && liste.includes(d.yol))) { tut.push(k); onceki = k; }
  return tut;
};
```

```js
// olcum-agi/tetikleyici.mjs — uc tetikleyici turu ayni iki haftalik takvimde
import { takvim, TETIKLEYICI, olc, YOLLAR, TOHUM } from "./takvim.mjs";

const d = takvim();
const sa = (s) => (s / 3600).toFixed(1);
const dagilim = YOLLAR.map(([y]) => `${y}${d.filter((x) => x.yol === y).length}`).join("  ");
console.log(`tohum ${TOHUM}; iki haftada ${d.length} degisiklik, ${d.filter((x) => x.etkili).length} etkili`);
console.log(`yol dagilimi: ${dagilim}`);

console.log("\ntetikleyici      kosum  bos  ilgisiz  gereksiz  cakisan  ort gecikme  en uzun  kapsanmayan");
const sonuc = {};
for (const [ad, uret] of Object.entries(TETIKLEYICI)) {
  const s = (sonuc[ad] = olc(uret(d), d));
  console.log(ad.padEnd(16) + String(s.kosum).padStart(5) + String(s.bos).padStart(5) +
    String(s.ilgisiz).padStart(9) + String(s.bos + s.ilgisiz).padStart(10) + String(s.cakisan).padStart(9) +
    (sa(s.ort) + " sa").padStart(13) + (sa(s.enUzun) + " sa").padStart(9) + String(s.kapsanmayan).padStart(13));
}

// Olay tetikleyicisi zaten varken zaman tetikleyicisinin ek katkisi.
const olay = TETIKLEYICI.olay(d);
for (const ad of ["zaman-4s", "zaman-gecelik"]) {
  const birlikte = [...olay, ...TETIKLEYICI[ad]()].sort((a, b) => a - b);
  const s = olc(birlikte, d);
  console.log(`\nolay + ${ad}: ${s.kosum} kosum (olay tek basina ${sonuc.olay.kosum}), ` +
    `bos ${s.bos}, ort gecikme ${sa(s.ort)} sa — olay tetikleyicisinin gecikmesi ${sa(sonuc.olay.ort)} sa`);
  console.log(`  eklenen ${s.kosum - sonuc.olay.kosum} kosumun ${s.bos} tanesi bos; ` +
    `gecikmeye katkisi ${(sonuc.olay.ort - s.ort).toFixed(1)} saniye`);
}
```

```
tohum 9173; iki haftada 53 degisiklik, 46 etkili
yol dagilimi: kaynak/21  sinama/12  belgeler/7  yapilandirma/3  ortak-kitaplik/10

tetikleyici      kosum  bos  ilgisiz  gereksiz  cakisan  ort gecikme  en uzun  kapsanmayan
olay               53    0        7         7        2       0.0 sa   0.0 sa            0
zaman-4s           84   58        1        59        0       2.0 sa   3.9 sa            0
zaman-gecelik      14    4        0         4        0      12.4 sa  16.8 sa            0
elle                6    0        0         0        0      22.2 sa 103.1 sa            0

olay + zaman-4s: 137 kosum (olay tek basina 53), bos 84, ort gecikme 0.0 sa — olay tetikleyicisinin gecikmesi 0.0 sa
  eklenen 84 kosumun 84 tanesi bos; gecikmeye katkisi 0.0 saniye

olay + zaman-gecelik: 67 kosum (olay tek basina 53), bos 14, ort gecikme 0.0 sa — olay tetikleyicisinin gecikmesi 0.0 sa
  eklenen 14 kosumun 14 tanesi bos; gecikmeye katkisi 0.0 saniye
```

#### Dört Sayının Okunması

Olay tetikleyicisi 53 koşum yapar ve gecikmesi **sıfırdır**: her değişiklik kendi koşumunu doğurur.
Gereksiz koşumu 7'dir ve hepsi aynı türdendir — yalnız `belgeler/` altına dokunan değişiklikler,
hattın çıktısını değiştirmediği hâlde tam bir koşum ödetir. Çakışan koşumu 2'dir: iki değişiklik
203 saniyeden yakın aralıkla gelmiş ve ikinci koşum, birincisi bitmeden başlamıştır. Çakışma
yalnız yuva harcamaz; önce başlayan koşum bittiğinde ürettiği sonuç zaten eski ağaca aittir.

Dört saatlik zaman tetikleyicisi **her iki eksende birden** kötüdür. 84 koşum yapar — olay
tetikleyicisinden yüzde 58 fazla — ve bunların **58'i tamamen boştur**, yani önceki koşumdan beri
hiçbir değişiklik gelmemiştir. Gereksiz koşum oranı 59/84, yani yüzde 70. Buna karşılık ortalama
gecikme sıfır değil **2 saattir**, en uzun bekleme 3,9 saat. Daha çok koşuyor ve daha geç haber
veriyor.

Elle tetikleme tablonun en aldatıcı satırıdır. Gereksiz koşumu **sıfırdır** ve koşum sayısı
yalnızca 6'dır; para sütununda açık ara kazanır. Kararı gecikme sütunu verir: ortalama 22,2 saat,
en uzun **103,1 saat** — dört günü aşkın. Gereksiz koşumunun sıfır olması da erdem değildir:
koşumlar o kadar seyrektir ki her biri onlarca değişikliği birden kapsar, dolayısıyla hiçbiri boşa
düşmez. Aynı seyreklik, bir koşum kırmızı yandığında **hangi değişikliğin kırdığını** belirsiz
bırakır.

#### Kaldırılabilir Kapı

Tetikleyiciler birlikte de kullanılabilir ve uygulamada en sık görülen bileşim olay artı zamandır.
Ölçüm bu bileşimin ne kattığını sorar. Olay tetikleyicisinin yanına dört saatlik zaman
tetikleyicisi eklendiğinde koşum sayısı 53'ten **137'ye** çıkar; eklenen **84 koşumun 84'ü de
boştur**, çünkü her değişiklik zaten kendi koşumunu tetiklemiştir. Gecikmeye katkısı **0,0
saniyedir**.

Bu kursun ölçüsüne göre karar açıktır: bir kapı yalnız kendi yakaladığı kadar değerlidir ve bu
kapının yalnız kendisine ait yakalaması yoktur. Dört saatlik zaman tetikleyicisi, olay
tetikleyicisi varken **kaldırılması gereken kapıdır** — 84 boş koşum, sıfır kazanç.

Gecelik koşum aynı ölçüde 14 koşum ekler ve o da bu takvimde sıfır katkı gösterir. Kaldırılıp
kaldırılmayacağı burada ölçülemez ve ölçülemediği yazılır: bu takvim yalnız **depoya gelen**
değişiklikleri modelliyor. Gecelik koşumun savunması depo dışındaki değişikliktir — kurulacak
bağımlılığın içeriğinin liste dosyası değişmeden büyümesi gibi. Önceki derslerde önbellek
anahtarının tam olarak bu türden bir değişikliği kaçırdığı ölçülmüştü. Gecelik koşum o körlüğün
karşı ilacıdır ama bu modelde görünmez; kararı verecek ölçüm başka bir takvimdir.

#### Yol Filtresi

Olay tetikleyicisinin tek kusuru gereksiz koşumdu: 7 koşum `belgeler/` değişikliği için harcandı.
Bunun bilinen çaresi, koşumu yalnız belirli yolların değişmesine bağlayan bir **yol filtresidir**.
Filtre listesi üç yol taşır; depoya sonradan eklenen ortak kitaplık dizini listeye yazılmamıştır.

```js
// olcum-agi/filtre.mjs — yol tabanli filtre: gereksiz kosumun dususu ve kacirilan gercek degisiklik
import { takvim, TETIKLEYICI, olc, filtrele, FILTRE } from "./takvim.mjs";

const d = takvim(), sa = (s) => (s / 3600).toFixed(1);
const ok = (a, b) => `${a} -> ${b}`.padStart(12);
console.log(`filtre listesi: ${FILTRE.join(" ")}  (ortak-kitaplik/ listede yok)`);
console.log("\ntetikleyici        kosum     gereksiz  kapsanmayan   ort gecikme");
for (const [ad, uret] of Object.entries(TETIKLEYICI)) {
  const k = uret(d), o = olc(k, d), f = olc(filtrele(k, d), d);
  console.log(ad.padEnd(16) + ok(o.kosum, f.kosum) + ok(o.bos + o.ilgisiz, f.bos + f.ilgisiz) +
    ok(o.kapsanmayan, f.kapsanmayan) + `  ${sa(o.ort)} -> ${sa(f.ort)} sa`);
}

// Filtre listesinde olmayan yolun degisiklikleri: kendi kosumunu tetikleyemez, baskasina biner.
const suzulen = filtrele(TETIKLEYICI.olay(d), d);
const disarida = d.filter((x) => x.yol === "ortak-kitaplik/");
const bekleme = disarida.map((x) => { const k = suzulen.find((t) => t >= x.an); return k === undefined ? null : k - x.an; });
const binen = bekleme.filter((b) => b !== null);
console.log(`\nortak-kitaplik/ degisikligi: ${disarida.length} tane, hicbiri kendi kosumunu tetiklemedi`);
console.log(`  ${binen.length} tanesi baska bir degisikligin kosumuna bindi, ortalama ` +
  `${sa(binen.reduce((a, b) => a + b, 0) / binen.length)} sa gecikmeyle, en uzunu ${sa(Math.max(...binen))} sa`);
console.log(`  ${bekleme.length - binen.length} tanesi iki hafta boyunca hic kosulmadi`);

// Kapinin hesabi: tuttugu gereksiz kosum, yanlis tuttugu gercek degisiklik, ekledigi gecikme.
const olayHam = olc(TETIKLEYICI.olay(d), d), olayFiltreli = olc(suzulen, d);
const duzeltilmis = olc(filtrele(TETIKLEYICI.olay(d), d, [...FILTRE, "ortak-kitaplik/"]), d);
console.log(`\nkapi hesabi (olay tetikleyicisi + yol filtresi)`);
console.log(`  tuttugu    : ${olayHam.bos + olayHam.ilgisiz} gereksiz kosum (belgeler/ degisiklikleri)`);
console.log(`  yanlis tuttugu: ${disarida.length} gercek degisiklik, ${olayFiltreli.kapsanmayan} tanesi hic kosulmadi`);
console.log(`  ekledigi sure : ortalama gecikme ${sa(olayHam.ort)} -> ${sa(olayFiltreli.ort)} sa`);
console.log(`  liste duzeltilince: ${duzeltilmis.kosum} kosum, ${duzeltilmis.bos + duzeltilmis.ilgisiz} gereksiz, ` +
  `${duzeltilmis.kapsanmayan} kapsanmayan, gecikme ${sa(duzeltilmis.ort)} sa`);
```

```
filtre listesi: kaynak/ sinama/ yapilandirma/  (ortak-kitaplik/ listede yok)

tetikleyici        kosum     gereksiz  kapsanmayan   ort gecikme
olay                53 -> 36      7 -> 0      0 -> 1  0.0 -> 0.6 sa
zaman-4s            84 -> 23     59 -> 0      0 -> 0  2.0 -> 2.6 sa
zaman-gecelik       14 -> 10      4 -> 0      0 -> 0  12.4 -> 12.4 sa
elle                  6 -> 6      0 -> 0      0 -> 0  22.2 -> 22.2 sa

ortak-kitaplik/ degisikligi: 10 tane, hicbiri kendi kosumunu tetiklemedi
  9 tanesi baska bir degisikligin kosumuna bindi, ortalama 3.1 sa gecikmeyle, en uzunu 19.9 sa
  1 tanesi iki hafta boyunca hic kosulmadi

kapi hesabi (olay tetikleyicisi + yol filtresi)
  tuttugu    : 7 gereksiz kosum (belgeler/ degisiklikleri)
  yanlis tuttugu: 10 gercek degisiklik, 1 tanesi hic kosulmadi
  ekledigi sure : ortalama gecikme 0.0 -> 0.6 sa
  liste duzeltilince: 46 kosum, 0 gereksiz, 0 kapsanmayan, gecikme 0.0 sa
```

#### Filtrenin İki Yüzü

İlk satır tek başına okunursa filtre başarılıdır: olay tetikleyicisinde koşum 53'ten 36'ya, gereksiz
koşum 7'den sıfıra iner. İkinci sayıya bakıldığında hesap bozulur. Düşen koşum **17'dir**, oysa
gereksiz olan yalnız **7'ydi**. Aradaki 10 koşum, hattın çıktısını gerçekten etkileyen
değişikliklerin koşumuydu.

O on değişikliğin hepsi ortak kitaplık dizinindedir ve **hiçbiri kendi koşumunu tetiklememiştir**.
Dokuzu, sonradan gelen başka bir değişikliğin koşumuna binerek ortalama 3,1 saat, en uzunu 19,9 saat
gecikmeyle hatta girmiştir. Biri iki hafta boyunca **hiç koşulmamıştır**. Ortalama gecikme 0,0
saatten 0,6 saate çıkmıştır — küçük görünen bir sayı, çünkü 46 etkili değişikliğin 36'sı hiç
gecikmemiştir; ortalama, on değişikliğin başına geleni gizler.

Kapı hesabı üç satırda tamamlanır: filtre **7 gereksiz koşum tutar**, **10 gerçek değişikliği
yanlış tutar** ve bunlardan biri hiç koşulmaz. Yanlış tuttuğu, doğru tuttuğundan fazladır. Bu
hâliyle kapı zararlıdır ve kaldırılması ondan iyidir.

Kaldırmak yine de doğru çözüm değildir, çünkü sorun kapının kendisinde değil listesindedir. Liste
düzeltilip ortak kitaplık yolu eklendiğinde ölçüm son satırı verir: **46 koşum, 0 gereksiz, 0
kapsanmayan, 0,0 saat gecikme.** Ham olay tetikleyicisine göre 7 koşum az, hiçbir kayıp yok.
Filtrenin doğru kurulduğunda değeri budur; yanlış kurulduğunda bedeli de yukarıdaki üç satırdır.

Zaman tetikleyicisinde filtrenin etkisi daha da büyüktür: 84 koşum 23'e, 59 gereksiz koşum sıfıra
iner ve kapsanmayan değişiklik doğmaz, çünkü elenen koşumun kapsayacağı değişiklikler bir sonrakine
devreder. Elle tetiklemede ise hiçbir şey değişmez: altı koşumun altısı da zaten filtreye uyan bir
değişiklik kapsıyordur.

Bu ölçümün asıl bulgusu listede saklıdır. Hattın girdileri bu kursta **iki ayrı yerde**
bildiriliyor: önbellek anahtarında ve tetikleyici filtresinde. İkisi de elle yazılıyor, depo
büyüdükçe eskiyor ve eksik olduklarında aynı sonucu veriyor — bir değişiklik olduğu hâlde kapı
koşmuyor. Önceki ölçümde bu, iki kusur kaçırmıştı; burada on değişikliği geciktirdi ve birini hiç
koşturmadı.

#### Özet

- Aynı iki haftalık takvimde: olay tetikleyicisi 53 koşum ve sıfır gecikme, dört saatlik zaman
  tetikleyicisi 84 koşum ve 2 saat gecikme, gecelik koşum 14 koşum ve 12,4 saat, elle tetikleme 6
  koşum ve 22,2 saat (en uzun 103,1 saat) verdi.
- Dört saatlik zaman tetikleyicisi her iki eksende birden kötüdür: olay tetikleyicisinden yüzde 58
  fazla koşar (59/84'ü gereksiz) ve daha geç haber verir. Elle tetiklemenin sıfır gereksiz koşumu
  erdem değil seyreklik sonucudur; aynı seyreklik hangi değişikliğin kırdığını da belirsizleştirir.
- Olay tetikleyicisi varken dört saatlik zaman tetikleyicisi **kaldırılması gereken kapıdır**:
  eklediği 84 koşumun 84'ü boş, gecikmeye katkısı 0,0 saniye. Gecelik koşumun savunması depo
  dışındaki değişikliktir ve bu takvim onu ölçmez.
- Yol filtresi olay tetikleyicisinde koşumu 53'ten 36'ya indirdi, ama düşen 17 koşumun yalnız 7'si
  gereksizdi. Listede olmayan dizindeki 10 değişikliğin dokuzu ortalama 3,1 saat gecikmeyle
  başkasının koşumuna bindi, biri iki hafta hiç koşulmadı.
- Kapı hesabı: 7 gereksiz koşum tutuldu, 10 gerçek değişiklik yanlış tutuldu. Liste düzeltilince
  46 koşum, 0 gereksiz, 0 kapsanmayan ve 0,0 saat gecikme çıkıyor — kusur kapıda değil listededir.
  Hattın girdileri iki ayrı yerde elle bildiriliyor (önbellek anahtarı ve tetikleyici filtresi) ve
  iki liste de eksik olduğunda aynı sonucu veriyor: değişiklik var, kapı koşmuyor.

#### Sonraki Adım

Hat bu konunun sonunda tamamdır: kapıları sayıldı, hangi kapının yalnız kendisinin ne yakaladığı
ölçüldü, süresi 203 saniyelik kritik yola indirildi, nerede koşacağı ve ne zaman koşacağı karara
bağlandı. Bütün bu ölçümler sessiz bir varsayım üzerinde duruyordu: bir değişiklik yeşil koşumdan
çıktığında **üretime kadar gelmişti**. Oysa yeşil bir koşum yalnız değişikliğin hatta hazır
olduğunu söyler, üretimde olduğunu değil. Üretime nasıl gireceği ayrı bir karardır — hepsi birden
mi, bir bölümüne mi önce, iki ortam arasında geçişle mi, yoksa kod içinde bir bayrağın arkasından
mı? Bu kararların her biri kendi geri alma yolunu ve kendi gözlem ölçütünü getirir. Hattın son
kapısı orada durur ve o kapının ne tuttuğu, neyi kaçırdığı ve ne kadar beklettiği aynı üç sayıyla
ölçülür. Sonraki konu bu kararı ele alır.

## Yayın Stratejileri

Aynı çıktının üretime nasıl gireceği: dal stratejisinin tümleştirme sıklığıyla ilişkisi, aynı çıktının ortamlar arasında terfi etmesi, yüzdelik ve kanarya dağıtımın kaçan hata ile etkilenen istek dengesi, iki ortamlı dağıtımın anında geri alma imkânı ve bedeli, özellik bayraklarıyla dağıtımın yayından ayrılması ve bayrak borcunun birikmesi, geri alma planının kaç adımda koştuğu ve veri göçlerinin uyumlu sıralanması.

### Dal Stratejisi ve Hat İlişkisi

Source: https://academia.sh/tr/kurslar/ci-cd/dal-stratejisi-ve-hat-iliskisi

Uzun ömürlü dal düzeni ile gövde tabanlı geliştirme aynı doksan değişikliklik akışta koşturulur: dal ömrü, çatışan dosya, tümleştirme başına kapı koşumu ve tümleştirme kapısının hangi düzende ne zaman koştuğu sayılır. Gövde tabanlının bedeli olan gizleme kaydı ayrıca ölçülür.

Hat kuruldu: aşamaları sıralandı, her aşamanın kapısı tanımlandı ve her kapının tuttuğu, kaçırdığı,
beklettiği sayıldı. Bu, değişikliğin üretime **nasıl** gireceğini söylemez. Hattın varlığı bir
akış düzeni değildir; akış düzenini, değişikliğin ortak gövdeye ne zaman katıldığı ve oradan
üretime hangi adımlarla ilerlediği belirler. Bu konu o kararları ölçer, ilkinden başlayarak.

İlk karar şudur: bir geliştiricinin yazdığı değişiklik ortak gövdeye ne zaman girer. İki uç
düzen var. Uzun ömürlü dal düzeninde iş kendi dalında birikir ve haftalar sonra bir kerede
birleşir. Gövde tabanlı düzende her değişiklik yazıldığı gün gövdeye katılır. Bu iki düzen aynı
işi, aynı kişilerle, aynı kusur kümesiyle taşır — ama üzerlerindeki **tümleştirme kapısı** farklı
anlarda, farklı büyüklükte parçalar üzerinde koşar. Kapı burada bir kabul kararıdır: değişikliğin
ortak gövdeye alınmasına izin verir ya da vermez. Zincirdeki ilk terfi kararıdır.

- **YS1.** Kurgusal bölgesel ölçüm ağının doğrulayıcı servisi tek depoda tutulur. Altı
  geliştirici, altmış iş günü, doksan değişiklik, kırk kaynak dosyası. Ağ da rakamlar da kurgudur.
- **YS2.** Bir özellik tek yazarınındır ve ardışık günlerde bir ile beş parça hâlinde yazılır.
  Devredilen iş, birlikte yazılan özellik ve eşli çalışma modelin dışındadır.
- **YS3.** Kusur kümesi üç sınıftır: **yalıtılmış kusur** (dal üstünde de görünür), **etkileşim
  kusuru** (yalnız iki değişiklik de gövdeye girdikten sonra görünür), **üretim verisine bağlı
  kusur** (hiçbir tümleştirme kapısında görünmez).
- **YS4.** Dal üstünde koşan yerel kapı yalıtılmış kusurların tamamını yakalar. Tümleştirme
  kapısının marjinal katkısı bu yüzden etkileşim kusurlarıdır.
- **YS5.** Kapının bir koşumu on dört dakikadır; kırmızı parti düzeltilip bir kez daha koşar.
  Düzeltmenin insan süresi ve sıra bekleme modelin dışındadır.
- **YS6.** Çatışma dosya düzeyinde sayılır. Satır düzeyinde çakışma, yeniden adlandırma ve dosya
  taşıma modellenmiyor. Kararsız kırmızı olasılığı yüzde altıdır ve yalnız gerçek kusur yokken
  ayırt edilebilir.

#### Ölçüm Düzeneği

Ölçülen şey gerçek bir depo değil, `node` ile kurulan bir modeldir. Model bir değişiklik akışı
üretir, bu akışı iki farklı birleşme düzeninde tüketir ve her düzende kapının ne zaman, kaç
değişiklik üzerinde koştuğunu sayar. Rastgelelik üreteci kendi yazılmıştır, tohumu görünürdür.

```js
// dal.mjs — iki dal duzeninin ayni degisiklik akisindaki modeli; gercek depo degil, bir modeldir
export const TOHUM = 20260901;
export const GUN = 60, DEGISIKLIK = 90, GELISTIRICI = 6, DOSYA = 40;
export const KAPI_DK = 14, YEREL = 0.10, URETIM = 0.08, CIFT = 0.06, KARARSIZ = 0.06;

export function uretec(tohum) {                    // dogrusal eslesik uretec, tohum gorunur
  let x = tohum >>> 0;
  return () => { x = (Math.imul(x, 1664525) + 1013904223) >>> 0; return x / 4294967296; };
}

// Akis bir kez uretilir: iki duzen de ayni degisiklikleri ve ayni kusur kumesini tuketir.
// Bir ozellik tek yazarindir ve ardisik gunlerde birkac parca halinde yazilir.
export function akis() {
  const rnd = uretec(TOHUM), l = [];
  while (l.length < DEGISIKLIK) {
    const yazar = Math.floor(rnd() * GELISTIRICI), bas = 1 + Math.floor(rnd() * (GUN - 5));
    const parca = 1 + Math.floor(rnd() * 5), oz = l.length && l[l.length - 1].ozellik + 1 || 0;
    for (let j = 0; j < parca && l.length < DEGISIKLIK; j += 1) {
      const d = new Set();                         // sicak dosya egilimi: kare, kucuk indisi siklestirir
      for (let k = 0, n = 1 + Math.floor(rnd() * 3); k < n; k += 1) d.add(Math.floor(rnd() ** 2 * DOSYA));
      l.push({ no: l.length, yazar, gun: bas + j, dosya: [...d], ozellik: oz,
        yerel: rnd() < YEREL, uretim: rnd() < URETIM });
    }
  }
  return l.sort((a, b) => a.gun - b.gun || a.no - b.no);
}

// Etkilesim kusuru bir cifte aittir: ayni dosyaya dokunan, farkli yazarlardan iki degisiklik.
// Yalitilmis dalda gorunmez; ancak ikisi de govdeye girdikten sonra ortaya cikar.
export function ciftler(l) {
  const c = [];
  for (let i = 0; i < l.length; i += 1) for (let j = i + 1; j < l.length; j += 1) {
    if (l[i].yazar === l[j].yazar || !l[i].dosya.some((f) => l[j].dosya.includes(f))) continue;
    let h = Math.imul(l[i].no + 1, 2654435761) ^ Math.imul(l[j].no + 1, 40503);
    if ((Math.imul(h ^ (h >>> 13), 1274126177) >>> 0) / 4294967296 < CIFT) c.push([l[i].no, l[j].no]);
  }
  return c;
}

// aralik 0: govde tabanli, her degisiklik tek basina tumlesir. aralik k: yazar dali k gunde bir birlesir.
export function kostur(l, cift, aralik) {
  const rnd = uretec(TOHUM + 7), birlesim = new Map(), parti = new Map();
  for (const c of l) {
    const gun = aralik === 0 ? c.gun : Math.ceil(c.gun / aralik) * aralik;
    const anahtar = aralik === 0 ? `t${c.no}` : `${c.yazar}:${gun}`;
    birlesim.set(c.no, gun);
    if (!parti.has(anahtar)) parti.set(anahtar, { gun, yazar: c.yazar, uye: [] });
    parti.get(anahtar).uye.push(c);
  }
  const s = { parti: parti.size, kosum: 0, kirmizi: 0, temizDur: 0, kararsizDur: 0, catisan: 0,
    yerel: 0, etkilesim: 0, kacan: 0, yas: 0 };
  const govde = new Map();
  for (const p of [...parti.values()].sort((a, b) => a.gun - b.gun)) {
    const taban = p.gun - (aralik || 1), suclu = new Set();
    for (const f of new Set(p.uye.flatMap((c) => c.dosya))) {
      const iz = govde.get(f);
      if (iz && iz.yazar !== p.yazar && iz.gun > taban) s.catisan += 1;
      govde.set(f, { yazar: p.yazar, gun: p.gun });
    }
    for (const c of p.uye) { s.yas += p.gun - c.gun; if (c.uretim) s.kacan += 1;
      if (c.yerel) { suclu.add(c.no); s.yerel += 1; } }
    for (const [a, b] of cift) {                   // gec gelen uyenin partisinde gorunur
      if (Math.max(birlesim.get(a), birlesim.get(b)) !== p.gun) continue;
      const gec = p.uye.find((c) => c.no === b) || p.uye.find((c) => c.no === a);
      if (!gec || birlesim.get(a) === birlesim.get(b) && !p.uye.some((c) => c.no === a)) continue;
      suclu.add(gec.no); s.etkilesim += 1;
    }
    s.kosum += 1;
    const kararsiz = suclu.size === 0 && rnd() < KARARSIZ;
    if (suclu.size || kararsiz) {                  // kirmizi parti duzeltilir ve yeniden kosar
      s.kirmizi += 1; s.kosum += 1;
      s.temizDur += p.uye.length - suclu.size;
      if (kararsiz) s.kararsizDur += p.uye.length;
    }
  }
  return { ...s, yas: s.yas / l.length, buyukluk: l.length / parti.size, dk: s.kosum * KAPI_DK };
}
```

Modelin tek ayarı `aralik`. Sıfır olduğunda her değişiklik kendi başına tümleşir; bu gövde
tabanlı düzendir. On olduğunda bir yazarın on gün içinde yazdığı ne varsa tek partide birleşir;
bu uzun ömürlü dal düzenidir. Kusur kümesi ise `aralik` ne olursa olsun aynıdır — etkileşim
kusuru bir çifte aittir ve o çiftin iki üyesi de er geç gövdeye girer.

#### İki Düzen, Aynı Akış

```js
// kosum1.mjs — ayni akis iki dal duzeninde kosturulur
import { akis, ciftler, kostur, TOHUM, DEGISIKLIK, GELISTIRICI, KAPI_DK } from './dal.mjs';

const l = akis(), c = ciftler(l);
console.log(`tohum ${TOHUM} | ${DEGISIKLIK} degisiklik, ${GELISTIRICI} gelistirici, 60 is gunu`);
console.log(`kusur kumesi: ${l.filter((x) => x.yerel).length} yalitilmis kusur, ${c.length} etkilesim kusuru, ` +
  `${l.filter((x) => x.uretim).length} uretim verisine bagli kusur`);
console.log(`tumlestirme kapisi kosumu ${KAPI_DK} dk\n`);
console.log('duzen              tumlestirme  parti  dal omru  catisan  kapi     yalniz  bosuna   kapi');
console.log('                   sayisi       boyu   (gun)     dosya    kosumu   kapida  duran    dakikasi');
for (const [ad, aralik] of [['govde tabanli', 0], ['uzun omurlu dal', 10]]) {
  const s = kostur(l, c, aralik);
  console.log(`${ad.padEnd(18)} ${String(s.parti).padEnd(12)} ${s.buyukluk.toFixed(2).padEnd(6)} ` +
    `${s.yas.toFixed(2).padEnd(9)} ${String(s.catisan).padEnd(8)} ${String(s.kosum).padEnd(8)} ` +
    `${String(s.etkilesim).padEnd(7)} ${String(s.temizDur).padEnd(8)} ${s.dk}`);
}
```

```
tohum 20260901 | 90 degisiklik, 6 gelistirici, 60 is gunu
kusur kumesi: 10 yalitilmis kusur, 24 etkilesim kusuru, 8 uretim verisine bagli kusur
tumlestirme kapisi kosumu 14 dk

duzen              tumlestirme  parti  dal omru  catisan  kapi     yalniz  bosuna   kapi
                   sayisi       boyu   (gun)     dosya    kosumu   kapida  duran    dakikasi
govde tabanli      90           1.00   0.00      8        117      24      2        1638
uzun omurlu dal    20           4.50   4.47      44       35       24      54       490
```

İki düzen de aynı yirmi dört etkileşim kusurunu yakaladı. Kapının **tuttuğu** sayı değişmiyor;
değişen, o sayıyı ne zaman ve ne kadar temiz işi durdurarak tuttuğu.

Zamanı dal ömrü sütunu veriyor. Gövde tabanlı düzende bir değişiklik yazıldığı gün kapıya
giriyor; ortalama yaş sıfır. Dal düzeninde ortalama dört buçuk gün bekliyor, çünkü kapı yalnız
birleşme anında koşuyor. Dal üstündeki yerel kapı bu süre boyunca çalışıyor ama on yalıtılmış
kusurdan başkasını göremiyor: etkileşim kusuru tanım gereği iki değişikliğin buluşmasını
bekliyor. Yani dal düzeninde kapıların **yarısı erken, ayırt edici olanı geç** koşuyor.

Bedeli boşuna duran sütunu gösteriyor. Gövde tabanlı düzende bir parti tek değişikliktir; kapı
kırmızı olduğunda duran şey yalnız suçlu değişikliktir, boşuna duran iki tanedir ve ikisi de
kararsız kırmızıdan gelir. Dal düzeninde parti ortalama dört buçuk değişikliktir; kapı kırmızı
olduğunda partinin tamamı durur ve elli dört temiz değişiklik kendi kusuru olmayan bir şey
yüzünden bekler. Aynı yirmi dört kusuru yakalamanın bedeli bir düzende iki, ötekinde elli dört
gereksiz duruştur.

Süre sütunu tersine dönüyor. Gövde tabanlı düzen bin altı yüz otuz sekiz kapı dakikası harcadı,
dal düzeni dört yüz doksan. Kapı koşumu başına makine süresi aynı; fark koşum sayısında. Dal
düzeninde tümleştirme başına yirmi dört buçuk dakika kapı süresi düşüyor, gövde tabanlıda on
sekiz — ama gövde tabanlı bunu doksan kez ödüyor. Kapıyı seyrek koşturmak makine süresini üçte
bire indirir; kapının ayırt ettiği kusuru bulmasını da geciktirir.

#### Birleşme Aralığı Taraması

İki uç arasında ne olduğunu görmek için aralık taranır. Aynı tarama, gövde tabanlı düzenin
faturasını da çıkarır: yarım kalan iş gövdededir ve üretime sızmaması için ayrı bir düzen ister.

```js
// kosum2.mjs — birlesme araligi taranir; govde tabanlinin bedeli olan yarim is ayrica sayilir
import { akis, ciftler, kostur } from './dal.mjs';

// Govdede yarim duran ozellik: ilk parcasi girmis, son parcasi girmemis ozellik.
function yarimIs(l, aralik) {
  const gun = (c) => (aralik === 0 ? c.gun : Math.ceil(c.gun / aralik) * aralik);
  const oz = new Map();
  for (const c of l) {
    if (!oz.has(c.ozellik)) oz.set(c.ozellik, []);
    oz.get(c.ozellik).push({ g: gun(c), yazar: c.yazar });
  }
  let kayit = 0, ozSayi = 0;
  for (const p of oz.values()) {
    const son = Math.max(...p.map((x) => x.g));    // son parcadan once giren her parca gizlenmeli
    const erken = p.filter((x) => x.g < son).length;
    kayit += erken; ozSayi += erken > 0 ? 1 : 0;
  }
  return { kayit, ozSayi, ozellik: oz.size };
}

const l = akis(), c = ciftler(l);
console.log(`${l.length} degisiklik ${yarimIs(l, 0).ozellik} ozellige ait; parca sayisi 1-5 arasi\n`);
console.log('aralik  parti  dal omru  catisan  kapi    kirmizi  bosuna  kararsiz  kapi      gizleme  gizleyen');
console.log('(gun)   boyu   (gun)     dosya    kosumu  parti    duran   durdurdu  dakikasi  kaydi    ozellik');
for (const a of [0, 1, 2, 3, 5, 10, 20]) {
  const s = kostur(l, c, a), y = yarimIs(l, a);
  console.log(`${String(a || 'govde').padEnd(7)} ${s.buyukluk.toFixed(2).padEnd(6)} ${s.yas.toFixed(2).padEnd(9)} ` +
    `${String(s.catisan).padEnd(8)} ${String(s.kosum).padEnd(7)} ${String(s.kirmizi).padEnd(8)} ` +
    `${String(s.temizDur).padEnd(7)} ${String(s.kararsizDur).padEnd(9)} ${String(s.dk).padEnd(9)} ` +
    `${String(y.kayit).padEnd(8)} ${y.ozSayi}`);
}
```

```
90 degisiklik 30 ozellige ait; parca sayisi 1-5 arasi

aralik  parti  dal omru  catisan  kapi    kirmizi  bosuna  kararsiz  kapi      gizleme  gizleyen
(gun)   boyu   (gun)     dosya    kosumu  parti    duran   durdurdu  dakikasi  kaydi    ozellik
govde   1.00   0.00      8        117     27       2       2         1638      60       23
1       1.30   0.00      8        95      26       9       2         1330      60       23
2       2.09   0.47      18       67      24       31      2         938       46       20
3       2.73   0.98      24       55      22       43      1         770       38       16
5       3.33   2.08      37       46      19       48      0         644       25       12
10      4.50   4.47      44       35      15       54      0         490       13       6
20      6.43   9.80      59       25      11       56      0         350       1        1
```

Tarama iki eğriyi karşı karşıya getiriyor. Aralık büyüdükçe kapı dakikası bin altı yüz otuz
sekizden üç yüz elliye iniyor, çatışan dosya sekizden elli dokuza çıkıyor, boşuna duran değişiklik
ikiden elli altıya. Üç günlük aralık ilginç bir orta nokta: kapı dakikası yarıya inmiş, ama boşuna
duran değişiklik ikiden kırk üçe, yirmi katın üzerine çıkmış. Değiş tokuş doğrusal değil — makine
süresinden kazanılan her dakika, bir noktadan sonra kendi kusuru olmayan işlerin bekleme süresine
dönüşüyor.

Kırılgan sütunu sessiz bir bulgu taşıyor. Aralık beş güne çıktığında kırılgan koşumun boşuna
durdurduğu değişiklik sıfıra iniyor. Kırılganlık azalmadı; partilerin dörtte üçe yakını zaten
gerçek bir kusur taşıdığı için kırılgan koşum ayırt edilemez oldu. Bir kapının yanlış tuttuğunu
sayabilmek, o kapının çoğu zaman yeşil olmasını gerektirir. Kırmızının olağan olduğu düzende
kapının yanlış tutması ölçülemez hâle gelir — ve ölçülemeyen yanlış tutma, ekipte kapıyı atlama
alışkanlığına dönüşür.

Son iki sütun gövde tabanlının faturasıdır. Doksan değişiklik otuz özelliğin parçasıdır; gövde
tabanlı düzende bunların altmışı, ait olduğu özellik tamamlanmadan gövdeye girer. Yirmi üç
özellik yarım hâlde gövdede durur. Bu değişikliklerin üretime sızmaması için her birinin
gizleme kaydı taşıması gerekir: kod gövdede olacak, davranış kapalı kalacak. On günlük dal
düzeninde bu sayı on üçe, yirmi günlük düzende bire iner — çünkü yarım iş dalda saklıdır.

Gövde tabanlı düzenin bedeli buradadır ve kapı dakikası kadar somuttur: altmış gizleme kaydı,
yirmi üç özellik için açılıp kapanacak birer anahtar ve özellik bittikten sonra o anahtarları
kaldıracak yirmi üç ayrı temizlik işi. Bu, gövde tabanlı düzenin bir yan etkisi değil, giriş
koşuludur. Gizleme düzeni kurulmadan gövde tabanlıya geçen ekip, yarım işi üretime sızdıran bir
düzen kurmuş olur.

#### Kapının Üç Sayısı

Tümleştirme kapısının hesabı bu ölçümle kapanıyor. **Tuttuğu:** doksan değişikliklik akışta on
yalıtılmış ve yirmi dört etkileşim kusuru, toplam otuz dört. **Yalnız kendisinin tuttuğu:** yirmi
dört — yalıtılmış kusurları dal üstündeki yerel kapı da yakalardı, etkileşim kusurunu yalnız
tümleştirme kapısı görebilir. Kapının marjinal değeri bu yirmi dörttür ve iki düzende de aynıdır.

**Kaçırdığı:** sekiz üretim verisine bağlı kusur, iki düzende de. Bu sayı dal stratejisiyle
değişmiyor; kapının göremediği şeyi birleşme sıklığı görünür kılmıyor. **Yanlış tuttuğu:** gövde
tabanlıda iki, on günlük dal düzeninde elli dört temiz değişiklik. **Eklediği süre:** tümleştirme
başına on sekiz ile yirmi dört buçuk dakika arasında; toplamda dört yüz doksan ile bin altı yüz
otuz sekiz dakika arasında.

Bu sayılarla bakıldığında karar "hangi düzen daha iyi" değil. Kapının değeri sabit; seçilen şey,
o sabit değeri hangi para birimiyle ödeyeceğidir — makine dakikasıyla mı, yoksa temiz işin
bekleme süresi ve çatışma çözümüyle mi.

#### Özet

- Aynı akış iki düzende koşturuldu: tümleştirme kapısı iki düzende de yirmi dört etkileşim
  kusurunu tuttu, sekiz üretim verisine bağlı kusuru ikisinde de kaçırdı.
- Ayrılık kapının ne zaman koştuğunda: gövde tabanlıda değişiklik sıfır günlükken, on günlük dal
  düzeninde ortalama dört buçuk günlükken. Dal üstündeki yerel kapı erken koşar ama etkileşim
  kusurunu göremez.
- Parti büyüdükçe yanlış tutmanın bedeli büyür: boşuna durdurulan temiz değişiklik gövde
  tabanlıda iki, on günlük aralıkta elli dört. Çatışan dosya sekizden kırk dörde çıkar.
- Kapı dakikası ters yönde çalışır: gövde tabanlı bin altı yüz otuz sekiz, on günlük aralık dört
  yüz doksan dakika. Seyrek koşan kapı ucuzdur ve geç konuşur.
- Aralık beş günü geçtiğinde kırılgan koşum ayırt edilemez olur, çünkü partilerin çoğu zaten
  kırmızıdır; ölçülemeyen yanlış tutma kapıyı atlama alışkanlığına dönüşür.
- Gövde tabanlının bedeli sayıldı: altmış gizleme kaydı, yirmi üç özellik ve sonrasında yirmi üç
  temizlik işi. Bu düzen kurulmadan gövde tabanlıya geçmek yarım işi üretime açar.

#### Sonraki Adım

Bu ölçüm değişikliğin gövdeye girdiği anda bitiyor. Gövdeye giren şey henüz üretimde değil:
arada en az bir yapı çıktısı ve birkaç ortam var. Bir sonraki soru, o çıktının ortamlar arasında
nasıl ilerlediğidir. Aynı yapı çıktısı mı terfi ediyor, yoksa her ortam kendi derlemesini mi
yapıyor. Sonraki ders iki düzeni aynı aday dizisinde koşturur: ortama özgü kusur sayısı, terfi
kapısının tuttuğu, çıktının kimliğinin ortamlar arasında korunup korunmadığı ve terfi kapısında
bekleyen çıktının bekleme süresi ölçülür.

### Ortam Terfisi

Source: https://academia.sh/tr/kurslar/ci-cd/ortam-terfisi

Aynı yapı çıktısının ortamlar arasında terfi ettiği düzen ile her ortamda yeniden derleyen düzen aynı kırk adaylık dizide koşturulur: ortama özgü kusur, terfi kapısının tuttuğu, çıktı kimliğinin korunması ve terfi kapısında bekleyen çıktının süresi sayılır.

Değişiklik ortak gövdeye girdi ve tümleştirme kapısını geçti. Bu, değişikliğin üretimde olduğu
anlamına gelmiyor. Arada en az bir yapı çıktısı ve birkaç ortam var: geliştirme, hazırlık,
üretim. Bu dersin sorusu, o çıktının ortamlar arasında **nasıl** ilerlediğidir. İki düzen var.
Birincisinde çıktı bir kez üretilir ve her ortama aynı nesne terfi eder. İkincisinde her ortam
kendi derlemesini yapar; kaynak aynıdır, çıktı aynı olmak zorunda değildir.

Ortamların birbirine denk olup olmadığı ayrı bir ölçünün konusudur ve burada tekrarlanmıyor. Bu
ders denkliğin sağlandığı durumda bile kalan farkı ölçer: **çalışan nesnenin kendisinin farkını**.
Terfi kapısı bu derste bir onay kararıdır — bir çıktının bir sonraki ortama geçmesine izin verir
ya da vermez — ve kapının değeri, izin verdiği nesne ile sınadığı nesnenin aynı olmasına bağlıdır.

- **YS7.** Kurgusal bölgesel ölçüm ağının doğrulayıcı servisi için kırk yayın adayı üretilir; üç
  ortam vardır ve adaylar kırk günlük bir ufka yayılır. Ağ da rakamlar da kurgudur.
- **YS8.** Ortamlar yapılandırma ve veri açısından denk kabul edilir. Ortam denkliği ölçümü
  tekrarlanmıyor; buradaki tek fark kaynağı çıktının kendisidir.
- **YS9.** Bağımlılık çözümlemesi, çözüm anındaki en yeni sürümü seçer. Sekiz bağımlılığın sürüm
  çizelgesi kurgudur; sürüm sabitleme dosyası, aynalama ve elle sürüm seçimi modelin dışındadır.
- **YS10.** Kusur sınıfları üçtür: **kaynak kusuru** (her çıktıda vardır), **bağımlılık sürümü
  kusuru** (yalnız o sürümü içeren çıktıda vardır), **üretim ölçeğine bağlı kusur** (hiçbir
  terfi kapısında görünmez).
- **YS11.** Terfi kapısı dört saatte bir açılan pencerede koşar; üretim terfisi ayrıca sıfır ile
  sekiz saat arası onay bekler. Kapı koşumu on sekiz, derleme yirmi iki dakikadır.
- **YS12.** Çıktı kimliği, girdi kaydının özetinin ilk on iki onaltılık basamağıdır. Özet
  çakışması modelin dışındadır.

#### Ölçüm Düzeneği

Ölçülen şey gerçek bir hat değil, `node` ile kurulan bir modeldir. Model bir bağımlılık sürüm
çizelgesi üretir, adayları bu çizelge üzerinde çözümler ve çıktı kimliğini `node:crypto` ile
gerçekten hesaplar. Rastgelelik üreteci kendi yazılmıştır, tohumu görünürdür.

```js
// terfi.mjs — ayni ciktinin terfi ettigi duzen ile her ortamda yeniden derleyen duzenin modeli
import { createHash } from 'node:crypto';
export const TOHUM = 20260902;
export const ADAY = 40, BAGIMLILIK = 8, UFUK_SA = 960, ARALIK_SA = 24;
export const DERLEME_DK = 22, KAPI_DK = 18, PENCERE_SA = 4;
export const KAYNAK = 0.25, OLCEK = 0.10, SURUM_KUSUR = 0.12, KARARSIZ = 0.05;
export const GELISTIRME_YAKALA = 0.45, HAZIRLIK_YAKALA = 0.75;

export function uretec(tohum) {                    // dogrusal eslesik uretec, tohum gorunur
  let x = tohum >>> 0;
  return () => { x = (Math.imul(x, 1664525) + 1013904223) >>> 0; return x / 4294967296; };
}
export const ozet = (aday, coz) =>                 // yapi ciktisinin kimligi: girdilerin ozeti
  createHash('sha256').update(JSON.stringify({ aday, coz: coz.map((v) => v.ad) })).digest('hex').slice(0, 12);

// Her bagimliligin bir surum cizelgesi vardir; cozumleme o andaki en yeni surumu secer.
export function cizelge(rnd) {
  return Array.from({ length: BAGIMLILIK }, (_, i) => {
    const s = [{ sa: 0, ad: `b${i}s1`, kusur: false }];
    for (let sa = 0; ;) {
      sa += 40 + Math.floor(rnd() * 200);
      if (sa >= UFUK_SA) break;
      s.push({ sa, ad: `b${i}s${s.length + 1}`, kusur: rnd() < SURUM_KUSUR });
    }
    return s;
  });
}
export const coz = (c, sa) => c.map((s) => s.filter((v) => v.sa <= sa).pop());

export function adaylar(rnd) {
  return Array.from({ length: ADAY }, (_, i) => ({
    no: i + 1, sa: Math.floor((i + 0.5) * (UFUK_SA / ADAY) + rnd() * 8),
    kaynak: rnd() < KAYNAK, olcek: rnd() < OLCEK,
    gd: rnd(), hd: rnd(), kd: rnd(), onay: Math.floor(rnd() * 9),
  }));
}
export const bekle = (sa) => (PENCERE_SA - (sa % PENCERE_SA)) % PENCERE_SA;

// yeniden=false: tek derleme, ayni cikti terfi eder. yeniden=true: her ortam kendi derlemesini yapar.
export function kostur(yeniden, aralik = ARALIK_SA) {
  const rnd = uretec(TOHUM), ciz = cizelge(rnd), ad = adaylar(rnd), kirmizi = uretec(TOHUM + 3);
  const s = { ozetAyni: 0, kaynakKacan: 0, olcekKacan: 0, gelMarj: 0, hazMarj: 0,
    ozguKacan: 0, sinananKacan: 0, tutulan: 0, yanlisTutulan: 0, farkliSurum: 0,
    dk: 0, bekleme: 0, bekleme1: 0, tepe: 0 };
  for (const a of ad) {
    const haz = yeniden ? coz(ciz, a.sa + 6) : coz(ciz, a.sa);
    const ur = yeniden ? coz(ciz, a.sa + 6 + aralik) : coz(ciz, a.sa);
    if (ozet(a.no, haz) === ozet(a.no, ur)) s.ozetAyni += 1;

    if (a.kaynak) {                                // kaynak kusuru her ciktida vardir
      const g = a.gd < GELISTIRME_YAKALA, h = a.hd < HAZIRLIK_YAKALA;
      if (g || h) s.tutulan += 1; else s.kaynakKacan += 1;
      if (g && !h) s.gelMarj += 1;                 // yalniz gelistirme kapisinda
      if (h && !g) s.hazMarj += 1;                 // yalniz hazirlik kapisinda
    }
    if (a.olcek) s.olcekKacan += 1;                // uretim olcegine bagli kusur: hicbir kapida gorunmez
    const sinanan = new Set(haz.map((v) => v.ad));
    s.farkliSurum += ur.filter((v) => !sinanan.has(v.ad)).length;
    for (const v of ur.filter((x) => x.kusur)) {
      if (!sinanan.has(v.ad)) s.ozguKacan += 1;    // uretimde var, sinanan ciktida yok
      else if (a.kd < HAZIRLIK_YAKALA) s.tutulan += 1;
      else s.sinananKacan += 1;
    }
    for (let k = 0; k < 2; k += 1) if (kirmizi() < KARARSIZ) s.yanlisTutulan += 1;

    s.dk += (yeniden ? 3 : 1) * DERLEME_DK + 2 * KAPI_DK;
    const gelis = a.sa + (yeniden ? DERLEME_DK / 60 : 0), b1 = bekle(gelis);
    const b2 = bekle(gelis + b1 + KAPI_DK / 60 + a.onay + (yeniden ? DERLEME_DK / 60 : 0));
    s.bekleme += b1 + b2; s.bekleme1 += b1; s.tepe = Math.max(s.tepe, b1 + b2);
  }
  return { ...s, ortBekleme: s.bekleme / ADAY };
}
```

Modelin tek anahtarı `yeniden`. Kapalıyken çıktı adayın hazır olduğu anda bir kez çözümlenir ve
üç ortamda da o çözümleme geçerlidir. Açıkken hazırlık kendi derlemesini altıncı saatte, üretim
yirmi dört saat sonra yapar. Kaynak her üç derlemede de aynıdır; değişen tek şey **ne zaman**
çözümlendiğidir.

#### İki Düzen, Aynı Aday Dizisi

```js
// kosum1.mjs — ayni aday dizisi iki terfi duzeninde kosturulur
import { kostur, ozet, cizelge, adaylar, uretec, coz, TOHUM, ADAY, ARALIK_SA } from './terfi.mjs';

console.log(`tohum ${TOHUM} | ${ADAY} aday, 8 bagimlilik, uc ortam, uretim derlemesi hazirliktan ${ARALIK_SA} sa sonra`);
console.log('duzen           ozet   farkli  ortama  sinanip  kaynak  olcek   yanlis  hat    bekleme  tepe');
console.log('                ayni   surum   ozgu    kacan    kacan   kacan   tutulan dk     ort/sa   sa');
for (const [ad, y] of [['ayni cikti', false], ['yeniden derle', true]]) {
  const s = kostur(y);
  console.log(`${ad.padEnd(15)} ${`${s.ozetAyni}/${ADAY}`.padEnd(6)} ${String(s.farkliSurum).padEnd(7)} ` +
    `${String(s.ozguKacan).padEnd(7)} ${String(s.sinananKacan).padEnd(8)} ${String(s.kaynakKacan).padEnd(7)} ` +
    `${String(s.olcekKacan).padEnd(7)} ${String(s.yanlisTutulan).padEnd(7)} ${String(s.dk).padEnd(6)} ` +
    `${s.ortBekleme.toFixed(2).padEnd(8)} ${s.tepe.toFixed(2)}`);
}
const t = kostur(false);
console.log(`\nterfi kapilarinin marjinal degeri (iki duzende de ayni kaynak kusuru kumesi):`);
console.log(`  yalniz gelistirme kapisinda yakalanan: ${t.gelMarj}`);
console.log(`  yalniz hazirlik kapisinda yakalanan:   ${t.hazMarj}`);
console.log(`  gelistirme kapisinin faturasi: ${ADAY * 18} dk kapi suresi + ${t.bekleme1.toFixed(1)} sa pencere beklemesi`);

const rnd = uretec(TOHUM), ciz = cizelge(rnd), ad = adaylar(rnd);
const a = ad.find((x) => ozet(x.no, coz(ciz, x.sa + 6)) !== ozet(x.no, coz(ciz, x.sa + 6 + ARALIK_SA)));
const h = coz(ciz, a.sa + 6), u = coz(ciz, a.sa + 6 + ARALIK_SA);
console.log(`\naday ${a.no} (yeniden derleyen duzen):`);
console.log(`  hazirlikta sinanan cikti: ${ozet(a.no, h)}`);
console.log(`  uretime giden cikti:      ${ozet(a.no, u)}`);
console.log(`  ayrisan bagimlilik: ${h.map((v, i) => v.ad === u[i].ad ? null : `${v.ad} -> ${u[i].ad}`).filter(Boolean).join(', ')}`);
```

```
tohum 20260902 | 40 aday, 8 bagimlilik, uc ortam, uretim derlemesi hazirliktan 24 sa sonra
duzen           ozet   farkli  ortama  sinanip  kaynak  olcek   yanlis  hat    bekleme  tepe
                ayni   surum   ozgu    kacan    kacan   kacan   tutulan dk     ort/sa   sa
ayni cikti      40/40  0       0       5        3       4       5       2320   3.93     6.70
yeniden derle   9/40   56      2       4        3       4       5       4080   4.19     6.97

terfi kapilarinin marjinal degeri (iki duzende de ayni kaynak kusuru kumesi):
  yalniz gelistirme kapisinda yakalanan: 1
  yalniz hazirlik kapisinda yakalanan:   7
  gelistirme kapisinin faturasi: 720 dk kapi suresi + 59.0 sa pencere beklemesi

aday 2 (yeniden derleyen duzen):
  hazirlikta sinanan cikti: 52d6a529d9a3
  uretime giden cikti:      1f9f016e7cfb
  ayrisan bagimlilik: b6s1 -> b6s2
```

İlk sütun dersin çekirdeğidir. Aynı çıktının terfi ettiği düzende kırk adayın kırkında üretimdeki
nesne hazırlıkta sınanan nesnedir. Yeniden derleyen düzende bu sayı dokuza iner: **otuz bir
adayda üretim, hiçbir kapının görmediği bir çıktıyı çalıştırdı**. Ayrışan bağımlılık sürümü sayısı
elli altı — kırk aday boyunca elli altı kez, üretime giden çıktıdaki bir bağımlılık hazırlıkta
sınanan çıktıdakinden başkaydı.

Bu ayrışmanın zararı üçüncü sütunda: iki ortama özgü kusur. Sayı küçük görünüyor, ama niteliği
başka. Sınanıp kaçan dört kusur, kapının **baktığı ve göremediği** kusurdur; kapının yakalama
oranı yükselirse azalır. İki ortama özgü kusur ise kapının **bakmadığı** kusurdur — o sürüm hiçbir
ortamda çalıştırılmadı. Kapının yakalama oranını yüzde yüze çıkarmak bu ikisini sıfırlamaz.

Kaynak kusuru ve ölçek kusuru sütunları iki düzende de aynı: üç ve dört. Bu beklenen sonuçtur ve
ölçümün tutarlılık kontrolüdür. Terfi düzeni, kaynağın kendi kusurunu ne azaltır ne çoğaltır;
yalnız sınanan nesne ile çalışan nesne arasındaki bağı kurar ya da koparır.

Alttaki blok tek bir adayı gösteriyor. İkinci adayın hazırlıkta sınanan çıktısının özeti
`52d6a529d9a3`, üretime gidenin `1f9f016e7cfb`. Kaynak aynı, aday aynı, tek fark altıncı
bağımlılığın `b6s1` yerine `b6s2` sürümüne çözülmesi. Hiç kimse bir karar vermedi; yalnız yirmi
dört saat geçti.

#### Ayrışma Neyin İşlevi

```js
// kosum2.mjs — hazirlik derlemesi ile uretim derlemesi arasindaki aralik taranir
import { kostur, ADAY } from './terfi.mjs';

console.log('aralik  ozet    ayrisan  ortama  sinanip  hat    bekleme');
console.log('(sa)    ayni    surum    ozgu    kacan    dk     ort/sa');
for (const a of [2, 6, 12, 24, 48, 96]) {
  const s = kostur(true, a);
  console.log(`${String(a).padEnd(7)} ${`${s.ozetAyni}/${ADAY}`.padEnd(7)} ${String(s.farkliSurum).padEnd(8)} ` +
    `${String(s.ozguKacan).padEnd(7)} ${String(s.sinananKacan).padEnd(8)} ${String(s.dk).padEnd(6)} ${s.ortBekleme.toFixed(2)}`);
}
console.log('\nayni ciktinin terfi ettigi duzende bu satirlarin hepsi: 40/40 ozet ayni, 0 ayrisan surum, 0 ortama ozgu');
```

```
aralik  ozet    ayrisan  ortama  sinanip  hat    bekleme
(sa)    ayni    surum    ozgu    kacan    dk     ort/sa
2       36/40   4        0       5        4080   4.19
6       24/40   18       0       4        4080   4.19
12      17/40   31       1       4        4080   4.19
24      9/40    56       2       4        4080   4.19
48      0/40    104      4       4        4080   4.19
96      0/40    192      8       1        4080   4.19

ayni ciktinin terfi ettigi duzende bu satirlarin hepsi: 40/40 ozet ayni, 0 ayrisan surum, 0 ortama ozgu
```

Tarama ayrışmanın neyin işlevi olduğunu söylüyor: **geçen zamanın**. İki saatlik aralıkta bile
kırk adayın dördü kimliğini kaybediyor. Kırk sekiz saatte hiçbir aday kimliğini korumuyor. Kimse
sürüm değiştirmiyor, kimse yapılandırmaya dokunmuyor; yalnız çözümleme iki kez yapılıyor ve
arasında dünya ilerliyor.

Son satır sessiz bir yer değiştirmeyi gösteriyor. Doksan altı saatlik aralıkta sınanıp kaçan
kusur beşten bire düşüyor — kapı daha iyi çalıştığı için değil, o kusurların artık **sınanmayan**
sütununa geçtiği için. Bir kapının kaçırdığı sayının düşmesi tek başına iyi haber değildir;
kapının baktığı küme küçülmüş de olabilir.

Hat dakikası ile bekleme sütunları tarama boyunca sabit kalıyor: dört bin seksen dakika ve aday
başına dört saat on bir dakika. Aralığı kısaltmak ne makine süresi kazandırıyor ne bekleme.
Yeniden derleyen düzende ayrışmayı azaltmanın tek yolu iki derlemeyi birbirine yaklaştırmaktır
ve o yaklaştırma sıfıra ulaştığında elde kalan şey zaten tek derlemedir. Yani tarama, iki düzenin
arasında bir orta yol olmadığını gösteriyor: aralık ne kadar küçülürse küçülsün ayrışma olasılığı
sıfırlanmıyor, sıfırlanan tek nokta ikinci derlemenin hiç yapılmadığı noktadır.

#### Kapının Üç Sayısı ve Kaldırılabilir Kapı

**Tuttuğu:** kırk adaylık dizide iki terfi kapısı birlikte on üç kaynak kusurundan onunu ve
sınanan çıktıdaki bağımlılık kusurlarının çoğunu tuttu. **Yalnız kendisinin tuttuğu:** hazırlık
kapısı yedi, geliştirme kapısı bir. **Kaçırdığı:** üç kaynak kusuru, dört ölçek kusuru, artı
yeniden derleyen düzende iki ortama özgü kusur. **Yanlış tuttuğu:** beş temiz aday. **Eklediği
süre:** aday başına ortalama üç saat elli altı dakika bekleme, tepe altı saat kırk iki dakika;
hat süresi iki bin üç yüz yirmi dakika, yeniden derleyen düzende dört bin seksen.

Geliştirme kapısının hesabı kendi kendini yazıyor: kırk adayda yalnız bir kusuru tek başına
yakaladı, karşılığında yedi yüz yirmi dakika kapı süresi ve elli dokuz saat pencere beklemesi
istedi. Hazırlık kapısı aynı süreye yedi kusur veriyor. Bu sayılarla geliştirme kapısını
kaldırmak savunulabilir bir karardır: kırk adayda bir kusurun daha geç yakalanması karşılığında
her aday bir pencere erken ilerler. Kararı veren şey kapının fikri değil, marjinali.

Aynı hesap yeniden derleyen düzen için başka bir sonuç veriyor. O düzende hazırlık kapısının
yedi kusurluk marjinali **sınadığı nesne için** geçerli; üretime giden nesne otuz bir adayda
başkası. Kapı çalışıyor, sayıları da iyi — ama kanıtı taşıdığı nesneyle birlikte gitmiyor.
Kaldırılması gereken şey kapı değil, kapının kanıtını geçersiz kılan ikinci derlemedir.

#### Özet

- Aynı çıktının terfi ettiği düzende kırk adayın kırkında üretim, hazırlıkta sınanan nesneyi
  çalıştırdı; yeniden derleyen düzende bu sayı dokuz, ayrışan bağımlılık sürümü elli altı oldu.
- Ayrışma iki ortama özgü kusur üretti: kapının bakmadığı, dolayısıyla yakalama oranı yükselerek
  azalmayacak bir kusur sınıfı.
- Kaynak kusuru üç, ölçek kusuru dört: iki düzende de aynı. Terfi düzeni kaynağın kusurunu
  değiştirmez, yalnız sınanan nesne ile çalışan nesne arasındaki bağı kurar ya da koparır.
- Ayrışma zamanın işlevidir: iki saatlik aralıkta kırk adayın dördü, kırk sekiz saatlik aralıkta
  hepsi kimliğini kaybetti. Doksan altı saatte kaçan kusur sayısının düşmesi kapının iyileşmesi
  değil, baktığı kümenin küçülmesidir.
- Geliştirme terfi kapısı kırk adayda yalnız bir kusuru tek başına yakaladı ve yedi yüz yirmi
  dakika ile elli dokuz saat pencere beklemesi istedi; bu sayılarla kaldırılması savunulabilir.
- Terfi kapısının bekletme faturası aday başına ortalama üç saat elli altı dakika, tepe altı saat
  kırk iki dakikadır; yeniden derleyen düzen buna aday başına iki fazladan derleme ekler.

#### Sonraki Adım

Bu ölçümde üretime terfi tek bir karardı: çıktı ya geçti ya geçmedi, geçtiğinde de tamamına açıldı.
Oysa terfi tek adımda olmak zorunda değil. Yeni sürüm önce isteklerin küçük bir yüzdesine
açılabilir, bir süre izlenebilir, sonra yüzde artırılabilir. Bu, kapıyı tek karardan bir karar
dizisine dönüştürür ve yeni bir soru açar: yüzdeyi artırma kararını hangi ölçüt veriyor, o ölçüt
ne kadarlık bir pencerede toplanıyor ve eşiği nedir. Sonraki ders bu üç ayarı tarar; ölçüt
gürültülüyken temiz bir sürümün durdurulmasını da sayar.

### Aşamalı Yayın

Source: https://academia.sh/tr/kurslar/ci-cd/asamali-yayin

Yüzdelik dağıtımın karar tarafı ölçülür: yüzdeyi artıran ölçüt, ölçüt penceresi ve karar eşiği taranır. Üç merdivende belirgin ve silik kusurun durdurulma oranı, etkilenen istek, temiz sürümün yanlış geri alınması ve yayının toplam süresi sayılır.

Terfi kapısı tek bir karardı: çıktı geçti ya da geçmedi, geçtiğinde tamamına açıldı. Terfi tek
adımda olmak zorunda değil. Yeni sürüm önce isteklerin küçük bir yüzdesine açılabilir, bir süre
izlenebilir, sonra pay artırılabilir. Bu, kapıyı tek karardan bir **karar dizisine** dönüştürür.

Dağıtımın kendisi — payın nasıl kaydırıldığı, kaç kopyanın hangi sürümde durduğu, kaçan hatanın
kaç isteğe dokunduğu — ölçülmüş bir konudur ve burada tekrarlanmıyor; bu dersin girdisidir.
Buranın sorusu bir adım geridedir: **payı kim, neye bakarak artırıyor.** Her adımın sonunda bir
ölçüt okunur, bir eşikle karşılaştırılır ve iki karardan biri verilir: yüzdeyi artır ya da geri
al. Kapı budur. Ölçülecek şey de bu kapının üç sayısıdır — hangi kusuru tutuyor, hangi temiz
sürümü boşuna durduruyor, yayına kaç dakika ekliyor.

- **YS13.** Kurgusal bölgesel ölçüm ağının faturalama servisi dakikada beş yüz istek alır. Günün
  saatine göre değişim, bölge farkı ve istek karması modellenmiyor. Ağ da rakamlar da kurgudur.
- **YS14.** Bozuk sürüm terfi kapılarını geçmiştir; kusuru yalnız üretim trafiğinde görünür. İki
  şiddet ölçülür: **belirgin** kusur isteklerin yüzde bir virgül altısında, **silik** kusur yüzde
  sekiz onda birinde hata verir. Tabandaki hata oranı yüzde dört onda birdir.
- **YS15.** Ölçüt tek kalemdir: yeni sürümün hata oranı. Gecikme, doygunluk ve iş ölçütleri ile
  çok kalemli karar modelin dışındadır.
- **YS16.** Pencere adımın sonunda bir kez okunur. Kayan pencere, ardışık ihlal sayacı ve ölçüt
  toplama gecikmesi modellenmiyor.
- **YS17.** Geri alma anında ve bedelsizdir. Geri almanın tur maliyeti bu dersin konusu değildir.
- **YS18.** Her ayar iki yüz kez koşturulur; oranlar bu iki yüz koşumun payıdır. Tohum görünürdür.

#### Ölçüm Düzeneği

Ölçülen şey gerçek bir yayın değil, `node` ile kurulan bir modeldir. Model bir merdiveni adım adım
tırmanır, her adımda pencereye düşen istekleri tek tek üretir ve adımın sonunda kararı verir.
Rastgelelik üreteci kendi yazılmıştır, tohumu görünürdür.

```js
// yayin.mjs — asamali yayinin karar tarafinin modeli; gercek trafik degil, bir modeldir
export const TOHUM = 20260903;
export const ISTEK_DK = 500, TABAN = 0.004, BELIRGIN = 0.016, SILIK = 0.008;
export const PENCERE = 10, CARPAN = 2.5, TEKRAR = 200;

export function uretec(tohum) {                    // dogrusal eslesik uretec, tohum gorunur
  let x = tohum >>> 0;
  return () => { x = (Math.imul(x, 1664525) + 1013904223) >>> 0; return x / 4294967296; };
}

export const merdivenler = {                       // yuzde adimlari
  hizli: [5, 50, 100], dengeli: [1, 5, 25, 50, 100], dikkatli: [1, 2, 5, 10, 25, 50, 100],
};

// Bir yayin: merdiven adim adim tirmanir, her adimin sonunda olcut penceresi okunur.
// Karar kapisi buradadir: pencerede gorulen hata orani esigi asarsa geri al, asmazsa yuzdeyi artir.
export function kosum(rnd, merdiven, pencere, esik, oran) {
  let dokunulan = 0;
  for (let i = 0; i < merdiven.length; i += 1) {
    const n = Math.round(ISTEK_DK * pencere * merdiven[i] / 100);
    let h = 0;
    for (let r = 0; r < n; r += 1) if (rnd() < oran) h += 1;
    dokunulan += n;
    if (n > 0 && h / n > esik) return { karar: 'geri al', adim: i + 1, dokunulan };
  }
  return { karar: 'tamam', adim: merdiven.length, dokunulan };
}

export function kostur(merdiven, oran, pencere = PENCERE, carpan = CARPAN) {
  const esik = TABAN * carpan, rnd = uretec(TOHUM);
  let durdurulan = 0, adim = 0, dokunulan = 0;
  const dagilim = merdiven.map(() => 0);
  for (let k = 0; k < TEKRAR; k += 1) {
    const s = kosum(rnd, merdiven, pencere, esik, oran);
    if (s.karar === 'geri al') { durdurulan += 1; adim += s.adim; dagilim[s.adim - 1] += 1; }
    dokunulan += s.dokunulan;
  }
  return { dagilim, oran: durdurulan / TEKRAR, ortAdim: durdurulan ? adim / durdurulan : 0,
    ortDokunulan: Math.round(dokunulan / TEKRAR), sure: merdiven.length * pencere, esik };
}
```

Aynı `kostur` üç ayrı sürüm için çağrılır. Belirgin ve silik kusurlu sürümlerde `oran` sonucu
kapının **tuttuğu**dur; taban oranıyla çağrıldığında aynı sayı kapının **yanlış tuttuğu**dur —
çünkü o sürümde kusur yoktur.

#### Üç Merdiven

```js
// kosum1.mjs — uc merdiven, uc surum: belirgin kusurlu, silik kusurlu, temiz
import { kostur, merdivenler, TOHUM, ISTEK_DK, TABAN, BELIRGIN, SILIK, PENCERE, CARPAN, TEKRAR } from './yayin.mjs';

console.log(`tohum ${TOHUM} | ${ISTEK_DK} istek/dk, pencere ${PENCERE} dk, ${TEKRAR} tekrar`);
console.log(`taban hata orani %${TABAN * 100} | belirgin kusur %${BELIRGIN * 100} | silik kusur %${SILIK * 100}`);
console.log(`karar esigi: tabanin ${CARPAN} kati, yani %${TABAN * CARPAN * 100}\n`);
console.log('merdiven  yuzde adimlari        belirgin kusur        silik kusur      temiz surum  yayin');
console.log('                                durdu  adim  etkilen  durdu  etkilen  yanlis durdu suresi');
for (const [ad, m] of Object.entries(merdivenler)) {
  const b = kostur(m, BELIRGIN), s = kostur(m, SILIK), t = kostur(m, TABAN);
  console.log(`${ad.padEnd(9)} ${m.join('-').padEnd(20)} ${`%${(b.oran * 100).toFixed(0)}`.padEnd(6)} ` +
    `${b.ortAdim.toFixed(2).padEnd(5)} ${String(b.ortDokunulan).padEnd(8)} ${`%${(s.oran * 100).toFixed(0)}`.padEnd(6)} ` +
    `${String(s.ortDokunulan).padEnd(8)} ${`%${(t.oran * 100).toFixed(0)}`.padEnd(12)} ${b.sure} dk`);
}
console.log('\nadim basina olcut penceresine dusen istek ve gorulebilecek en kucuk sifir disi oran:');
for (const p of [1, 2, 5, 10, 25, 50, 100]) {
  const n = ISTEK_DK * PENCERE * p / 100;
  console.log(`  %${String(p).padStart(3)} pay: ${String(n).padStart(5)} istek/pencere, tek hata = %${(100 / n).toFixed(2)}` +
    ` -> esik %${(TABAN * CARPAN * 100).toFixed(1)} ${100 / n > TABAN * CARPAN * 100 ? 'ayirt edilemez' : 'ayirt edilebilir'}`);
}
```

```
tohum 20260903 | 500 istek/dk, pencere 10 dk, 200 tekrar
taban hata orani %0.4 | belirgin kusur %1.6 | silik kusur %0.8
karar esigi: tabanin 2.5 kati, yani %1

merdiven  yuzde adimlari        belirgin kusur        silik kusur      temiz surum  yayin
                                durdu  adim  etkilen  durdu  etkilen  yanlis durdu suresi
hizli     5-50-100             %100   1.23  825      %40    5375     %9           30 dk
dengeli   1-5-25-50-100        %100   1.60  306      %70    3423     %27          50 dk
dikkatli  1-2-5-10-25-50-100   %100   1.72  188      %82    2403     %32          70 dk

adim basina olcut penceresine dusen istek ve gorulebilecek en kucuk sifir disi oran:
  %  1 pay:    50 istek/pencere, tek hata = %2.00 -> esik %1.0 ayirt edilemez
  %  2 pay:   100 istek/pencere, tek hata = %1.00 -> esik %1.0 ayirt edilebilir
  %  5 pay:   250 istek/pencere, tek hata = %0.40 -> esik %1.0 ayirt edilebilir
  % 10 pay:   500 istek/pencere, tek hata = %0.20 -> esik %1.0 ayirt edilebilir
  % 25 pay:  1250 istek/pencere, tek hata = %0.08 -> esik %1.0 ayirt edilebilir
  % 50 pay:  2500 istek/pencere, tek hata = %0.04 -> esik %1.0 ayirt edilebilir
  %100 pay:  5000 istek/pencere, tek hata = %0.02 -> esik %1.0 ayirt edilebilir
```

Belirgin kusurda üç merdiven de tam not aldı: iki yüz koşumun iki yüzünde de sürüm durduruldu.
Ayrıştıkları yer etkilenen istek sayısı. Hızlı merdiven ilk adımını yüzde beşten açtığı için
sekiz yüz yirmi beş isteğe dokundu; dikkatli merdiven yüzde birden başlayıp yüz seksen sekizde
kaldı. Bedeli süre: otuz dakikaya karşı yetmiş dakika.

Silik kusur sütunu tabloyu çeviriyor. Hata oranı yüzde sekiz onda bir olan sürüm eşiğin
altındadır; kapı onu ancak gürültü eşiği aştırdığında yakalar. Hızlı merdiven bu sürümü on
koşumdan dördünde durdurdu, dikkatli merdiven sekizinde. Ama durduramadığında dokunduğu istek
sayısı hızlıda beş bin üç yüz yetmiş beş, dikkatlide iki bin dört yüz üç. Kapının kaçırdığı bir
kusurda merdivenin ayrıntısı hâlâ işe yarıyor — çünkü kaçırma anında bile pay küçük kalıyor.

Son sütun faturayı yazıyor. Dikkatli merdiven temiz bir sürümü on koşumdan üçünde durdurdu.
Yüzde otuz iki. Bunun neden böyle olduğunu alttaki tablo söylüyor: yüzde bir paya on dakikalık
pencerede elli istek düşer. Elli istekte görülebilecek en küçük sıfır olmayan hata oranı yüzde
ikidir — karar eşiğinin iki katı. O adımda tek bir taban hatası bile kapıyı tetikler. Kapı orada
sürümü ölçmüyor, gürültüyü ölçüyor.

#### Pencere ve Eşik

```js
// kosum2.mjs — dengeli merdivende olcut penceresi ve karar esigi ayri ayri taranir
import { kostur, merdivenler, TABAN, BELIRGIN, SILIK, PENCERE, CARPAN } from './yayin.mjs';
const m = merdivenler.dengeli;

console.log(`merdiven ${m.join('-')} | esik carpani ${CARPAN} sabit`);
console.log('pencere  belirgin durdu  silik durdu  temiz yanlis durdu  etkilenen  yayin suresi');
for (const p of [5, 10, 20, 40, 80]) {
  const b = kostur(m, BELIRGIN, p), s = kostur(m, SILIK, p), t = kostur(m, TABAN, p);
  console.log(`${`${p} dk`.padEnd(8)} ${`%${(b.oran * 100).toFixed(0)}`.padEnd(15)} ${`%${(s.oran * 100).toFixed(0)}`.padEnd(12)} ` +
    `${`%${(t.oran * 100).toFixed(0)}`.padEnd(19)} ${String(b.ortDokunulan).padEnd(10)} ${b.sure} dk`);
}
console.log(`\npencere ${PENCERE} dk sabit | esik = taban x carpan`);
console.log('carpan  esik    belirgin durdu  silik durdu  temiz yanlis durdu  etkilenen');
for (const c of [1.5, 2, 2.5, 3, 4, 6]) {
  const b = kostur(m, BELIRGIN, PENCERE, c), s = kostur(m, SILIK, PENCERE, c), t = kostur(m, TABAN, PENCERE, c);
  console.log(`${String(c).padEnd(7)} ${`%${(TABAN * c * 100).toFixed(1)}`.padEnd(7)} ${`%${(b.oran * 100).toFixed(0)}`.padEnd(15)} ` +
    `${`%${(s.oran * 100).toFixed(0)}`.padEnd(12)} ${`%${(t.oran * 100).toFixed(0)}`.padEnd(19)} ${b.ortDokunulan}`);
}

const t2 = kostur(m, TABAN), b2 = kostur(m, BELIRGIN);
console.log(`\npencere ${PENCERE} dk, esik carpani ${CARPAN}: durusun hangi adimda oldugu`);
console.log(`  yuzde adimi:         ${m.map((x) => `%${x}`.padStart(6)).join('')}`);
console.log(`  temiz surum durdu:   ${t2.dagilim.map((x) => String(x).padStart(6)).join('')}`);
console.log(`  belirgin kusur durdu:${b2.dagilim.map((x) => String(x).padStart(6)).join('')}`);
```

```
merdiven 1-5-25-50-100 | esik carpani 2.5 sabit
pencere  belirgin durdu  silik durdu  temiz yanlis durdu  etkilenen  yayin suresi
5 dk     %100            %64          %20                 341        25 dk
10 dk    %100            %70          %27                 306        50 dk
20 dk    %100            %48          %9                  563        100 dk
40 dk    %100            %40          %5                  770        200 dk
80 dk    %100            %34          %2                  890        400 dk

pencere 10 dk sabit | esik = taban x carpan
carpan  esik    belirgin durdu  silik durdu  temiz yanlis durdu  etkilenen
1.5     %0.6    %100            %100         %53                 236
2       %0.8    %100            %91          %26                 306
2.5     %1.0    %100            %70          %27                 306
3       %1.2    %100            %45          %23                 565
4       %1.6    %94             %37          %19                 1373
6       %2.4    %31             %7           %2                  6333

pencere 10 dk, esik carpani 2.5: durusun hangi adimda oldugu
  yuzde adimi:             %1    %5   %25   %50  %100
  temiz surum durdu:       37    16     0     0     0
  belirgin kusur durdu:   100    79    21     0     0
```

Pencere taraması düz bir eğri vermiyor. Beş dakikadan on dakikaya çıkıldığında yanlış durdurma
yüzde yirmiden yirmi yediye **yükseliyor**, sonra düşüyor. Sebep karar kuralında: yüzde bir payda
pencere ne kadar küçükse o kadar az istek gelir, ama tek bir hata hâlâ eşiği aşar. Pencereyi
büyütmek önce "tek hata görme" olasılığını artırır; ancak pencere iki hata gerektirecek kadar
büyüdüğünde eğri döner. Yirmi dakikada yanlış durdurma yüzde dokuza, seksen dakikada yüzde ikiye
iner — karşılığında yayın süresi yüz dakikadan dört yüz dakikaya çıkar ve etkilenen istek beş yüz
altmış üçten sekiz yüz doksana yükselir. Beklemenin bedeli, beklerken dokunulan isteklerdir.

Eşik taraması kesin bir değiş tokuş gösteriyor. Çarpanı bir buçuğa indirmek silik kusurun
tamamını yakalatıyor, ama temiz sürümlerin yarısından fazlasını durduruyor. Altıya çıkarmak
yanlış durdurmayı yüzde ikiye indiriyor; bu kez belirgin kusurun bile yalnız üçte biri
yakalanıyor ve etkilenen istek altı bin üç yüz otuz üçe fırlıyor. İki ile iki buçuk arasındaki
satırlar dikkate değer: yanlış durdurma neredeyse aynı kalırken silik kusur yakalama yüzde
yetmişten doksan bire çıkıyor. Eşiği iki buçuktan ikiye çekmek karşılıksız bir kazançtır.

Bunun nedeni son bloktadır. Temiz sürümün elli üç yanlış duruşunun otuz yedisi ilk adımda, on
altısı ikinci adımda oldu; üçüncü adımdan sonra bir tane bile yok. Yanlış durdurmaları eşik değil
**küçük paylı adımlar** üretiyor. Eşiği oynatmak o adımları düzeltmiyor, çünkü orada ölçülen sayı
zaten eşikten bağımsız olarak sıçrıyor. Belirgin kusur satırı da aynı yerde yoğunlaşıyor: yüz
koşum ilk adımda, yetmiş dokuzu ikinci adımda durduruldu. Yani ilk iki adım hem kararların hem
yanlışların neredeyse tamamını üretiyor; sonraki üç adım yalnız süre ekliyor.

#### Kapının Üç Sayısı

**Tuttuğu:** belirgin kusurlu sürümü iki yüz koşumun iki yüzünde durdurdu. **Yalnız kendisinin
tuttuğu:** bu kusurların hepsi — bozuk sürüm tanım gereği terfi kapılarını geçmiş bir sürümdür ve
kusuru yalnız üretim trafiğinde görünür. Yüzde artırma kapısı, gerçek trafiği ölçüte çeviren ilk
kapıdır; marjinali bu yüzden yüzde yüzdür.

**Kaçırdığı:** silik kusurun merdivene göre yüzde on sekizi ile altmışı; eşik altıya çekildiğinde
belirgin kusurun bile yüzde altmış dokuzu. **Yanlış tuttuğu:** dengeli merdivende temiz
sürümlerin yüzde yirmi yedisi, dikkatli merdivende yüzde otuz ikisi; seksen dakikalık pencerede
yüzde ikiye iniyor. **Eklediği süre:** merdiven başına otuz, elli ve yetmiş dakika; pencere
büyütüldüğünde dört yüz dakikaya kadar.

Bu üç sayının hiçbiri kalıbın adından okunamaz. "Kanarya yaptık" cümlesi, kapının hangi ölçüte,
hangi pencereyle, hangi eşikle baktığını söylemedikçe boş bir cümledir — ve bu üç ayarın en
kötüsü, kararın alınamayacağı kadar küçük bir payda karar almaya çalışmaktır.

#### Özet

- Üç merdiven aynı kusur şiddetlerinde koşturuldu: belirgin kusuru üçü de iki yüz koşumda
  durdurdu, ama etkilenen istek sekiz yüz yirmi beşten yüz seksen sekize indi, süre otuz
  dakikadan yetmiş dakikaya çıktı.
- Silik kusurda durdurma oranı yüzde kırk ile seksen iki arasında değişti; kapının kaçırdığı
  durumda bile ayrıntılı merdiven dokunulan isteği beş bin üç yüz yetmiş beşten iki bin dört yüz
  üçe indirdi.
- Yanlış geri alma ölçüldü: dengeli merdivende yüzde yirmi yedi, dikkatlide yüzde otuz iki. Elli
  üç yanlış duruşun tamamı ilk iki adımda gerçekleşti, üçüncü adımdan sonra bir tane bile yok.
- Pencere taraması tek yönlü değil: beş dakikadan on dakikaya çıkmak yanlış durdurmayı artırdı,
  yirmiden sonra düşürdü; seksen dakikada yüzde iki, karşılığında yayın süresi dört yüz dakika.
- Eşik taramasında iki buçuk yerine iki çarpanı silik kusur yakalamasını yüzde yetmişten doksan
  bire çıkardı ve yanlış durdurmayı artırmadı; eşiği yükseltmek kaçırmayı hızla büyüttü.
- Yüzde bir paya on dakikalık pencerede elli istek düşer; tek hata yüzde iki okunur, eşik yüzde
  bir. O adımda kapı sürümü değil gürültüyü ölçer — küçük pay etkiyi sınırlar, kararı sağlamaz.

#### Sonraki Adım

Bu ölçümün tamamı bir varsayıma yaslandı: geri alma anında ve bedelsizdir. Kapı "geri al" dediği
anda modelde hiçbir şey olmuyordu. Gerçekte geri almanın bir turu, bir maliyeti ve bir sınırı
vardır. Eski sürüm hâlâ ayakta duruyorsa geri alma tek anahtar çevirme işidir; ayakta değilse
yeniden yayın demektir. Sonraki ders iki tam ortamı yan yana tutan düzeni ölçer: geri almanın tur
maliyetini, iki ortamı sürekli ayakta tutmanın kaynak bedelini, geçiş anında kesilen durum taşıyan
istekleri ve veri katmanı paylaşıldığında geri almanın gerçekte neyi geri almadığını sayar.

### İki Ortamlı Dağıtım

Source: https://academia.sh/tr/kurslar/ci-cd/iki-ortamli-dagitim

Geri almanın tur maliyeti ile iki tam ortamı ayakta tutmanın sürekli bedeli birlikte sayılır: geri alma penceresi taranır, geçiş anında kesilen oturum ve uzun süren iş ölçülür, paylaşılan veri katmanında geri almanın geri almadığı satırlar gösterilir.

Önceki ölçüm bir varsayıma yaslanıyordu: geri alma anında ve bedelsizdir. Kapı "geri al" dediği
anda modelde hiçbir şey olmuyordu. Bu ders o varsayımı kaldırır. Geri almanın bir turu, bir
fiyatı ve bir sınırı vardır — ve fiyatın büyük kısmı geri alma **hiç gerekmediğinde** de ödenir.

İki ortamlı dağıtımın vaadi tek cümledir: yeni sürüm eski sürümün yanında ayağa kalkar, trafik
tek anahtarla çevrilir, bir şey ters giderse anahtar geri çevrilir. Bu vaadin karşılığı, aynı işi
yapan iki tam ortamın bir süre birlikte ayakta durmasıdır. Bu derste kapı **geri alma
kararıdır**: kararın kaç tur sürdüğü, hangi durumda tek tura indiği ve o tek turun neyi
kapsamadığı ölçülür.

- **YS19.** Kurgusal bölgesel ölçüm ağının iş emri servisi on altı kopyayla çalışır; altmış yayın
  ölçülür ve bir dağıtım turu üç dakikadır. Ağ da rakamlar da kurgudur.
- **YS20.** Yayınların yüzde otuzu kusurludur. Kusurun görünme süresi üstel dağılır, ortalaması
  yirmi beş dakikadır. Kusurun hangi ölçütle görüldüğü ayrı bir ölçünün konusudur; burada yalnız
  görünme anı modellenir.
- **YS21.** İkinci ortam anahtar çevrilmeden on iki dakika önce ayağa kalkar ve geri alma
  penceresi boyunca ayakta kalır. Pencere kapandıktan sonra geri alma yeniden yayın demektir:
  beş tur.
- **YS22.** Aşamalı düzen dört fazladan kopyayla elli dakikada beş adım tırmanır; geri alma o ana
  kadar tırmanılan adım kadar tur sürer.
- **YS23.** Geçiş anında üç iş birimi sınıfı vardır ve süreleri üstel dağılır. Oturum
  yapışkanlığının yeni ortama taşınması modellenmiyor; ölçülen şey taşınmadığında kesilenin
  sayısıdır.
- **YS24.** Veri katmanı iki ortam arasında paylaşılır. Yazma hızı dakikada yüz kırk satır, geri
  alma sonrası satırların yüzde otuz beşi güncellenir. Göç sırası ve geriye uyumlu yazma bu
  dersin konusu değildir.

#### Ölçüm Düzeneği

Ölçülen şey gerçek bir küme değil, `node` ile kurulan bir modeldir. Model üç ayrı şeyi taşır:
bir yayın dizisinin kaynak bedeli ve geri alma turları, anahtar çevrildiği anda uçuşta olan iş
birimleri ve paylaşılan veri katmanına yazılan satırlar. Rastgelelik üreteci kendi yazılmıştır,
tohumu görünürdür.

```js
// ikiortam.mjs — iki ortamli dagitimin maliyet ve gecis modeli; gercek kume degil, bir modeldir
export const TOHUM = 20260904;
export const KOPYA = 16, YAYIN = 60, TUR_DK = 3, HAZIRLIK_DK = 12, YENIDEN_TUR = 5;
export const ASAMALI_EK = 4, ASAMALI_SURE = 50, ASAMALI_ADIM = 5;
export const KUSUR_ORT = 25, PENCERE_DK = 30;     // kusurun gorunme suresi ust ustel dagilir

export function uretec(tohum) {                    // dogrusal eslesik uretec, tohum gorunur
  let x = tohum >>> 0;
  return () => { x = (Math.imul(x, 1664525) + 1013904223) >>> 0; return x / 4294967296; };
}
export const ustel = (rnd, ort) => -ort * Math.log(1 - rnd());

// Yayin dizisi: her yayinin kusurlu olup olmadigi ve kusurun kacinci dakikada gorundugu.
export function yayinlar(rnd, bozukOran = 0.30) {
  return Array.from({ length: YAYIN }, (_, i) => ({
    no: i + 1, bozuk: rnd() < bozukOran, gorunme: ustel(rnd, KUSUR_ORT),
  }));
}

// Iki ortamli duzen: eski ortam anahtar cevrildikten sonra pencere boyunca ayakta tutulur.
export function ikiOrtamli(liste, pencere) {
  let kopyaDk = 0, tur = 0, pencereIci = 0, pencereDisi = 0;
  for (const y of liste) {
    kopyaDk += KOPYA * (HAZIRLIK_DK + pencere);    // ikinci ortamin ayakta durdugu sure
    if (!y.bozuk) continue;
    if (y.gorunme <= pencere) { tur += 1; pencereIci += 1; }
    else { tur += YENIDEN_TUR; pencereDisi += 1; }
  }
  const bozuk = liste.filter((y) => y.bozuk).length;
  return { ad: `iki ortamli (pencere ${pencere} dk)`, kopyaDk, pencereIci, pencereDisi,
    ortTur: bozuk ? tur / bozuk : 0, geriDk: bozuk ? tur * TUR_DK / bozuk : 0 };
}

// Asamali duzen: fazladan kopya yayin suresince ayakta; geri alma o ana kadar tirmanilan adim kadar tur.
export function asamali(liste) {
  let kopyaDk = 0, tur = 0;
  for (const y of liste) {
    kopyaDk += ASAMALI_EK * ASAMALI_SURE;
    if (!y.bozuk) continue;
    const adim = Math.min(ASAMALI_ADIM, 1 + Math.floor(y.gorunme / (ASAMALI_SURE / ASAMALI_ADIM)));
    tur += adim;
  }
  const bozuk = liste.filter((y) => y.bozuk).length;
  return { ad: 'asamali (tek ortam)', kopyaDk, pencereIci: 0, pencereDisi: bozuk,
    ortTur: bozuk ? tur / bozuk : 0, geriDk: bozuk ? tur * TUR_DK / bozuk : 0 };
}

// Gecis ani: anahtar cevrildiginde ucusta olan is birimleri. Uc sinif, ust ustel sure.
export const SINIFLAR = [
  { ad: 'kisa istek', hiz: 300, ort: 0.04 },       // hiz: dakikada baslayan birim
  { ad: 'oturum', hiz: 8, ort: 12 },
  { ad: 'uzun suren is', hiz: 0.5, ort: 25 },
];

export function ucustakiler(rnd, sinif, T = 240) {  // T dakikalik isinma, anahtar T aninda
  const acik = [];
  for (let dk = 0; dk < T; dk += 1) {
    for (let k = 0; k < sinif.hiz; k += 1) {
      const bas = dk + rnd(), sure = ustel(rnd, sinif.ort);
      if (bas + sure > T) acik.push({ bas, bitis: bas + sure });
    }
  }
  return acik;
}

// Paylasilan veri katmani: yeni surum yeni bicimde yazar, geri alma kodu geri alir, satiri almaz.
export function veriKatmani(rnd, acikDk, yazmaHizi = 140, gunceleme = 0.35) {
  let yeniBicim = 0, sonraGuncellenen = 0;
  for (let dk = 0; dk < acikDk; dk += 1) {
    for (let k = 0; k < yazmaHizi; k += 1) {
      yeniBicim += 1;
      if (rnd() < gunceleme) sonraGuncellenen += 1;  // geri alma sonrasi eski surumce guncellenen
    }
  }
  return { yeniBicim, kayip: sonraGuncellenen, sessiz: yeniBicim - sonraGuncellenen };
}
```

Modelin ayarı `pencere`: eski ortamın anahtar çevrildikten sonra kaç dakika daha ayakta
tutulacağı. Bu tek sayı hem geri almanın tur maliyetini hem de düzenin sürekli bedelini
belirler ve ikisini ters yönde belirler.

#### Kaynak Bedeli ve Geri Alma Turu

```js
// kosum1.mjs — iki duzenin kaynak bedeli ve geri alma tur maliyeti; geri alma penceresi taranir
import { uretec, yayinlar, ikiOrtamli, asamali, TOHUM, KOPYA, YAYIN, TUR_DK, PENCERE_DK, KUSUR_ORT } from './ikiortam.mjs';

const l = yayinlar(uretec(TOHUM));
const bozuk = l.filter((y) => y.bozuk).length;
console.log(`tohum ${TOHUM} | ${YAYIN} yayin, ${KOPYA} kopya, ${bozuk} yayin kusurlu`);
console.log(`kusurun gorunme suresi ust ustel, ortalama ${KUSUR_ORT} dk | bir tur ${TUR_DK} dk\n`);
console.log('duzen                        kopya-dk  pencere ici  pencere disi  ort tur  ort geri alma');
for (const s of [ikiOrtamli(l, PENCERE_DK), asamali(l)]) {
  console.log(`${s.ad.padEnd(28)} ${String(s.kopyaDk).padEnd(9)} ${String(s.pencereIci).padEnd(12)} ` +
    `${String(s.pencereDisi).padEnd(13)} ${s.ortTur.toFixed(2).padEnd(8)} ${s.geriDk.toFixed(1)} dk`);
}
console.log('\ngeri alma penceresi taranir (iki ortamli duzen):');
console.log('pencere  kopya-dk  tek anahtarla  yeniden yayinla  ort tur  ort geri alma  fazladan kopya-dk');
for (const p of [0, 5, 15, 30, 60, 120, 240]) {
  const s = ikiOrtamli(l, p), a = asamali(l);
  console.log(`${`${p} dk`.padEnd(8)} ${String(s.kopyaDk).padEnd(9)} ${String(s.pencereIci).padEnd(14)} ` +
    `${String(s.pencereDisi).padEnd(16)} ${s.ortTur.toFixed(2).padEnd(8)} ${`${s.geriDk.toFixed(1)} dk`.padEnd(14)} ${s.kopyaDk - a.kopyaDk}`);
}
```

```
tohum 20260904 | 60 yayin, 16 kopya, 18 yayin kusurlu
kusurun gorunme suresi ust ustel, ortalama 25 dk | bir tur 3 dk

duzen                        kopya-dk  pencere ici  pencere disi  ort tur  ort geri alma
iki ortamli (pencere 30 dk)  40320     11           7             2.56     7.7 dk
asamali (tek ortam)          12000     0            18            2.78     8.3 dk

geri alma penceresi taranir (iki ortamli duzen):
pencere  kopya-dk  tek anahtarla  yeniden yayinla  ort tur  ort geri alma  fazladan kopya-dk
0 dk     11520     0              18               5.00     15.0 dk        -480
5 dk     16320     3              15               4.33     13.0 dk        4320
15 dk    25920     8              10               3.22     9.7 dk         13920
30 dk    40320     11             7                2.56     7.7 dk         28320
60 dk    69120     17             1                1.22     3.7 dk         57120
120 dk   126720    18             0                1.00     3.0 dk         114720
240 dk   241920    18             0                1.00     3.0 dk         229920
```

Üst tablo vaadi sınırlıyor. Otuz dakikalık pencerede on sekiz kusurlu yayının yalnız on biri tek
anahtarla geri alındı; yedisi pencere kapandıktan sonra görüldü ve beş turluk yeniden yayın
istedi. Ortalama geri alma turu iki virgül elli altı, aşamalı düzende iki virgül yetmiş sekiz.
İki düzen arasındaki fark bu ayarda yüzde sekiz. Karşılığında iki ortamlı düzen üç kat kaynak
tüketti: kırk bin üç yüz yirmi kopya-dakikaya karşı on iki bin.

"Anında geri alma" cümlesi, pencerenin kusurun görünme süresinden uzun tutulmasıyla sayıya
dönüşüyor. Tarama fiyatı gösteriyor. Altmış dakikalık pencerede on sekiz kusurun on yedisi tek
turda geri alındı, ortalama tur bir virgül yirmi ikiye indi — fazladan elli yedi bin yüz yirmi
kopya-dakika. Yüz yirmi dakikada tamamı tek tura indi, fazladan bedel yüz on dört bin yedi yüz
yirmi kopya-dakika. Kazanılan her tur bir fiyat taşıyor: otuz dakikalık pencerede kırk dört tur
kısaldı ve bunun bedeli tur başına altı yüz elli beş kopya-dakika oldu.

İlk satır ters yönden aynı şeyi söylüyor. Pencere sıfır olduğunda iki ortamlı düzen aşamalı
düzenden **ucuz** (on bir bin beş yüz yirmiye karşı on iki bin), çünkü ikinci ortam yalnız on iki
dakikalık hazırlık için ayakta. Ama o düzende hiçbir yayın tek anahtarla geri alınamadı; ortalama
tur beşe çıktı. Eski ortamı kapatmakla iki ortamlı dağıtımın tek gerekçesi de kapanıyor.

#### Geçiş Anında Kesilen İş

```js
// kosum2.mjs — gecis aninda kesilen durum tasiyan istekler ve paylasilan veri katmani
import { uretec, ucustakiler, veriKatmani, SINIFLAR, TOHUM, KOPYA } from './ikiortam.mjs';

const acik = SINIFLAR.map((s) => ({ s, liste: ucustakiler(uretec(TOHUM + s.hiz), s) }));
console.log('anahtar cevrildigi anda ucusta olan is birimleri:');
for (const { s, liste } of acik) console.log(`  ${s.ad.padEnd(15)} ${String(liste.length).padStart(5)} birim (ortalama sure ${s.ort} dk)`);

console.log('\nbosaltma suresi taranir: sure sonunda hala calisan birim kesilir');
console.log('bosaltma  kisa istek  oturum  uzun suren is  kesilen toplam  iki ortam birlikte');
for (const d of [0, 0.5, 1, 5, 15, 30]) {
  const k = acik.map(({ liste }) => liste.filter((u) => u.bitis > 240 + d).length);
  console.log(`${`${d} dk`.padEnd(9)} ${String(k[0]).padEnd(11)} ${String(k[1]).padEnd(7)} ${String(k[2]).padEnd(14)} ` +
    `${String(k[0] + k[1] + k[2]).padEnd(15)} ${2 * KOPYA * d} kopya-dk`);
}

const v = veriKatmani(uretec(TOHUM + 11), 11);
console.log('\npaylasilan veri katmani: anahtar cevrildi, 11. dakikada geri alindi');
console.log(`  yeni bicimde yazilan satir: ${v.yeniBicim}`);
console.log(`  geri alma turu: 1 (kod) | veri icin geri alinan satir: 0`);
console.log(`  geri alma sonrasi eski surumce guncellenip yeni alani silinen satir: ${v.kayip}`);
console.log(`  yeni alani duran, eski surumun gormedigi satir: ${v.sessiz}`);
```

```
anahtar cevrildigi anda ucusta olan is birimleri:
  kisa istek         10 birim (ortalama sure 0.04 dk)
  oturum             89 birim (ortalama sure 12 dk)
  uzun suren is      24 birim (ortalama sure 25 dk)

bosaltma suresi taranir: sure sonunda hala calisan birim kesilir
bosaltma  kisa istek  oturum  uzun suren is  kesilen toplam  iki ortam birlikte
0 dk      10          89      24             123             0 kopya-dk
0.5 dk    0           88      24             112             16 kopya-dk
1 dk      0           86      24             110             32 kopya-dk
5 dk      0           68      20             88              160 kopya-dk
15 dk     0           31      14             45              480 kopya-dk
30 dk     0           7       5              12              960 kopya-dk

paylasilan veri katmani: anahtar cevrildi, 11. dakikada geri alindi
  yeni bicimde yazilan satir: 1540
  geri alma turu: 1 (kod) | veri icin geri alinan satir: 0
  geri alma sonrasi eski surumce guncellenip yeni alani silinen satir: 491
  yeni alani duran, eski surumun gormedigi satir: 1049
```

Anahtar çevrildiği anda uçuşta yüz yirmi üç iş birimi vardı. Bunların onu kısa istek, seksen
dokuzu oturum, yirmi dördü uzun süren iş. Yarım dakikalık boşaltma kısa isteklerin tamamını
kurtardı — ve oturumların yalnız birini. Bir dakikalık boşaltma seksen dokuzu seksen altıya
indirdi, uzun süren işlerin hiçbirine dokunmadı.

Sebep ölçek farkıdır. Boşaltma süresi kısa isteğin ömrüne göre ayarlanır; oturum on iki, uzun
süren iş yirmi beş dakika yaşar — iki-üç mertebe üstünde. Yüz yirmi üç birimin on ikiye inmesi
otuz dakikalık boşaltma istiyor ve o otuz dakika boyunca iki sürüm aynı veri katmanına birlikte
yazıyor. Kesintisiz geçişin bedeli yalnız dokuz yüz altmış kopya-dakika değil; iki sürümün aynı
anda yazdığı bir yarım saattir.

Bir hesap daha yapılmalı. Her geçiş seksen dokuz oturum kesiyorsa, **temiz bir sürümün boşuna
geri alınması** iki geçiş demektir: yüz yetmiş sekiz oturum ve kırk sekiz uzun süren iş. Yanlış
geri almanın bedeli, kusurun kendisinin bedelinden büyük olabilir — ve yanlış geri almanın kaç
kez olduğu, geri alma kararının değil ondan önceki kapının ayarına bağlıdır.

#### Geri Almanın Geri Almadığı

Son blok tek anahtarın sınırını gösteriyor. Anahtar çevrildi, on birinci dakikada kusur görüldü,
geri alma bir turda tamamlandı. Kod eski sürüme döndü. O on bir dakikada paylaşılan veri
katmanına bin beş yüz kırk satır yeni biçimde yazılmıştı; geri alma bunların **sıfırını** geri
aldı.

İki ayrı zarar var. Dört yüz doksan bir satır, geri almadan sonra eski sürüm tarafından
güncellendi; eski sürüm yeni alanı tanımadığı için o alan yazma sırasında düştü ve bilgi kalıcı
olarak kayboldu. Kalan bin kırk dokuz satır yeni alanını taşımaya devam ediyor; eski sürüm
onları görmüyor, dolayısıyla sessizler — ama yeni sürüm geri geldiğinde bu iki küme arasında
biçim ayrımı olacak.

İki ortamlı dağıtım **kodun** geri alınmasını tek tura indirir. Veri katmanı paylaşıldığında geri
alma kapsamı kodla sınırlıdır ve o sınır tur maliyetinde hiç görünmez: bir turluk geri alma, bin
beş yüz kırk satırlık bir kalıntı bırakabilir.

#### Kapının Üç Sayısı

**Tuttuğu:** on sekiz kusurlu yayının hepsi sonunda geri alındı. **Yalnız kendisinin tuttuğu:**
otuz dakikalık pencerede on bir yayının tek turda geri alınması — bu yalnız eski ortam ayaktayken
mümkündür ve toplam kırk dört tur, yani yüz otuz iki dakika kazandırdı. Marjinalin fiyatı tur
başına altı yüz elli beş kopya-dakikadır.

**Kaçırdığı:** pencere kapandıktan sonra görülen yedi yayın ve her geri almada veri katmanına
yazılmış satırlar — örnek yayında bin beş yüz kırk satır, dört yüz doksan biri kalıcı kayıp.
**Yanlış tuttuğu:** temiz bir sürümün geri alınması iki geçiş, yüz yetmiş sekiz oturum ve kırk
sekiz uzun süren iş demektir. **Eklediği süre:** yayın başına on iki dakika hazırlık artı pencere
süresi; kesintisiz geçiş için otuz dakikaya kadar boşaltma; toplam kırk bin üç yüz yirmi
kopya-dakika.

#### Özet

- Otuz dakikalık pencerede on sekiz kusurlu yayının on biri tek anahtarla, yedisi beş turluk
  yeniden yayınla geri alındı; ortalama tur iki virgül elli altı, aşamalı düzende iki virgül
  yetmiş sekiz.
- Kaynak bedeli üç kat: kırk bin üç yüz yirmi kopya-dakikaya karşı on iki bin. Pencereyi yüz
  yirmi dakikaya çıkarmak tüm yayınları tek tura indiriyor, fazladan bedeli yüz on dört bin yedi
  yüz yirmi kopya-dakika.
- Pencere sıfır olduğunda iki ortamlı düzen aşamalıdan ucuz çıkıyor ama tek anahtarla geri
  alınabilen yayın kalmıyor: eski ortamı kapatmak kalıbın gerekçesini de kapatıyor.
- Geçiş anında yüz yirmi üç iş birimi uçuştaydı. Yarım dakikalık boşaltma kısa isteklerin
  tamamını kurtardı, seksen dokuz oturumun yalnız birini; on ikiye inmek otuz dakikalık boşaltma
  ve iki sürümün yarım saat birlikte yazması demek.
- Paylaşılan veri katmanında bir turluk geri alma bin beş yüz kırk satırın sıfırını geri aldı;
  dört yüz doksan biri eski sürümce güncellenip kalıcı olarak kayboldu.
- Temiz bir sürümün boşuna geri alınması iki geçiş, yüz yetmiş sekiz oturum ve kırk sekiz uzun
  süren iş kesintisi demektir; bu sayı geri alma kapısının değil, ondan önceki kapının ayarına
  bağlıdır.

#### Sonraki Adım

Bu dersin bütün ölçümleri tek bir zorunluluğa dayanıyordu: yeni davranışı açmak için yeni sürümü
trafiğe koymak gerekiyor, geri almak için de trafikten çekmek. Yayın ile davranışın açılması aynı
işlem olduğu sürece geri alma hep bir dağıtım işidir — turu, geçiş anı ve veri kalıntısı vardır.
Bu ikisi ayrılabilir. Davranış kodda taşınıp bir kayıtla kapalı tutulursa, açma ve kapama artık
dağıtım değil bir ayar değişikliğidir; geri alma tur maliyeti sıfıra yaklaşır. Sonraki ders bu
ayrımı ölçer: ayrımın kazandırdığı turu, açma kararının kendi kapısını ve kapalı kalan her
kaydın zamanla biriktirdiği borcu.

### Özellik Bayrakları

Source: https://academia.sh/tr/kurslar/ci-cd/ozellik-bayraklari

Aynı değişiklik dizisi bayraklı ve bayraksız iki düzende yayınlanır; geri alma biriminin kod mu davranış mı olduğu tur ve etkilenen fatura sayısıyla ölçülür. Üç bayrağın açtığı sekiz kod yolu sayılır, sınanan yol oranı çıkarılır ve bir dönemin bayrak borcu ölü kod yoluyla birlikte yazılır.

Önceki dersin iki ortamlı düzeninde geri alma tek anahtara indi: trafik eski ortama çevrilir
ve dakikalar içinde önceki davranışa dönülür. O anahtarın geri aldığı şey **kodun tamamıydı**.
Yayın bir sürümü taşır; sürüm içindeki her değişiklik aynı anda gider ve aynı anda geri gelir.
Oysa geri alınması gereken çoğu zaman tek bir davranıştır.

Özellik bayrağı bu birimi ayırır: kod üretime dağıtılır, davranış ayrı bir anahtarla açılır.
Bu dersin ölçüsü bayrağın var olup olmadığı değil, bu ayrılığın **hangi sayıyı değiştirdiğidir**.
Aynı değişiklik dizisi iki düzende koşturulur, geri alma birimi ve turu sayılır, bayrağın açtığı
kod yolları sayılır ve bir dönemin sonunda geriye kalan bayrak borcu yazılır.

- **YS25.** Bayrak değeri hat dışında, çalışan sürecin okuduğu bir ayar kaydında durur;
  değiştirilmesi yeni bir yayın gerektirmez. Ayarın bütün düğümlere yayılması bir tur sürer.
- **YS26.** Bayraklar birbirinden bağımsızdır: herhangi bir kombinasyon geçerli bir kod yoludur.
- **YS27.** Üretimdeki sapma, doğrulayıcı servisin uyuşmazlık raporundan görülür; rapor iki
  dağıtım turunda bir üretilir. Fark etme süresi iki düzende de aynıdır.
- **YS28.** Bayraksız düzende geri alma birimi bir sürümdür: sürüm içindeki bütün değişiklikler
  birlikte geri gider.
- **YS29.** Bayrak okuma noktası, koşulun kodda yazıldığı yerdir; bir bayrak birden çok noktada
  okunabilir ve temizleme maliyeti bu sayıyla orantılıdır.
- **YS30.** Kurgu, bölgesel ölçüm ağının faturalama servisidir: sayaç okumasından fatura tutarı
  üretilir. Servis de, defter de, aşağıdaki sayılar da kurgudur.

#### Geri Alma Birimi

Altı değişiklikten oluşan bir dönem yayınlanır. Üçüncüsü — gecikme faizi — kusurludur ve kusur
ancak sonraki değişiklikler de üretime çıktıktan sonra görülür. Bayraksız düzende değişiklikler
ikişerli üç yayına bölünür. Bayraklı düzende her değişiklik ayrı yayınlanır, kusurlu olan bayrak
arkasında dağıtılıp sonra açılır.

```js
// duzen.mjs — ayni degisiklik dizisi iki duzende yayinlanir; yayin duzeni MODELDIR

const DEGISIKLIK = ['tarife tablosu', 'kademeli tarife', 'gecikme faizi',
  'fatura ozet alani', 'is emri baglantisi', 'toplu is raporu'];
const KUSURLU = 2;        // gecikme faizi, kademe acikken sapiyor
const FATURA_TURU = 40;   // bir dagitim turunda hesaplanan fatura
const AYAR_TURU = 4;      // bir ayar yayilma turunda hesaplanan fatura
const FARK_ETME = 2;      // uyusmazlik raporunun kusuru gostermesi: iki dagitim turu

function bayraksiz() {
  const yayin = [[0, 1], [2, 3], [4, 5]];   // uc yayin, her yayin bir surum
  let surum = yayin.length, tur = 0;
  while (yayin.slice(0, surum).flat().includes(KUSURLU)) { surum -= 1; tur += 1; }
  const kalan = yayin.slice(0, surum).flat();
  const cikan = yayin.flat().filter((i) => !kalan.includes(i));
  return { ad: 'bayraksiz', birim: 'kod', tur, cikan,
    etkilenen: (FARK_ETME + tur) * FATURA_TURU };
}

function bayrakli() {
  // her degisiklik ayri yayinlandi; kusurlu olan bayrak arkasinda dagitilip acildi
  const bayrak = { 'gecikme faizi': 'acik' };
  let tur = 0;
  bayrak['gecikme faizi'] = 'kapali'; tur += 1;
  const cikan = Object.entries(bayrak).filter(([, d]) => d === 'kapali')
    .map(([ad]) => DEGISIKLIK.indexOf(ad));
  return { ad: 'bayrakli', birim: 'davranis', tur, cikan,
    etkilenen: FARK_ETME * FATURA_TURU + tur * AYAR_TURU };
}

console.log('duzen      geri alma birimi  tur  cikan  bunlardan saglam  etkilenen fatura');
for (const d of [bayraksiz(), bayrakli()]) {
  const saglam = d.cikan.filter((i) => i !== KUSURLU);
  console.log(`${d.ad.padEnd(10)} ${d.birim.padEnd(17)} ${String(d.tur).padEnd(4)} ` +
    `${String(d.cikan.length).padEnd(6)} ${String(saglam.length).padEnd(17)} ${d.etkilenen}`);
  console.log(`  uretimden cikan: ${d.cikan.map((i) => DEGISIKLIK[i]).join(', ')}`);
}
```

```
duzen      geri alma birimi  tur  cikan  bunlardan saglam  etkilenen fatura
bayraksiz  kod               2    4      3                 160
  uretimden cikan: gecikme faizi, fatura ozet alani, is emri baglantisi, toplu is raporu
bayrakli   davranis          1    1      0                 84
  uretimden cikan: gecikme faizi
```

Bayraksız düzende iki tur geri sarma gerekti, çünkü kusurlu değişiklik ortadaki sürümdeydi ve
ondan sonra bir sürüm daha yayınlanmıştı. Üretimden dört değişiklik çıktı; üçü sağlamdı. O üç
değişikliğin sahipleri kusurla ilgisiz kişilerdir ve işleri, kendi hataları olmayan bir nedenle
üretimden geri alınmıştır. Bayraklı düzende geri alınan tek şey bir davranıştır: sağlam
değişikliklerin hiçbiri üretimden çıkmadı.

Etkilenen fatura sayısındaki fark ise geri alma turunun uzunluğundan gelir. İki düzende de
fark etme aynı sürüyor — seksen fatura. Ayrılık ondan sonrasında: sürüm geri sarma iki dağıtım
turu, bayrak kapatma bir ayar yayılma turudur. Yüz altmışa karşı seksen dört. Bu farkın
tamamı geri alma yolunun kısalmasından gelir, kusurun daha erken görülmesinden değil.

#### Bayrağın Açtığı Kod Yolları

Bayrak bir koşuldur ve her koşul kodu ikiye ayırır. Üç bayrak sekiz kod yolu açar. Aşağıdaki
hesap gerçekten koşar: fatura tutarı üç bayrağın her kombinasyonu için ayrı ayrı hesaplanır ve
o kombinasyon için ayrı yazılmış beklenen davranışla karşılaştırılır.

```js
// bayrak.mjs — uc bayragin actigi kod yollari sayilir; faturalama hesabi MODELDIR
// Bolgesel olcum agi kurgusu: sayac okumasindan fatura tutari uretilir.

const TABAN = 12.4;
const KADEME = [[10, 12.4], [25, 18.6], [Infinity, 27.9]];
const OKUMA = [
  { no: 'S-0431', m3: 8, gecikmeAy: 0 }, { no: 'S-0432', m3: 8, gecikmeAy: 2 },
  { no: 'S-0433', m3: 17, gecikmeAy: 0 }, { no: 'S-0434', m3: 17, gecikmeAy: 1 },
  { no: 'S-0435', m3: 34, gecikmeAy: 3 }, { no: 'S-0436', m3: 52, gecikmeAy: 0 },
];
const BAYRAK = ['kademe', 'gecikme', 'ozet'];
const kurus = (x) => Math.round(x * 100) / 100;

function kademeli(m3) {
  let kalan = m3, alt = 0, tutar = 0;
  for (const [ust, birim] of KADEME) {
    const dilim = Math.min(kalan, ust - alt);
    tutar += dilim * birim; kalan -= dilim; alt = ust;
    if (kalan <= 0) break;
  }
  return tutar;
}

// Uretimdeki kod: her bayrak bir dallanma noktasi.
function uretim(o, b) {
  let tutar = b.kademe ? kademeli(o.m3) : o.m3 * TABAN;
  const taban = o.m3 * TABAN;
  if (b.gecikme && o.gecikmeAy > 0) tutar += taban * 0.015 * o.gecikmeAy;
  const f = { tutar: kurus(tutar) };
  if (b.ozet) f.ozet = { m3: o.m3, gecikmeAy: o.gecikmeAy };
  return f;
}

// Her kombinasyonun beklenen davranisi ayri yazilir: faiz kendi tutari uzerinden isler.
function beklenen(o, b) {
  let tutar = b.kademe ? kademeli(o.m3) : o.m3 * TABAN;
  if (b.gecikme && o.gecikmeAy > 0) tutar += tutar * 0.015 * o.gecikmeAy;
  return kurus(tutar);
}

const yollar = [];
for (let m = 0; m < 2 ** BAYRAK.length; m += 1) {
  const b = Object.fromEntries(BAYRAK.map((ad, i) => [ad, Boolean(m & (1 << i))]));
  const sapan = OKUMA.filter((o) => uretim(o, b).tutar !== beklenen(o, b));
  yollar.push({ maske: BAYRAK.map((ad) => (b[ad] ? '1' : '0')).join(''), b, sapan });
}

// Sinama kumesi: uretimin bugunku hali ve her bayrak tek basina.
const SINANAN = new Set(['000', '100', '010', '001']);
console.log('yol   kademe gecikme ozet  sapan fatura  sinandi');
for (const y of yollar) {
  console.log(`${y.maske}  ${BAYRAK.map((a) => (y.b[a] ? 'acik  ' : 'kapali')).join(' ')}` +
    `  ${String(y.sapan.length).padEnd(13)} ${SINANAN.has(y.maske) ? 'evet' : 'hayir'}`);
}
const kusurlu = yollar.filter((y) => y.sapan.length > 0);
console.log('');
console.log(`toplam kod yolu            : ${yollar.length}`);
console.log(`sinanan yol                : ${SINANAN.size} (oran ${(SINANAN.size / yollar.length * 100).toFixed(0)} yuzde)`);
console.log(`sapan davranis ureten yol  : ${kusurlu.length} — ${kusurlu.map((y) => y.maske).join(', ')}`);
console.log(`bunlardan sinanani         : ${kusurlu.filter((y) => SINANAN.has(y.maske)).length}`);
console.log('');
console.log('kusurun gorundugu ilk yolda fatura basina fark');
for (const o of kusurlu[0].sapan) {
  const y = kusurlu[0];
  console.log(`  ${o.no} ${String(o.m3).padStart(3)} m3  ${o.gecikmeAy} ay gecikme` +
    `  uretim=${uretim(o, y.b).tutar}  beklenen=${beklenen(o, y.b)}`);
}
```

```
yol   kademe gecikme ozet  sapan fatura  sinandi
000  kapali kapali kapali  0             evet
100  acik   kapali kapali  0             evet
010  kapali acik   kapali  0             evet
110  acik   acik   kapali  2             hayir
001  kapali kapali acik    0             evet
101  acik   kapali acik    0             hayir
011  kapali acik   acik    0             hayir
111  acik   acik   acik    2             hayir

toplam kod yolu            : 8
sinanan yol                : 4 (oran 50 yuzde)
sapan davranis ureten yol  : 2 — 110, 111
bunlardan sinanani         : 0

kusurun gorundugu ilk yolda fatura basina fark
  S-0434  17 m3  1 ay gecikme  uretim=257.36  beklenen=258.01
  S-0435  34 m3  3 ay gecikme  uretim=673.07  beklenen=683.53
```

Kusur tek bir bayrakta değil, **iki bayrağın kesişiminde** duruyor: gecikme faizi kademeli
tutar yerine taban tutar üzerinden işliyor. Kademe kapalıyken iki değer eşit olduğu için hesap
doğru çıkıyor; kademe açıldığında sapıyor. Sınama kümesi her bayrağı tek başına denediği için
sekiz yolun dördünü hiç görmüyor ve kusurlu iki yolun ikisi de o dördün içinde.

Sapmanın büyüklüğü ayrıca kayda değer: fatura başına altmış beş kuruş ile on lira arasında.
Bu, bir hata mesajı üretmeyen, oranı yüzde çeyrekten yüzde bir buçuğa değişen ve yalnız gecikmesi
olan abonelerde görünen bir sapmadır. Sekiz yolun kaçının sınandığı sorusu bu yüzden sorulur: bayrak
eklemek sınama yükümlülüğünü toplamaz, ikiyle çarpar.

#### Kapının Üç Sayısı

Bayrak koşulu bir kapıdır ve öteki kapılar gibi üç sayıyla değerlendirilir. Birincisi tuttuğu.
Bayrak koşulunun tuttuğu kusur sayısı **sıfırdır**: koşul iki dalı da geçirir, hiçbir şeyi
sınamaz. Tuttuğu tek şey maruziyettir — yüz altmış yerine seksen dört fatura, yani yetmiş altı
faturalık fark. Bu farkın tamamı geri alma yolundan gelir ve bu kapının yalnız kendisinin
sağladığı şeydir: hat kapılarının hiçbiri geri alma birimini değiştiremez.

İkincisi kaçırdığı ve yanlış tuttuğu. Bayrak kapısı kaçırdığı kusuru kendisi üretmiştir:
sınanmayan dört yol, üç bayrak eklenmeden önce var olmayan yollardır. Yanlış tutmanın ölçüsü ise
kapalı bekleyen sağlam davranıştır — aşağıdaki defterde on iki haftalık dönemin sonunda hâlâ
kapalı duran, kusurla ilgisi olmayan, tamamlanmış ve dağıtılmış bir değişiklik vardır.

Üçüncüsü eklediği süre. Bu kapı öncü süreye dakika eklemez: kod yine tek yayında gider, hat
aşamaları değişmez. Eklediği şey **ertelenmiş turdur** — okuma noktalarının yazılması ve sonra
silinmesi. Erteleme bedava göründüğü için ödenmez ve borç olarak birikir. Kapının üçüncü sayısı
bu derste dakika değil, temizlenmemiş okuma noktası sayısıdır.

#### Bayrak Borcu

Bayrağın maliyeti eklendiği anda değil, kaldırılmadığı sürece ödenir. On iki haftalık bir dönemin
defteri sayılır: hangi bayrak ne zaman eklendi, temizlendi mi, kaç noktada okunuyor.

```js
// borc.mjs — bir donemin bayrak defteri sayilir; defter MODELDIR
// [ad, eklendigi hafta, temizlendigi hafta (null: duruyor), okuma noktasi, bugunku deger]
const DONEM = 12;
const DEFTER = [
  ['kademeli-tarife', 1, 6, 3, 'acik'], ['gecikme-faizi', 2, null, 2, 'kapali'],
  ['fatura-ozeti', 2, 7, 1, 'acik'], ['is-emri-baglantisi', 3, null, 4, 'acik'],
  ['toplu-is-raporu', 4, null, 2, 'acik'], ['dogrulayici-ikinci-gecis', 5, 9, 2, 'acik'],
  ['okuma-yeniden-deneme', 7, null, 5, 'acik'], ['sozlesme-uyarisi', 8, null, 3, 'kapali'],
  ['ozet-yeni-alan', 10, null, 2, 'acik'],
];

const temiz = DEFTER.filter(([, , t]) => t !== null);
const kalan = DEFTER.filter(([, , t]) => t === null);
const nokta = kalan.reduce((t, [, , , n]) => t + n, 0);
const yasli = kalan.reduce((e, k) => (k[1] < e[1] ? k : e));
console.log(`donem                        : ${DONEM} hafta`);
console.log(`eklenen / temizlenen / kalan : ${DEFTER.length} / ${temiz.length} / ${kalan.length}`);
console.log(`temizlenenlerin ortalama omru: ` +
  `${(temiz.reduce((t, [, e, k]) => t + (k - e), 0) / temiz.length).toFixed(1)} hafta`);
console.log(`kalan bayraklarin actigi yol : ${2 ** kalan.length}`);
console.log(`bugun kosan yol              : 1 (kalanlarin degeri sabit)`);
console.log(`olu kod yolu                 : ${2 ** kalan.length - 1}`);
console.log(`her bayrak tek basina sinanirsa: ${kalan.length + 1} yol` +
  ` (yuzde ${((kalan.length + 1) / 2 ** kalan.length * 100).toFixed(0)})`);
console.log(`temizlenecek okuma noktasi   : ${nokta}`);
console.log(`acilmayi bekleyen kapali bayrak: ` +
  kalan.filter(([, , , , d]) => d === 'kapali').map(([a, e]) => `${a} (${DONEM - e} hafta)`).join(', '));
console.log(`en yasli temizlenmemis bayrak: ${yasli[0]} — ${DONEM - yasli[1]} hafta`);
```

```
donem                        : 12 hafta
eklenen / temizlenen / kalan : 9 / 3 / 6
temizlenenlerin ortalama omru: 4.7 hafta
kalan bayraklarin actigi yol : 64
bugun kosan yol              : 1 (kalanlarin degeri sabit)
olu kod yolu                 : 63
her bayrak tek basina sinanirsa: 7 yol (yuzde 11)
temizlenecek okuma noktasi   : 18
acilmayi bekleyen kapali bayrak: gecikme-faizi (10 hafta), sozlesme-uyarisi (4 hafta)
en yasli temizlenmemis bayrak: gecikme-faizi — 10 hafta
```

Altı bayrak kaldı ve altmış dört kod yolu açtı. Bugün bu yollardan yalnız biri koşuyor, çünkü
altı bayrağın da değeri sabit. Kalan altmış üçü ölü kod yoludur: hiçbiri çalışmıyor, hepsi
kodda duruyor ve her biri sınanabilir bir davranış olarak var sayılıyor. Sınama kümesi aynı
kalıpla — her bayrak tek başına — genişletilirse altmış dört yolun yedisini örter; oran yüzde
elliden yüzde on bire düşer. Bayrak sayısı doğrusal artarken kapsama üstel olarak seyrelir.

On sekiz okuma noktası, bu bayrakların temizlenmesi için silinecek koşul sayısıdır. Bu iş
ertelendikçe kod okunmaz hâle gelir, ama daha önemlisi, hangi dalın canlı olduğu kodun kendisinden
okunamaz olur. Temizlenen üç bayrağın ortalama ömrü 4,7 haftaydı; kalanlardan biri on
haftadır duruyor. İki bayrak kapalı bekliyor: biri kusurlu, biri tamamlanmış ama hiç açılmamış.
İkincisi kapının yanlış tuttuğu değişikliktir — dört haftadır üretimde duran ve hiçbir aboneye
ulaşmayan bir davranış.

Kaldırılması gereken kapı burada görünür hâle gelir. `kademeli-tarife` altıncı haftada
temizlendi: bayrak açılmış, davranış kalıcı olmuş, koşul silinmiştir. Kapının kaldırılması
kapının kurulması kadar plana girer; girmediğinde defterdeki tek sayı büyür.

#### Özet

- Bayrak geri alma birimini kodun tamamından tek davranışa indirdi: bayraksız düzende üretimden
  dört değişiklik çıktı ve üçü sağlamdı, bayraklı düzende çıkan tek şey kusurlu davranıştı.
- Geri alma turu ikiden bire, etkilenen fatura yüz altmıştan seksen dörde indi; farkın tamamı
  geri alma yolunun kısalmasından geldi, kusurun daha erken görülmesinden değil.
- Üç bayrak sekiz kod yolu açtı; her bayrağı tek başına sınayan küme dördünü kapsadı ve sapan
  davranış üreten iki yolun ikisi de kapsam dışında kaldı.
- Bayrak koşulu kusur tutmaz — tuttuğu şey maruziyettir; kaçırdığı yolları ise kendisi üretir.
- On iki haftada dokuz bayrak eklendi, üçü temizlendi; kalan altı bayrak altmış dört yol açtı,
  bunların altmış üçü ölü kod yolu olarak durdu ve on sekiz okuma noktası temizlenmeyi bekledi.

#### Sonraki Adım

Bayrak kapatmak, geri alınabilecek şey bir davranışsa işe yarar. Kusur bir bayrağın arkasında
durmuyorsa — yapılandırma değişmişse, veri dönüştürülmüşse ya da değişiklik bayraklanmadan
yayınlanmışsa — geri dönüş yolu tek anahtar değil, sıralı bir adım listesidir. O listenin kaç
adım olduğu, her adımın kaç tur sürdüğü ve hangi adımdan sonra geri dönüşün kapandığı ayrı ayrı
ölçülür. Sonraki ders geri almayı bir düğme olarak değil, **plan olarak** ele alır ve planın
prova edilip edilmemesinin ilk koşumda kaç adıma mal olduğunu sayar.

### Geri Alma Planı

Source: https://academia.sh/tr/kurslar/ci-cd/geri-alma-plani

Geri alma bir düğme olarak değil plan olarak modellenir: üç senaryo (yalnız kod, kod ve yapılandırma, kod ve veri) adım adım koşturulur, her adımın turu sayılır ve dönüşü olmayan noktanın kaçıncı adım olduğu bulunur. Provası yapılmamış planın ilk koşumda kaç adımda takıldığı ve ön koşul denetiminin kapı olarak değeri ölçülür.

Bayrak kapatmak, geri alınacak şey bir davranışsa bir tur sürer. Kusur bayraklanmamış bir
değişiklikteyse, yapılandırma da değişmişse ya da yayınla birlikte veri dönüştürülmüşse geri
dönüş tek anahtar değildir. Sıralı adımlar vardır, adımların ön koşulları vardır ve bazı
adımlardan sonra geri dönüş kapanır.

Bu dersin ölçtüğü şey yayın mekaniği değil, **planın kendisidir**. Sıfır kesintili yayın
düzenleri ve göç ile yayın sırası M16/K08'de ölçüldü; burada sorulan soru, o düzenlerden geri
dönmenin kaç adım ve kaç tur olduğudur. Üç senaryo koşturulur, her adımın turu sayılır, dönüşü
olmayan noktanın kaçıncı adım olduğu bulunur ve planın prova edilip edilmemesinin ilk koşumda
kaç tura mal olduğu ölçülür.

- **YS31.** Geri alma bir plandır: sıralı adımlar, her adımın bir turu ve sıfır ya da daha çok
  ön koşulu vardır. Adımlar atlanamaz.
- **YS32.** Tur, olay anında ödenen süre birimidir; modelde bütün turlar eşit uzunluktadır.
  Turun dakika karşılığı ortamdan ortama değişir, oran değişmez.
- **YS33.** Yapılandırma kaydı sürüm tutmaz: üzerine yazılan değer geri getirilemez.
- **YS34.** Prova, planın olay dışında bir ortamda baştan sona koşturulmasıdır. Kalıcı eksikleri
  bulur; olay anına bağlı olanları — nöbet, yetki, o gecenin ekibi — bulmaz.
- **YS35.** Yarıda kalan geri alma karışık durum bırakır: düğümlerin bir kısmı eski, bir kısmı
  yeni sürümdedir. Bu durumdan çıkmak sabit bir tur ekler.
- **YS36.** Kurgu, bölgesel ölçüm ağının faturalama servisidir; hatalı bir yayından dönülmektedir.
  Servis de, aşağıdaki plan ve girişim defterleri de kurgudur.

#### Planın Üç Senaryosu

Aynı olay üç ayrı geri alma planı doğurur. Yalnız kod değiştiyse plan kısadır. Yayınla birlikte
bir yapılandırma değeri de değiştiyse iki adım eklenir. Yayın bir veri göçü taşıdıysa üç adım
eklenir. Aşağıdaki yürütücü planı adım adım koşturur, ön koşulu sağlanmayan adımda takılır ve
onarım turunu ekler.

```js
// plan.mjs — geri alma bir plan olarak modellenir ve uc senaryoda kosturulur; plan MODELDIR
// Bolgesel olcum agi kurgusu: faturalama servisinin hatali yayinindan donus.

const ORTAM = {                     // olay anindaki gercek durum
  'onceki-cikti-depoda': true,      // saklama kurali icindeki yapi ciktisi duruyor
  'saglik-ucu-uyumlu': true,        // eski surumun saglik ucu yeni denetimle uyusuyor
  'goc-oncesi-yedek': true,         // gocten once anlik yedek alinmis
  'ayar-onceki-surumu': false,      // yapilandirma kaydi surum tutmuyor
  'nobetci-yetkisi': false,         // geri alma yetkisi yalniz gunduz ekibinde
  'geri-goc-betigi': false,         // gocun ters adimi yazilmamis
};
const ONARIM = { 'ayar-onceki-surumu': 3, 'nobetci-yetkisi': 2, 'geri-goc-betigi': 6 };

// [ad, tur, on kosullar, bu adimdan sonra geri donulebilir mi]
const KOD = [
  ['kusur dogrulanir', 1, ['saglik-ucu-uyumlu'], true],
  ['geri alma karari ve yetki', 1, ['nobetci-yetkisi'], true],
  ['onceki yapi ciktisi secilir', 1, ['onceki-cikti-depoda'], true],
  ['dugumler eski ciktiya dondurulur', 2, [], true],
  ['saglik ve tutar dogrulanir', 1, ['saglik-ucu-uyumlu'], true],
];
const AYAR = [
  ['ayar onceki degere yazilir', 2, ['ayar-onceki-surumu'], false],
  ['ayar dugumlere yayilir', 1, [], true],
];
const VERI = [
  ['yazma durdurulur', 1, [], true],
  ['ters goc kosar', 3, ['geri-goc-betigi'], false],
  ['yazma acilir, sayilar karsilastirilir', 2, ['goc-oncesi-yedek'], true],
];
const SENARYO = {
  'yalniz kod': KOD,
  'kod + yapilandirma': [...KOD.slice(0, 4), ...AYAR, KOD[4]],
  'kod + veri': [...KOD.slice(0, 4), ...VERI, KOD[4]],
};

function kostur(plan, provaliMi) {
  const o = { ...ORTAM };
  if (provaliMi) for (const k of Object.keys(ONARIM)) o[k] = true;  // prova once buldu, once onardi
  let tur = 0, ek = 0;
  const takilan = [];
  plan.forEach(([ad, t, kosul], i) => {
    for (const k of kosul) {
      if (!o[k]) { takilan.push([i + 1, ad, k, ONARIM[k]]); ek += ONARIM[k]; o[k] = true; }
    }
    tur += t;
  });
  const d = plan.findIndex(([, , , g]) => !g);
  return { adim: plan.length, tur, ek, takilan, donus: d < 0 ? '—' : String(d + 1) };
}

console.log('senaryo             adim  provasiz takilma  ek tur  provasiz  provali  donusu olmayan adim');
for (const [ad, plan] of Object.entries(SENARYO)) {
  const y = kostur(plan, false), p = kostur(plan, true);
  console.log(`${ad.padEnd(19)} ${String(y.adim).padEnd(5)} ${String(y.takilan.length).padEnd(17)} ` +
    `${String(y.ek).padEnd(7)} ${String(y.tur + y.ek).padEnd(9)} ${String(p.tur + p.ek).padEnd(8)} ${y.donus}`);
}
console.log('');
console.log('kod + veri, provasiz ilk kosum — adim adim');
const kv = kostur(SENARYO['kod + veri'], false);
for (const [no, ad, kosul, onarimTuru] of kv.takilan) {
  console.log(`  adim ${no} takildi: ${ad.padEnd(34)} eksik on kosul: ${kosul} (+${onarimTuru} tur)`);
}
console.log(`  toplam ${kv.adim} adim, ${kv.tur} plan turu, ${kv.ek} onarim turu`);
console.log(`  ${kv.donus}. adimdan sonra geri donus yok: ters goc veriyi uzerine yazar`);
```

```
senaryo             adim  provasiz takilma  ek tur  provasiz  provali  donusu olmayan adim
yalniz kod          5     1                 2       8         6        —
kod + yapilandirma  7     2                 5       14        9        5
kod + veri          8     2                 8       20        12       6

kod + veri, provasiz ilk kosum — adim adim
  adim 2 takildi: geri alma karari ve yetki          eksik on kosul: nobetci-yetkisi (+2 tur)
  adim 6 takildi: ters goc kosar                     eksik on kosul: geri-goc-betigi (+6 tur)
  toplam 8 adim, 12 plan turu, 8 onarim turu
  6. adimdan sonra geri donus yok: ters goc veriyi uzerine yazar
```

Üç senaryo aynı olaydan doğuyor ve beş, yedi, sekiz adım sürüyor. Adım sayısındaki artış
küçük görünür; tur sayısındaki artış öyle değil. Provasız koşumda sekiz turluk bir plan yirmi
tura çıkıyor — iki buçuk katı. Aradaki farkın tamamı plan adımlarından değil, adımların ön
koşullarından geliyor.

Buradaki asıl sayı, geri almanın **yayınla aynı ağırlıkta olmamasıdır**. Yayın hattan geçer:
aşamalar sıralıdır, ön koşullar hat tarafından sağlanır, her adım daha önce yüzlerce kez
koşmuştur. Geri alma ise çoğu kez ilk kez koşar. Aynı sistemin iki yönü arasındaki bu asimetri,
üç senaryonun provasız sütununda görünüyor.

Modeldeki altı ön koşulun üçü sağlanmıyor ve üçü de aynı nedenden sağlanmıyor: hiçbiri ileri
yönde gerekmiyor. Yapılandırma kaydının geçmiş tutması yayın için gerekmez, ileri yönde son
değer yeterlidir. Nöbetçinin geri alma yetkisi yayın için gerekmez, yayını hat yapar. Göçün ters
betiği yayın için gerekmez, göç ileri koşar. Üçü de yalnız geri dönüşte gerekir ve yalnız geri
dönüşte gereken şey, geri dönülene kadar kimsenin fark etmediği şeydir. Bir ön koşulun eksik
olduğunun anlaşıldığı an, çoğunlukla ona en çok ihtiyaç duyulan andır.

#### Dönüşü Olmayan Nokta

Bir geri alma planında her adım geri alınabilir değildir. Yalnız kod senaryosunda dönüşü
olmayan nokta yok: her adım, önceki durumu bozmadan bir sonraki duruma geçiyor; sekizinci turda
kararından dönmek isteyen biri yeni sürüme geri yayın yapabilir.

Yapılandırma senaryosunda dönüşü olmayan nokta beşinci adımdır. Yapılandırma kaydı sürüm
tutmadığı için önceki değerin üzerine yazmak, o anki değeri kaybetmektir. Kaybolan değer yeni
sürümün değeridir ve geri alma başarısız olup yeni sürüme dönülmek istendiğinde o değer artık
elde yoktur. Adımın kendisi bir saniye sürer; geri döndürülemezliği süresinden değil, yazdığı
yerin geçmiş tutmamasından gelir.

Veri senaryosunda dönüşü olmayan nokta altıncı adımdır. Ters göç, yeni şemada biriken kayıtları
eski biçime dönüştürerek üzerine yazar. Bu adımdan sonra planın geri kalanı zorunlu hâle gelir:
yedinci ve sekizinci adım koşmazsa sistem ne eski ne yeni durumdadır. Sekiz adımlık planın son
üç adımı bu yüzden tek parça sayılır.

Dönüşü olmayan noktanın plan içindeki yeri karar için doğrudan bilgidir. Beşinci ya da altıncı
adıma gelmiş bir geri alma, artık "vazgeçilebilir" değildir; o noktadan önce verilmesi gereken
karar, geri almanın gerçekten gerekli olup olmadığıdır. Planın hangi adımına kadar ücretsiz
olduğunu bilmek, kararın ne zaman verilmesi gerektiğini de söyler.

#### Prova Edilmemiş Planın Bedeli

Provasız ve provalı sütunlar aynı planları, aynı adım sayısıyla, aynı sırada koşturuyor. Tek
fark, ön koşulların olaydan önce mi yoksa olay sırasında mı bulunduğudur. Yalnız kod senaryosunda
fark iki tur, veri senaryosunda sekiz tur.

Provasız koşumun adım adım dökümü nerede takıldığını gösteriyor. İkinci adımda nöbetçinin geri
alma yetkisi yok: iki tur, başka birine ulaşıp yetkinin açılmasıyla geçiyor. Altıncı adımda
göçün ters betiği yazılmamış: altı tur, betiğin olay anında yazılmasıyla geçiyor. İkinci
takılmanın yeri kayda değer — dönüşü olmayan noktanın tam üstünde. Betik, geri dönüşün kapandığı
adımda yazılıyor ve hiç sınanmadan koşuyor.

Prova bedava değildir. Planın on iki turu prova sırasında da ödenir, ayrıca eksiklerin onarımı
da ödenir. Aradaki fark ödemenin **ne zaman** yapıldığıdır: prova turları olay dışında, seçilmiş
bir zamanda, kimseyi bekletmeden geçer. Provasız planın sekiz onarım turu ise olay penceresinin
içinde, kusur üretimde koşarken geçer. Aynı iş, farklı fiyatla.

Provanın sınırı da ölçüye girer. Betiğin yazılmamış olması provada bulunur, çünkü kalıcı bir
eksiktir. Nöbetçinin yetkisi öyle değildir: prova gündüz koşarsa gündüz ekibinin yetkisiyle
geçer ve gece nöbetinde açılan boşluk görünmez.

#### Kapı: Adım Öncesi Ön Koşul Denetimi

Planın her adımının ön koşulunu adım başlamadan denetlemek bir kapıdır: geçmeyen adım
başlatılmaz. Bu kapı da üç sayıyla değerlendirilir. On iki geri alma girişiminden oluşan bir
defter iki kipte koşturulur — denetimli ve denetimsiz.

```js
// denetim.mjs — on kosul denetimi bir kapidir; on iki geri alma girisimi iki kipte kosar
// [girisim, senaryo, gercekten eksik on kosul, denetimin yanlis "eksik" dedigi]  MODELDIR
const GIRISIM = [
  ['O-01', 'yalniz kod', [], []],
  ['O-02', 'yalniz kod', ['nobetci-yetkisi'], []],
  ['O-03', 'kod + yapilandirma', ['ayar-onceki-surumu'], []],
  ['O-04', 'yalniz kod', [], ['onceki-cikti-depoda']],
  ['O-05', 'kod + veri', ['geri-goc-betigi'], []],
  ['O-06', 'yalniz kod', [], []],
  ['O-07', 'kod + yapilandirma', ['nobetci-yetkisi', 'ayar-onceki-surumu'], []],
  ['O-08', 'yalniz kod', [], []],
  ['O-09', 'kod + veri', ['goc-oncesi-yedek'], []],
  ['O-10', 'yalniz kod', ['nobetci-yetkisi'], []],
  ['O-11', 'kod + yapilandirma', [], ['ayar-onceki-surumu']],
  ['O-12', 'kod + veri', [], []],
];
const ONARIM = { 'ayar-onceki-surumu': 3, 'nobetci-yetkisi': 2, 'geri-goc-betigi': 6,
  'goc-oncesi-yedek': 4, 'onceki-cikti-depoda': 2 };
const DENETLENEN = new Set(['onceki-cikti-depoda', 'saglik-ucu-uyumlu',
  'ayar-onceki-surumu', 'nobetci-yetkisi', 'geri-goc-betigi']);
const PROVA_YAKALAR = new Set(['ayar-onceki-surumu', 'geri-goc-betigi', 'onceki-cikti-depoda']);
const YARIM = 5;      // yarida kalan geri almanin karisik durumdan cikma turu
const DENETIM = 1;    // plan basina on kosul denetiminin ekledigi tur

const say = { tuttu: 0, yalniz: 0, kacirdi: 0, yanlis: 0, yanlisTur: 0,
  denetimsizEk: 0, denetimliEk: 0 };
for (const [, , eksik, yanlisEksik] of GIRISIM) {
  const onarim = eksik.reduce((t, k) => t + ONARIM[k], 0);
  say.denetimsizEk += onarim + (eksik.length ? YARIM : 0);

  const goren = eksik.filter((k) => DENETLENEN.has(k));
  const kacan = eksik.filter((k) => !DENETLENEN.has(k));
  const bosuna = yanlisEksik.reduce((t, k) => t + ONARIM[k], 0);
  say.yanlisTur += bosuna;
  say.denetimliEk += DENETIM + onarim + (kacan.length ? YARIM : 0) + bosuna;

  if (goren.length) { say.tuttu += 1; if (goren.every((k) => !PROVA_YAKALAR.has(k))) say.yalniz += 1; }
  if (kacan.length) say.kacirdi += 1;
  if (yanlisEksik.length) say.yanlis += 1;
}

console.log(`girisim                                  : ${GIRISIM.length}`);
console.log(`kapinin tuttugu (yarim kalmayi onledigi) : ${say.tuttu}`);
console.log(`bunlardan yalniz bu kapinin tuttugu      : ${say.yalniz} (provanin yakalayamadigi)`);
console.log(`kapinin kacirdigi (denetlenmeyen kosul)  : ${say.kacirdi}`);
console.log(`kapinin yanlis tuttugu                   : ${say.yanlis}`);
console.log('');
console.log(`denetimsiz kipte olay anina eklenen tur  : ${say.denetimsizEk}`);
console.log(`denetimli kipte olay anina eklenen tur   : ${say.denetimliEk}`);
console.log(`  bunun denetimin kendisinden geleni     : ${GIRISIM.length * DENETIM}`);
console.log(`  yanlis tutmadan geleni                 : ${say.yanlisTur}`);
console.log(`kapinin net kazanci                      : ${say.denetimsizEk - say.denetimliEk} tur`);
console.log(`girisim basina net kazanc                : ${((say.denetimsizEk - say.denetimliEk) / GIRISIM.length).toFixed(2)} tur`);
```

```
girisim                                  : 12
kapinin tuttugu (yarim kalmayi onledigi) : 5
bunlardan yalniz bu kapinin tuttugu      : 2 (provanin yakalayamadigi)
kapinin kacirdigi (denetlenmeyen kosul)  : 1
kapinin yanlis tuttugu                   : 2

denetimsiz kipte olay anina eklenen tur  : 52
denetimli kipte olay anina eklenen tur   : 44
  bunun denetimin kendisinden geleni     : 12
  yanlis tutmadan geleni                 : 5
kapinin net kazanci                      : 8 tur
girisim basina net kazanc                : 0.67 tur
```

Kapı on iki girişimin beşinde yarıda kalan bir geri almayı önledi. Ama bu beşin üçünü prova da
yakalardı: kalıcı eksiklerdi, planı bir kez baştan sona koşturmak yeterdi. Kapının **yalnız
kendisinin** tuttuğu iki girişim, nöbetçi yetkisinin o gece eksik olduğu girişimlerdir — prova
gündüz koştuğu için görünmeyen, olay anına bağlı eksik. Marjinal değer beşte değil, ikidedir.

Kapı bir girişimi kaçırdı: göç öncesi yedeğin alınmamış olması denetim listesinde yok. Denetim
listesi kendisi bir kod parçasıdır ve neyi denetlediği kadar neyi denetlemediği de sayılmalıdır.
İki girişimde ise kapı yanlış tuttu — sağlanmış bir ön koşulu eksik sandı ve beş turluk bir
onarımı boşuna ödetti.

Kapının eklediği süre bu defterde ayrı bir satırdır: her girişimde bir tur, on iki girişimde on
iki tur. Bu süre, kusurun üretimde koşmaya devam ettiği bir pencerede ödeniyor ve girişimlerin
yedisinde hiçbir eksik bulmadan geçiyor. Yanlış tutmanın beş turu da aynı pencerede. Kapının
brüt kazancı yirmi beş tur; denetimin kendi maliyeti on iki, yanlış tutmanınki beş turdur —
toplamı on yedi.

Net kazanç elli iki tura karşı kırk dört tur, girişim başına üçte iki tur. Bu ince bir farktır ve
kapının kendi maliyeti — on iki tur — kazancın üstündedir. Kapıyı ayakta tutan şey ortalama
değil, dağılımdır: kaçırılan girişimlerde bedel beş turluk karışık durumdur ve bu bedel olay
penceresinin içinde, kusur koşarken ödenir. Kapının savunulabilir tek biçimi, denetim listesinin
prova ile bölüşülmesidir: kalıcı eksikler provada, olay anına bağlı olanlar kapıda.

#### Özet

- Aynı olay üç plan doğurdu: yalnız kod beş adım, kod ile yapılandırma yedi adım, kod ile veri
  sekiz adım; provasız toplam süreleri sırasıyla sekiz, on dört ve yirmi tur oldu.
- Dönüşü olmayan nokta yalnız kod senaryosunda yok, yapılandırma senaryosunda beşinci,
  veri senaryosunda altıncı adımdır; o adımdan sonra planın kalanı zorunlu hâle gelir.
- Provasız koşum iki adımda takıldı ve sekiz onarım turu ekledi; ikinci takılma dönüşü olmayan
  noktanın hemen üstünde, ters göç betiğinin olay anında yazıldığı adımdaydı.
- Prova aynı işi ödetir, farklı zamanda: on iki plan turu olay dışında geçer, sekiz onarım turu
  ise kusur üretimde koşarken geçer.
- Ön koşul denetimi kapısı on iki girişimin beşini tuttu, ikisini yalnız kendisi tuttu, birini
  kaçırdı, ikisini yanlış tuttu; net kazancı sekiz tur, kendi maliyeti on iki turdu.

#### Sonraki Adım

Üç senaryodan ikisinde dönüşü olmayan nokta vardı ve ikisi de veriyle ilgiliydi. Kod geri
alınabilir, yapılandırma sürümlenirse geri alınabilir; şema değişikliği için bu o kadar sade
değildir. Kimi göç türü tam olarak geri alınabilir, kimisi çalışır görünüp veriyi eksik geri
getirir, kimisinden dönüş yoktur. Sonraki ders göç türlerini geri alınabilirlik ekseninde
sıralar, genişlet–taşı–daralt düzenini adım adım koşturur ve her adımda eski ile yeni sürümün
aynı anda çalışıp çalışamadığını sayar.

### Veritabanı Göçleriyle Eşgüdüm

Source: https://academia.sh/tr/kurslar/ci-cd/veritabani-gocleriyle-esgudum

Altı göç türü gerçekten uygulanıp tersi koşturulur ve geri alınabilirlik, komutun hatasız çalışmasıyla değil tablo içeriğinin özdeşliğiyle ölçülür. Genişlet–taşı–daralt düzeni adım adım koşar; her adımda eski ile yeni sürümün birlikte çalışıp çalışamadığı sayılır ve uyumsuz adımın yayın penceresine ve geri alma planına etkisi yazılır.

Önceki dersin üç senaryosundan ikisinde dönüşü olmayan bir nokta vardı ve veri senaryosunda o
nokta ters göç adımıydı. Bir geri alma planı ancak içindeki her adım kadar geri alınabilirdir;
şema değişikliği o adımların en kırılganıdır. Kod bir çıktı seçilerek, yapılandırma bir değer
yazılarak geri gelir. Şema için bu her zaman doğru değildir.

Bu dersin ekseni **geri alınabilirliktir**. Göç ile kod yayınının sırası ve o sıranın hata alan
istek sayısı M16/K08'de ölçüldü; burada sorulan soru sıra değil, sınıftır: hangi göç türü tam
olarak geri alınabilir, hangisi çalışır görünüp veriyi eksik getirir, hangisinden dönüş yoktur.
İkinci ölçü, adımların **iki sürümü birlikte taşıyıp taşımadığıdır** — geri alma planının
yayın penceresiyle kesiştiği yer burasıdır.

- **YS37.** Şema tek bir veritabanında durur ve bütün sürümler aynı tabloyu okuyup yazar.
- **YS38.** Göç adımları uygulama yayınından ayrı koşar; ikisi aynı işlemin parçası değildir.
- **YS39.** Geri alınabilirlik, ters işlem koşturulduktan sonra tablo içeriğinin **özdeş** olmasıyla
  ölçülür. Komutun hatasız çalışması yeterli sayılmaz.
- **YS40.** İki sürümün uyumluluğu, her birinin ötekinin yazdığı kaydı doğru okuyabilmesidir.
  Sorgunun hata vermemesi yeterli değildir; okunan değer de doğru olmalıdır.
- **YS41.** Geçiş sürümlerinin okuması geri düşüşlüdür: yeni sütun boşsa eski sütuna bakar.
- **YS42.** Kurgu, bölgesel ölçüm ağının sayaç okuma tablosudur; okuma değeri metreküp cinsinden
  tek bir sütunken litre cinsinden ilk ve son endeks çiftine bölünmektedir. Sayılar kurgudur.

#### Göç Türlerinin Geri Alınabilirliği

Altı göç türü gerçek bir tabloda uygulanır, hemen ardından tersi koşturulur ve tablo içeriği
başlangıçtakiyle karşılaştırılır. Ölçü, ters komutun çalışıp çalışmadığı değil, satırların
özdeş kalıp kalmadığıdır.

```js
// goc-turleri.mjs — alti goc turu gercekten uygulanir, tersi kosturulur, icerik karsilastirilir
// Bolgesel olcum agi kurgusu: sayac okuma tablosu. Olcu: ters islemden sonra ozdeslik.
import { DatabaseSync } from 'node:sqlite';
import { createHash } from 'node:crypto';

const OKUMA = [
  ['R-101', 'S-0431', 8.432, 'gorsel'], ['R-102', 'S-0432', 17.06, 'gorsel'],
  ['R-103', 'S-0433', 34.915, 'uzaktan'], ['R-104', 'S-0434', 52.5, 'uzaktan'],
  ['R-105', 'S-0435', 6.004, 'gorsel'], ['R-106', 'S-0436', 21.37, 'tahmin'],
];

function kur() {
  const db = new DatabaseSync(':memory:');
  db.exec('CREATE TABLE okuma (id TEXT PRIMARY KEY, sayac TEXT, m3 REAL, kaynak TEXT)');
  const ekle = db.prepare('INSERT INTO okuma VALUES (?, ?, ?, ?)');
  for (const o of OKUMA) ekle.run(...o);
  return db;
}
const satirlar = (db) => db.prepare('SELECT * FROM okuma ORDER BY id').all()
  .map((s) => Object.keys(s).sort().map((k) => `${k}=${s[k]}`).join('|'));
const ozet = (db) => createHash('sha256').update(satirlar(db).join('\n')).digest('hex').slice(0, 10);

const TUR = [
  ['sutun ekleme', (db) => db.exec('ALTER TABLE okuma ADD COLUMN litre INTEGER'),
    (db) => db.exec('ALTER TABLE okuma DROP COLUMN litre')],
  ['indeks ekleme', (db) => db.exec('CREATE INDEX ix_sayac ON okuma(sayac)'),
    (db) => db.exec('DROP INDEX ix_sayac')],
  ['sutun yeniden adlandirma', (db) => db.exec('ALTER TABLE okuma RENAME COLUMN m3 TO tuketim'),
    (db) => db.exec('ALTER TABLE okuma RENAME COLUMN tuketim TO m3')],
  ['geri doldurma', (db) => { db.exec('ALTER TABLE okuma ADD COLUMN litre INTEGER');
    db.exec('UPDATE okuma SET litre = CAST(m3 * 1000 AS INTEGER)'); },
    (db) => db.exec('ALTER TABLE okuma DROP COLUMN litre')],
  ['yerinde hassasiyet daraltmasi', (db) => db.exec('UPDATE okuma SET m3 = ROUND(m3, 1)'),
    (db) => db.exec('UPDATE okuma SET m3 = m3')],
  ['sutun silme', (db) => db.exec('ALTER TABLE okuma DROP COLUMN kaynak'),
    (db) => db.exec('ALTER TABLE okuma ADD COLUMN kaynak TEXT')],
];

console.log('goc turu                        once       sonra      geri donusten sonra  ozdes  farkli satir');
for (const [ad, ileri, geri] of TUR) {
  const db = kur();
  const o1 = satirlar(db), h1 = ozet(db);
  ileri(db);
  const h2 = ozet(db);
  geri(db);
  const o3 = satirlar(db), h3 = ozet(db);
  const fark = o1.filter((s, i) => s !== o3[i]).length;
  console.log(`${ad.padEnd(31)} ${h1} ${h2} ${h3}           ` +
    `${(h1 === h3 ? 'evet' : 'hayir').padEnd(6)} ${fark}`);
  db.close();
}
```

```
goc turu                        once       sonra      geri donusten sonra  ozdes  farkli satir
sutun ekleme                    39a9eddd27 773061e459 39a9eddd27           evet   0
indeks ekleme                   39a9eddd27 39a9eddd27 39a9eddd27           evet   0
sutun yeniden adlandirma        39a9eddd27 92df9b4475 39a9eddd27           evet   0
geri doldurma                   39a9eddd27 188c3af8a8 39a9eddd27           evet   0
yerinde hassasiyet daraltmasi   39a9eddd27 dfea77da56 dfea77da56           hayir  5
sutun silme                     39a9eddd27 e244ca795a c5d40ca8c9           hayir  6
```

Altı türün dördü tam olarak geri alınıyor: özet önceki değerine dönüyor ve hiçbir satır
farklı değil. Bu dördünün ortak yanı, veriyi hiç kaybetmemeleridir. Sütun ve indeks eklemek
mevcut satırlara dokunmaz. Yeniden adlandırma yalnız adı değiştirir. Geri doldurma yeni bir
sütuna yazar ve eski sütun yerinde durur.

Son iki tür geri alınmıyor ve ikisi ayrı nedenlerle geri alınmıyor. Hassasiyet daraltması
değeri **yerinde** ezdi: ondalıklar kesildikten sonra onları geri getirecek bir ters komut
yoktur. Ters işlem yerine kimlik işlemi konuldu ve beş satırın değeri eski hâline dönmedi —
ikinci ile üçüncü özet aynı çıktı. Sütun silme ise komutun hatasız çalıştığı hâlde geri
getirmeyen türün örneğidir: sütun yeniden eklendi, altı satırın hepsi boş geldi.

Ayrım şurada duruyor: geri alma komutunun var olması, geri alınabilirlik demek değildir. Her
iki başarısız satırda da ters komut çalıştı ve sıfır kodla döndü. Ölçüyü özdeşlik üzerinden
kurmak, "geri alma betiği yazıldı" ifadesini sınanabilir bir şeye çeviriyor.

Tablonun üçüncü satırı ayrıca bir uyarı taşıyor. Sütun yeniden adlandırma tam olarak geri
alınıyor — sıfır farklı satır — ama bu, adımın güvenli olduğu anlamına gelmiyor. Eski adı
kullanan bir sürüm, adlandırma geçtiği anda sütunu bulamaz. Geri alınabilirlik bir adımın
**sonrasına** bakar; uyumluluk **sırasına**. Bir adım geri alınabilir olduğu hâlde iki sürümü
birlikte taşımayabilir, ya da tersi. İkinci ölçü bu yüzden ayrı koşturulur.

#### Genişlet–Taşı–Daralt, Adım Adım

Metreküp cinsinden tek bir okuma sütunu, litre cinsinden ilk ve son endeks çiftine bölünüyor.
Düzen altı adım: şema genişler, veri taşınır, kod dört sürümde ilerler, şema daralır. Her adımda
o an koşan sürüm ile bir önceki sürüm birlikte sınanır — biri yazar, öteki okur, sonra tersi.

```js
// esgudum.mjs — genislet-tasi-daralt adim adim kosar; her adimda iki surum birlikte sinanir
// Kurgu: sayac okumasi m3 tek sutunundan litre cinsinden ilk/son endeks ciftine bolunur.
import { DatabaseSync } from 'node:sqlite';

const M3 = 12.345, ILK = 100000, SON = 112345;
const OKU_GERI_DUSUSLU = 'SELECT COALESCE((son_endeks - ilk_endeks) / 1000.0, m3) AS deger' +
  ' FROM okuma WHERE id = ?';
const SURUM = {
  v1: { alan: 'm3', oku: 'SELECT m3 AS deger FROM okuma WHERE id = ?',
    yaz: 'INSERT INTO okuma (id, sayac, m3) VALUES (?, ?, ?)' },
  v2: { alan: 'm3+endeks', oku: OKU_GERI_DUSUSLU,
    yaz: 'INSERT INTO okuma (id, sayac, m3, ilk_endeks, son_endeks) VALUES (?, ?, ?, ?, ?)' },
  v3: { alan: 'endeks', oku: OKU_GERI_DUSUSLU,
    yaz: 'INSERT INTO okuma (id, sayac, ilk_endeks, son_endeks) VALUES (?, ?, ?, ?)' },
  v4: { alan: 'endeks', oku: 'SELECT (son_endeks - ilk_endeks) / 1000.0 AS deger FROM okuma WHERE id = ?',
    yaz: 'INSERT INTO okuma (id, sayac, ilk_endeks, son_endeks) VALUES (?, ?, ?, ?)' },
};
const DEGER = { m3: ['S-0440', M3], 'm3+endeks': ['S-0440', M3, ILK, SON], endeks: ['S-0440', ILK, SON] };

const SEMA = {
  genislet: [(db) => db.exec('ALTER TABLE okuma ADD COLUMN ilk_endeks INTEGER;' +
    ' ALTER TABLE okuma ADD COLUMN son_endeks INTEGER'), true],
  tasi: [(db) => db.exec('UPDATE okuma SET ilk_endeks = 0,' +
    ' son_endeks = CAST(m3 * 1000 AS INTEGER) WHERE son_endeks IS NULL'), true],
  daralt: [(db) => db.exec('ALTER TABLE okuma DROP COLUMN m3'), false],
};
// [ad, sema adimi, kosan surum, bir surum geri alma hedefi, iki surum geri alma hedefi]
const ADIM = [
  ['genislet: endeks sutunlari eklenir', 'genislet', 'v1', 'v1'],
  ['cift yazan surum yayinlanir', null, 'v2', 'v1'],
  ['tasi: eski satirlar geri doldurulur', 'tasi', 'v2', 'v1'],
  ['m3 yazmayi birakan surum yayinlanir', null, 'v3', 'v2', 'v1'],
  ['geri dususu kaldiran surum yayinlanir', null, 'v4', 'v3'],
  ['daralt: m3 sutunu silinir', 'daralt', 'v4', 'v3'],
];

const db = new DatabaseSync(':memory:');
db.exec('CREATE TABLE okuma (id TEXT PRIMARY KEY, sayac TEXT, m3 REAL)');
db.prepare('INSERT INTO okuma (id, sayac, m3) VALUES (?, ?, ?)').run('R-101', 'S-0431', 8.432);

function sina(A, B, ek) {
  const bulgu = [];
  for (const [j, [yazan, okuyan]] of [[A, B], [B, A]].entries()) {
    const id = `T-${ek}-${j}`;
    try { db.prepare(SURUM[yazan].yaz).run(id, ...DEGER[SURUM[yazan].alan]); }
    catch (e) { bulgu.push(`${yazan} yazamadi: ${e.message}`); continue; }
    try {
      const d = db.prepare(SURUM[okuyan].oku).get(id)?.deger;
      if (Math.abs((d ?? -1) - M3) > 1e-9) bulgu.push(`${okuyan}, ${yazan} kaydini ${d} okudu`);
    } catch (e) { bulgu.push(`${okuyan} okuyamadi: ${e.message}`); }
  }
  return bulgu;
}

console.log('adim                                    sema      kosan  hedef  uyumlu  sema geri alinir');
let uyumlu = 0, geriAlinir = 0, semaAdimi = 0;
ADIM.forEach(([ad, sema, A, B, C], i) => {
  if (sema) { SEMA[sema][0](db); semaAdimi += 1; if (SEMA[sema][1]) geriAlinir += 1; }
  const bulgu = sina(A, B, i + 1);
  if (bulgu.length === 0) uyumlu += 1;
  console.log(`${(i + 1)} ${ad.padEnd(38)} ${(sema ?? '—').padEnd(9)} ${A}     ${B}     ` +
    `${(bulgu.length ? 'hayir' : 'evet').padEnd(7)} ${sema ? (SEMA[sema][1] ? 'evet' : 'hayir') : '—'}`);
  for (const b of bulgu) console.log(`    ${b}`);
  if (C) {
    const iki = sina(A, C, `${i + 1}x`);
    console.log(`    iki surum geri (${A} ile ${C}): ${iki.length ? 'uyumsuz' : 'uyumlu'}`);
    for (const b of iki) console.log(`      ${b}`);
  }
});
console.log('');
console.log(`iki surumun birlikte kostugu adim : ${uyumlu} / ${ADIM.length}`);
console.log(`kosamadigi adim                   : ${ADIM.length - uyumlu}`);
console.log(`sema adimi                        : ${semaAdimi}, geri alinabilen ${geriAlinir}`);
```

```
adim                                    sema      kosan  hedef  uyumlu  sema geri alinir
1 genislet: endeks sutunlari eklenir     genislet  v1     v1     evet    evet
2 cift yazan surum yayinlanir            —         v2     v1     evet    —
3 tasi: eski satirlar geri doldurulur    tasi      v2     v1     evet    evet
4 m3 yazmayi birakan surum yayinlanir    —         v3     v2     evet    —
    iki surum geri (v3 ile v1): uyumsuz
      v1, v3 kaydini null okudu
5 geri dususu kaldiran surum yayinlanir  —         v4     v3     evet    —
6 daralt: m3 sutunu silinir              daralt    v4     v3     hayir   hayir
    v3 okuyamadi: no such column: m3

iki surumun birlikte kostugu adim : 5 / 6
kosamadigi adim                   : 1
sema adimi                        : 3, geri alinabilen 2
```

Altı adımın beşinde iki sürüm birlikte koştu. Uyumsuz tek adım sonuncusudur ve geri alınamayan
tek şema adımı da odur. İki özellik aynı adımda buluşuyor: eski sütunu silmek hem önceki kod
sürümünü çalışamaz hâle getiriyor hem de kendisi geri alınamıyor.

Dördüncü adımdaki ek satır, uyumluluğun **kaç sürüm geriye** sorusuna bağlı olduğunu gösteriyor.
Bir sürüm geri — koşan sürüm ile ondan önceki — uyumlu. İki sürüm geri uyumsuz: metreküp
sütununa yazmayı bırakan sürümün kaydını, iki kuşak önceki sürüm boş okuyor. Hata yok, kayıt
var, değer yok. Geri alma penceresi bu yüzden tek sürümdür; iki sürüm geri dönmek isteyen bir
plan, kod adımlarını tek tek çıkmak zorundadır.

Üç şema adımının ikisi geri alınabilir. Genişletme sütunları düşürerek, taşıma yeni sütunları
boşaltarak geri alınır — değerler eski sütunda durmaya devam ettiği için ikisinde de kayıp yok.
Daraltma katalogdaki altıncı satırın kendisidir.

Kod merdiveninin dört basamaklı olması da bu ölçümden çıkıyor. Çift yazan sürüm eski sütunu
doldurmaya devam ettiği için önceki sürüm okumaya devam edebiliyor. Metreküp yazmayı bırakan
sürüm bir sonraki basamak, geri düşüşü kaldıran sürüm ondan sonraki. Dördüncü basamak olmadan
daraltma adımı koşamaz: geri düşüşlü okuma silinen sütuna başvurur ve o an üretimde koşan sürüm
kırılır. Basamaklardan biri atlanırsa uyumsuz adım sayısı birden ikiye çıkar.

#### Uyumsuz Adımın Bedeli

Uyumsuz adım iki yerde fatura kesiyor. Birincisi yayın penceresidir. İki sürümün birlikte
koşamadığı bir adım, aşamalı olarak dağıtılamaz: düğümlerin bir kısmının eski, bir kısmının yeni
sürümde olduğu her an bir arıza anıdır. Beş uyumlu adımın her biri düğüm düğüm ilerleyebilir;
altıncı adım tek seferde ve bütün düğümlerde aynı anda geçmek zorundadır. Aşamalı yayının
sağladığı kademeli gözlem penceresi bu adımda yoktur.

İkincisi geri alma planıdır. İlk beş adımda geri alma önceki dersin "yalnız kod" senaryosudur:
beş adım, sekiz tur, dönüşü olmayan nokta yok. Altıncı adımdan sonra plan "kod ile veri"
senaryosuna geçer: sekiz adım, provasız yirmi tur ve altıncı adımda kapanan bir dönüş. Tek bir
göç adımı, geri alma planını iki buçuk kat uzatıyor ve içine geri dönülemez bir nokta koyuyor.

Buradaki kapı, göç adımını üretime bırakmadan önce eski sürümün sorgularını yeni şemaya karşı
koşturan **uyumluluk denetimidir**. Tuttuğu bir adımdır: altı adımın altıncısı. Bu adımı yalnız
bu kapı tutar — göç adımının kendi sınamaları yeni şemada koşar ve eski sorguyu hiç görmez.
Kaçırdığı ise katalogdaki beşinci satırdır: hassasiyet daraltması her iki sürümle de uyumludur,
kapı onu geçirir, ama geri alınamaz. Uyumluluk ile geri alınabilirlik ayrı eksenlerdir ve bu kapı
yalnız birine bakar.

Kapının yanlış tutması hedef seçiminden doğuyor. Denetim, koşan sürümü bir önceki yerine en
eski dağıtılabilir sürümle karşılaştırırsa dördüncü adım da durdurulur — çıktının gösterdiği
gibi o karşılaştırma uyumsuz çıkar. Altı adımın biri boşuna beklemiş olur. Eklediği süre üç
şema adımı için üç turdur; kaçırdığında ödenen ise yirmi turluk bir geri alma planıdır.

#### Özet

- Altı göç türünün dördü ters işlemden sonra tabloyu özdeş bıraktı; hassasiyet daraltması beş
  satırı, sütun silme altı satırı geri getirmedi — ikisinde de ters komut hatasız çalıştı.
- Geri alınabilirliğin ölçüsü komutun varlığı değil içeriğin özdeşliğidir; bu ölçüyle "geri alma
  betiği yazıldı" ifadesi sınanabilir hâle gelir.
- Genişlet–taşı–daralt altı adımın beşinde iki sürümü birlikte taşıdı; uyumsuz tek adım, geri
  alınamayan tek şema adımıyla aynı adımdı.
- Geri alma penceresi tek sürümdür: bir sürüm geri uyumlu çıkan dördüncü adım, iki sürüm geri
  gidildiğinde eski sürüme boş değer okuttu.
- Uyumsuz adım yayın penceresini aşamalıdan tek seferliğe indirdi ve geri alma planını beş
  adım sekiz turdan sekiz adım yirmi tura çıkardı.

#### Sonraki Adım

Bu konu boyunca yayın kararının nereden geldiği ölçüldü: hangi ölçüte bakılarak terfi ediliyor,
geri alma birimi ne, plan kaç adım, göç adımı iki sürümü taşıyor mu. Bütün bu kararların
sonucunda ortaya bir istenen durum çıkıyor — şu sürüm, şu bayrak değeri, şu şema. Ama o istenen
durumun kendisi hiçbir yerde yazılı değil. Onu üretime taşıyan şey hâlâ bir arayüzde tıklanan
düğme ya da bir kişinin elindeki komut; üretimde gerçekte ne koştuğunu öğrenmenin yolu da
üretime bakmak. Sonraki konu bu soruyla açılır: istenen durum nerede durur, kim değiştirir ve
gerçek durumun ondan sapıp sapmadığı nasıl ölçülür.

## Depo Tabanlı Dağıtım ve Tedarik Zinciri

İstenen durumun sürüm kontrolünde tutulması ve çıktının kaynağının doğrulanması: depo tabanlı istenen durum ilkesi, küme içi uzlaştırıcı ajanlarla çekme tabanlı dağıtım, depoda şifreli sır saklama yaklaşımları, bağımlılık envanterinin üretilmesi, yapı çıktısının imzalanması ve doğrulanması, en az yetkili kimlikler ve yalıtılmış derleme ortamıyla hattın kendi güvenliği.

### Depo Tabanlı İstenen Durum

Source: https://academia.sh/tr/kurslar/ci-cd/depo-tabanli-istenen-durum

Aynı değişiklik akışı iki düzende koşturulur — elden uygulama ve depoya işleme — ve iki düzenin izlenebilirliği, kayıtsız değişiklik oranı, dönem sonu sapması ve değişiklik başına adım sayısı 180 değişiklikte sayılır. Gözden geçirme kapısının yalnız kendi yakaladığı kusur, yanlış tuttuğu temiz değişiklik ve eklediği 796 saat ayrı ayrı ölçülür.

Önceki konular yayının nasıl verileceğini kurdu: hangi ölçüte bakılarak terfi edileceği, yüzdelik
dağıtımın hangi eşikte durdurulacağı, geri alma planının kaç adım olduğu. Bu kararların hepsinin
ortak bir yanı vardı — sonunda biri bir arayüzde bir düğmeye basıyor ya da bir kabuk komutu
yazıyordu. Kararın kendisi kayıt altındaydı; kararın **uygulanması** değildi.

Bu ders o boşluğu ölçer. Soru şudur: kümenin uyguladığı istenen durum bir yerde **yazılı olarak**
duruyor mu, yoksa yalnız çalışan sistemin belleğinde mi? **Depo tabanlı istenen durum** ilkesi tek
bir cümledir — bir ortamda ne çalışacağının tanımı bir depoda durur ve o depo dışında hiçbir yoldan
değiştirilmez. İlkenin kendisi kısa, faturası uzundur; ders faturayı çıkarır.

Ölçüm **bölgesel ölçüm ağı** üzerindedir ve **kurgudur**: su sayacı okuma toplayıcısı, doğrulayıcı,
faturalama ve iş emri servisleri, üç ortam. Koşan şey `node` ile kurulmuş bir **modeldir**, gerçek
bir sistem değil.

**DD1.** İstenen durum 60 kalemdir: dört servis × üç ortam × beş alan (kopya sayısı, eşik, sürüm,
ağ kuralı, zamanlama).
**DD2.** Doksan günde 180 değişiklik üretilir; üreteç kendi yazılmıştır, tohum 20260801 görünürdür.
**DD3.** Değişikliklerin bir bölümü **acil**dir (vardiya dışı müdahale); %20'si bilinen bir kusur
taşır: biçim, eşik, ortam, koşum anı.
**DD4.** Elden düzende kayıt gönüllüdür — uygulayan isterse bir yere yazar. Depo düzeninde işleme
kaydı kim, ne zaman ve gerekçe alanlarını zorunlu taşır.
**DD5.** Depo düzeninde de acil değişikliklerin bir bölümü depoyu atlar; ilke %100 uygulanmaz.
**DD6.** İki kapı vardır ve ikisi de yalnız depo yolundadır: şema doğrulayıcısı (otomatik) ve
gözden geçirme (insan).

#### Aynı Değişiklik İki Yolla

```js
// akis.mjs — bolgesel olcum agi (kurgu) uzerinde 90 gunluk degisiklik akisi modeli.
// Ayni akis iki duzende uygulanir: elden uygulama ve depoya isleme. Kosan sey bir
// modeldir, gercek bir sistem degil. Rastgelelik kendi uretecimizden gelir, tohum gorunur.
export const uretec = (t) => { let s = t >>> 0;
  return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; };

const SERVIS = ["toplayici", "dogrulayici", "faturalama", "isemri"];
const ORTAM = ["gelistirme", "hazirlik", "uretim"];
const ALAN = ["kopya", "esik", "surum", "ag-kurali", "zamanlama"];
export const KALEM = SERVIS.flatMap((s) => ORTAM.flatMap((o) => ALAN.map((a) => `${s}/${o}/${a}`)));

export function akis(tohum = 20260801, adet = 180) {          // 90 gunluk degisiklik akisi
  const r = uretec(tohum), liste = [];
  for (let i = 0; i < adet; i++) {
    const kalem = KALEM[Math.floor(r() * KALEM.length)];
    const acil = r() < 0.22;                                   // vardiya disi mudahale
    const p = r();                                             // bilinen kusur kumesi: %20
    const kusur = p < 0.06 ? "bicim" : p < 0.12 ? "esik" : p < 0.16 ? "ortam"
                : p < 0.20 ? "kosum" : null;
    liste.push({ i, gun: Math.floor(r() * 90), kalem, acil, kusur });
  }
  return liste;
}

// depo=false: her degisiklik elden uygulanir, kayit gonullu.
// depo=true : degisiklik depoya islenir; acil olanlarin bir kismi yine de depoyu atlar.
export function duzen(liste, { depo, tohum = 7717 }) {
  const r = uretec(tohum);
  const gercek = new Map(), defter = new Map();                // kalem -> surum numarasi
  let kim = 0, gerekce = 0, kayitsiz = 0, atlayan = 0, adim = 0, incelenen = 0;
  for (const d of liste) {
    const depoYolu = depo && !(d.acil && r() < 0.35);
    const s = (gercek.get(d.kalem) ?? 0) + 1;
    gercek.set(d.kalem, s);
    if (depoYolu) {                                            // dal, degisiklik, isleme,
      defter.set(d.kalem, s);                                  // oneri, gozden gecirme,
      kim++; gerekce++; incelenen++; adim += 7;                // birlestirme, uygulama
    } else {
      atlayan++; adim += 2;                                    // komut yaz, uygula
      const kaydeder = r() < (d.acil ? 0.25 : 0.7);
      if (kaydeder) { kim++; if (r() < 0.45) gerekce++; } else kayitsiz++;
      if (r() < (d.acil ? 0.1 : 0.3)) defter.set(d.kalem, s);  // sonradan deftere gecirme
    }
  }
  let sapan = 0;
  for (const [k, s] of gercek) if (defter.get(k) !== s) sapan++;
  return { n: liste.length, kim, gerekce, kayitsiz, atlayan, incelenen, sapan,
    dokunulan: gercek.size, adim: adim / liste.length };
}

if (import.meta.url === `file://${process.argv[1]}`) {
  const liste = akis();
  const acil = liste.filter((d) => d.acil).length;
  const kusurlu = liste.filter((d) => d.kusur).length;
  console.log(`tohum 20260801 | ${liste.length} degisiklik / 90 gun | ${KALEM.length} kalem`);
  console.log(`acil: ${acil} | bilinen kusur tasiyan: ${kusurlu}`);
  const BAS = ["duzen", "kim", "gerekce", "kayitsiz", "atlayan", "incelenen", "sapan kalem", "adim"];
  const G = [16, 5, 9, 10, 9, 11, 13, 7];
  const satir = (h) => h.map((v, j) => j ? String(v).padStart(G[j]) : String(v).padEnd(G[0])).join("");
  console.log("\n" + satir(BAS));
  for (const depo of [false, true]) {
    const x = duzen(liste, { depo });
    console.log(satir([depo ? "depoya isleme" : "elden uygulama", x.kim, x.gerekce, x.kayitsiz,
      x.atlayan, x.incelenen, `${x.sapan}/${x.dokunulan}`, x.adim.toFixed(2)]));
  }
}
```

```
tohum 20260801 | 180 degisiklik / 90 gun | 60 kalem
acil: 35 | bilinen kusur tasiyan: 36

duzen             kim  gerekce  kayitsiz  atlayan  incelenen  sapan kalem   adim
elden uygulama    117       54        63      180          0        45/56   2.00
depoya isleme     175      172         5        9        171         1/56   6.75
```

İzlenebilirlik sütunları beklenen yönde ayrışıyor. Elden düzende 180 değişikliğin 117'sinde kim ve
ne zaman bilgisi bir yere düşmüş, yalnız 54'ünde gerekçe var; 63 değişiklik — akışın %35'i — hiçbir
iz bırakmadan uygulanmış. Depo düzeninde gerekçe 172'ye çıkıyor, çünkü işleme kaydı boş
bırakılamıyor: kim, ne zaman ve niçin alanları değişikliğin taşıyıcısının kendisidir, ayrıca
doldurulan bir form değil.

Asıl sayı sondan üçüncü sütundadır. Doksan gün boyunca 60 kalemin 56'sına dokunulmuş; elden düzende
dönem sonunda bu 56 kalemin **45'i** depodaki değerden farklıdır. Yani sistemi anlatan belge,
sistemin beşte dördünde yanlıştır. Depo düzeninde sapan kalem sayısı 1'dir ve o bir kalem depoyu
atlayan dokuz acil değişiklikten arta kalandır.

Bu farkın işletim karşılığı, belgenin eskimesinden ağırdır. Elden düzende bir kalemin doğru
değerini bilen tek yer çalışan sistemin kendisidir; ortam yeniden kurulduğunda ya da bir kalem
sıfırlandığında geri dönülecek bir kaynak yoktur. Faturalama servisinin üretimdeki eşiği bir gece
elle değiştirilmiş ve kimse yazmamışsa, o eşik artık kimsenin kararı değil, bir kazadır. Depo
tabanlı istenen durumun ilk kazancı denetim değil, **geri dönülebilirliktir**: 56 kalemin 55'i için
depodaki değer, sistemin son bilinen doğru hâlidir.

#### Gözden Geçirme Kapısı Neyi Yalnız Kendisi Tutuyor

```js
// kapi.mjs — depo yolundaki gozden gecirme kapisinin defteri: tuttugu, YALNIZ kendisinin
// tuttugu, kacirdigi, yanlis tuttugu, ekledigi sure. Ayni akis modeli, ayri tohum.
import { akis, uretec } from "./akis.mjs";
const SEMA = { bicim: 1, esik: 0, ortam: 0, kosum: 0 };             // sema dogrulayicisi
const GOZ = { bicim: 0.85, esik: 0.55, ortam: 0.75, kosum: 0.05 };  // gozden gecirme

function kos(liste, { kacis, tohum = 4231 }) {
  const r = uretec(tohum), gercek = new Map(), defter = new Map();
  const ortam = {}, sinif = {};
  let atlayan = 0, kapiya = 0, semaYalniz = 0, marjinal = 0, ikisi = 0,
    gecen = 0, yanlis = 0, dakika = 0, tetikli = false;
  const uygula = (d, depoyla) => { const s = (gercek.get(d.kalem) ?? 0) + 1;
    gercek.set(d.kalem, s); if (depoyla) defter.set(d.kalem, s); return s; };
  for (const d of liste) {
    const o = (ortam[d.kalem.split("/")[1]] ??= { kapiya: 0, marjinal: 0, yanlis: 0, dk: 0 });
    const k = d.kusur ? (sinif[d.kusur] ??= { n: 0, goz: 0, sema: 0, gecen: 0 }) : null;
    if (k) k.n++;
    const atla = r() < (d.acil ? 0.35 : (kacis && tetikli ? 0.5 : 0));
    tetikli = false;
    if (atla) {                                                     // kapisiz yol
      atlayan++; const s = uygula(d, false);
      if (k) { gecen++; k.gecen++; }
      if (r() < 0.2) defter.set(d.kalem, s);
      continue;
    }
    kapiya++; o.kapiya++;
    const bekle = d.acil ? 5 + r() * 55 : 25 + r() * 575;           // gozden gecirme beklemesi
    dakika += bekle; o.dk += bekle;
    const sema = d.kusur ? r() < SEMA[d.kusur] : false;
    const goz = d.kusur ? r() < GOZ[d.kusur] : false;
    if (k && (sema || goz)) {                                       // durduruldu, uygulanmadi
      if (sema) k.sema++;
      if (goz) k.goz++;
      if (sema && goz) ikisi++; else if (sema) semaYalniz++; else { marjinal++; o.marjinal++; }
      continue;
    }
    if (!k && r() < 0.06) {                                         // temiz ama durduruldu
      yanlis++; o.yanlis++; tetikli = true;
      dakika += bekle * 0.6; o.dk += bekle * 0.6;                   // ikinci tur
    }
    uygula(d, true);
    if (k) { gecen++; k.gecen++; }
  }
  let sapan = 0; for (const [a, s] of gercek) if (defter.get(a) !== s) sapan++;
  return { atlayan, kapiya, semaYalniz, marjinal, ikisi, gecen, yanlis, sapan,
    dk: dakika / kapiya, saat: dakika / 60, ortam, sinif };
}

const yaz = (g, h) => h.map((v, j) => j ? String(v).padStart(g[j]) : String(v).padEnd(g[0])).join("");

if (import.meta.url === `file://${process.argv[1]}`) {
  const liste = akis(), kusurlu = liste.filter((d) => d.kusur).length;
  const G = [7, 9, 8, 13, 15, 8, 8, 8, 8, 12];
  console.log(`${liste.length} degisiklik, ${kusurlu} bilinen kusur; iki kapi: sema + gozden gecirme`);
  console.log("\n" + yaz(G, ["kacis", "atlayan", "kapiya", "yalniz sema", "yalniz gozden",
    "ikisi", "gecen", "yanlis", "sapan", "dk/inceleme"]));
  for (const kacis of [false, true]) {
    const x = kos(liste, { kacis });
    console.log(yaz(G, [kacis ? "var" : "yok", x.atlayan, x.kapiya, x.semaYalniz, x.marjinal,
      x.ikisi, x.gecen, x.yanlis, x.sapan, x.dk.toFixed(0)]));
  }
  const x = kos(liste, { kacis: false });
  const S = [12, 7, 7, 8, 8];
  console.log("\n" + yaz(S, ["kusur", "adet", "sema", "gozden", "gecen"]));
  for (const [ad, k] of Object.entries(x.sinif))
    console.log(yaz(S, [ad, k.n, k.sema, k.goz, k.gecen]));
  const O = [13, 8, 15, 15, 13, 15];
  console.log("\n" + yaz(O, ["ortam", "kapiya", "yalniz gozden", "yanlis tuttu",
    "toplam saat", "saat/marjinal"]));
  for (const [ad, o] of Object.entries(x.ortam))
    console.log(yaz(O, [ad, o.kapiya, o.marjinal, o.yanlis, (o.dk / 60).toFixed(1),
      o.marjinal ? (o.dk / 60 / o.marjinal).toFixed(1) : "-"]));
  console.log(`\ntoplam gozden gecirme yuku: ${x.saat.toFixed(0)} saat / 90 gun`);
}
```

```
180 degisiklik, 36 bilinen kusur; iki kapi: sema + gozden gecirme

kacis    atlayan  kapiya  yalniz sema  yalniz gozden   ikisi   gecen  yanlis   sapan dk/inceleme
yok           13     167            1             13      11      11       7       4         286
var           20     160            1             15       9      11       9       7         270

kusur          adet   sema  gozden   gecen
esik              8      0       3       5
bicim            12     12      11       0
ortam            11      0       9       2
kosum             5      0       1       4

ortam          kapiya  yalniz gozden   yanlis tuttu  toplam saat  saat/marjinal
hazirlik           67              6              2        312.5           52.1
gelistirme         44              1              2        193.7          193.7
uretim             56              6              3        289.9           48.3

toplam gozden gecirme yuku: 796 saat / 90 gun
```

İki kapı da iş görüyor, ama defterleri birbirine benzemiyor. Şema doğrulayıcısı 12 biçim kusurunu
tuttu; bunların 11'ini gözden geçirme de yakalardı, dolayısıyla **yalnız şemanın** tuttuğu kusur
sayısı 1'dir. Gözden geçirme 24 kusur tuttu ve bunların 13'ünü tek başına yakaladı — öteki kapı
olsa da olmasa da geçecek olan kusurlar bunlardır. Marjinal değer sıralaması nettir: şema kapısı 1,
gözden geçirme 13.

Kusur sınıfı tablosu bu sıralamanın nedenini gösteriyor. Biçim kusurunun 12'si de tutuldu, hiçbiri
geçmedi; bu sınıfı iki kapı da görüyor ve şema kapısı bunu saniyeler içinde yapıyor. Eşik ve ortam
kusurları şema için görünmezdir: değer geçerli, alan doğru, yalnız sayı ya da hedef yanlıştır.
Bunları yalnız okuyan bir insan yakalar — 8 eşik kusurunun 3'ü, 11 ortam kusurunun 9'u tutuldu.

Kapının kaçırdığı 11 kusur da sınıfına göre okunur: 5 eşik, 2 ortam, 4 koşum anı. Koşum anı sınıfı
beş kusurun dördünü geçirdi, çünkü bu sınıf yazılı metne bakarak görülmez — değer ancak servis
gerçek veriyle karşılaştığında yanlış olur. Statik bir kapının önünde duran bir sınır burasıdır ve
gözden geçirmenin süresini uzatmak bu sınırı geriye itmez.

Yanlış tutma sütunu kapının öteki yüzüdür: 7 temiz değişiklik durduruldu ve her biri ikinci bir tur
üretti. İnceleme başına ortalama bekleme 286 dakikadır; doksan günde toplam **796 saat**, günde
yaklaşık dokuz saatlik bir bekleme yükü. Bu süre bir işgücü maliyeti değil, öncü sürenin kendisidir:
değişiklik yazıldıktan sonra ortalama dört saat kırk altı dakika boyunca hiçbir yere gitmez.

#### Kapıdan Kaçmanın Ölçülen İzi

Tablonun ikinci satırı, yanlış tutmanın ekipte ürettiği davranışı modelliyor: boşuna durdurulan bir
değişikliğin ardından gelen değişikliğin acil olmasa bile kapısız yoldan gitme olasılığı artıyor.
Kapıyı atlayan değişiklik 13'ten 20'ye, dönem sonunda sapan kalem 4'ten 7'ye çıkıyor. Üretime geçen
kusur sayısı iki koşumda da 11'dir; 180 değişiklik ölçeğinde bu fark üretecin gürültüsünün altında
kalır ve öyle sunulmalıdır. Ölçülebilen etki kusur sayısında değil, **kapının kendi ürettiği
sapmada**dır: bir kapıya güven azaldığında kayıp önce izlenebilirlikte görünür.

Ortam tablosu ise kaldırılması gereken kapıyı doğrudan işaret ediyor. Geliştirme ortamına giden 44
değişiklik gözden geçirildi, bu incelemelerin toplamı 193,7 saat sürdü ve **yalnız 1** kusur
marjinal olarak yakalandı — kusur başına 193,7 saat. Aynı ölçü üretimde 48,3, hazırlıkta 52,1
saattir. Üstelik geliştirme ortamındaki 2 yanlış tutma, marjinal yakalamanın iki katıdır: kapı
burada tuttuğundan çok boşuna durduruyor.

Buradan çıkan karar ilkeye dokunmaz, kapıya dokunur: geliştirme ortamının değişiklikleri depoya
işlenmeye devam eder, gözden geçirme kapısı kalkar. Ayrım bu dersin taşıdığı en pratik sonuçtur —
**depoya işleme ile gözden geçirme aynı şey değildir.** İzlenebilirlik, gerekçe ve geri
dönülebilirlik kazancı işleme kaydından gelir; kapıdan gelen tek şey tutulan kusurdur ve o da
ortama göre çok farklı fiyatlanır.

#### İlkenin Bedeli: Değişiklik Başına Ek Adım

İlk tablonun son sütunu ilkenin doğrudan faturasıdır. Elden uygulama değişiklik başına 2 adımdır:
komut yazılır, uygulanır. Depo yolu 7 adımdır — dal açılır, dosya değiştirilir, işlenir, öneri
açılır, gözden geçirme beklenir, birleştirilir, uygulanır. Karışım ortalaması 6,75'tir; fark
değişiklik başına 4,75 adım, doksan günde 855 ek adımdır.

Bu adımların hepsi aynı ağırlıkta değildir. Altısı yazan kişinin dakikalarıdır; yalnız biri —
gözden geçirme beklemesi — başkasının takvimine bağlıdır ve ölçülen 286 dakikanın tamamı oradan
gelir. Acil yolun varlığı da bu yüzden sürüyor: 35 acil değişikliğin 13'ü kapıyı atladı, çünkü
gece yarısı dört saatlik bir bekleme kesintiyi uzatır. İlkeyi %100 uygulamak, ölçülen kayıtsız
değişiklik oranını 5'ten 0'a indirir ama acil müdahalenin süresine dört saat ekler; hangi tarafın
seçildiği ölçülmeden savunulamaz.

#### Özet

- Elden düzende 180 değişikliğin 63'ü (%35) hiçbir iz bırakmadan uygulandı ve gerekçe yalnız 54
  değişiklikte vardı; depo düzeninde kayıtsız değişiklik 5'e, gerekçesiz değişiklik 8'e indi.
- Dönem sonunda dokunulan 56 kalemin elden düzende 45'i depodaki değerden sapmıştı, depo düzeninde
  1'i; ilkenin ilk kazancı denetim değil, geri dönülebilirliktir.
- Şema kapısı 12 kusur tuttu ama yalnız 1'ini tek başına yakaladı; gözden geçirme 24 tuttu, 13'ünü
  tek başına — marjinal değer sıralaması 1'e karşı 13'tür.
- Kapı 11 kusuru kaçırdı (5 eşik, 2 ortam, 4 koşum anı) ve 7 temiz değişikliği boşuna durdurdu;
  inceleme başına 286 dakika, doksan günde 796 saat ekledi.
- Geliştirme ortamında kapı 193,7 saatte 1 marjinal kusur yakaladı ve 2 temiz değişikliği durdurdu;
  aynı ölçü üretimde 48,3 saat. Bu kapı geliştirme ortamında kaldırılır, işleme kaydı kalır.
- İlkenin adım maliyeti değişiklik başına 2'den 6,75'e çıkıyor; ek adımların yalnız biri başkasının
  takvimine bağlıdır ve ölçülen beklemenin tamamı oradan gelir.

#### Sonraki Adım

Bu ders istenen durumun **nerede yazılı olduğunu** ölçtü ve depodaki yazının değerini sapan kalem
sayısıyla gösterdi. Açık kalan soru bir sonraki dersin konusudur: depoda duran o yazıyı kümeye kim
taşıyor? Şimdiye kadarki düzende taşıyan taraf hattır — hat kümeye bağlanır ve yazar. Bunun bir
alternatifi vardır: küme içinde çalışan bir ajanın depoyu okuyup farkı kendisinin kapatması.
Ölçülecek kalemler bellidir: hattın küme üzerindeki yetki yüzeyi, elle yapılan bir değişikliğin
fark edilme gecikmesi, ağ bağlantısının yönü ve hat düştüğünde ne olduğu.

### Çekme Tabanlı Dağıtım

Source: https://academia.sh/tr/kurslar/ci-cd/cekme-tabanli-dagitim

Depodaki istenen durumu kümeye taşıyan iki düzen — hattın kümeye yazması ve küme içindeki ajanın depoyu okuması — aynı kalemlerde sayılır: yetki yüzeyi, bağlantı yönü, elle yapılan sapmanın fark edilme gecikmesi ve hat düştüğünde ne olduğu. Uzlaştırma turunun marjinal değeri 17 sapma, yanlış geri aldığı doğru müdahale 9 çıkar.

Bir önceki ders istenen durumun depoda durmasının ne kazandırdığını saydı: kayıtsız değişiklik
63'ten 5'e, dönem sonunda sapan kalem 45'ten 1'e indi. O ölçümde sessiz kalan bir kalem vardı —
depodaki yazıyı kümeye kim taşıyor. Şimdiye kadarki bütün düzeneklerde taşıyan taraf hattır: hat
kümenin denetim düzlemine bağlanır, kimlik bilgisiyle kendini tanıtır ve nesneleri yazar. Buna
**itme** denir.

İkinci bir yol vardır: kümenin içinde çalışan bir **uzlaştırıcı ajan** depoyu okur, kendi ortamının
gerçek durumuyla karşılaştırır ve farkı kendisi kapatır. Buna **çekme** denir. Uzlaşma döngüsünün
mekaniği küme mimarisi konusunda ölçülmüştü — periyot, gecikmeli görüntü, salınım — ve burada
tekrarlanmaz. Bu dersin ölçtüğü tek şey **kaynağın depo olmasının** bu iki taşıma yönünde ne
değiştirdiğidir.

Ölçüm yine **bölgesel ölçüm ağı** üzerindedir ve **kurgudur**; koşan şey `node` ile kurulmuş bir
**modeldir**.

**DD7.** Model 4 servis × 3 ortam = 12 ad alanı, 6 nesne türü, 72 nesne örneği ve 4 işlem taşır.
**DD8.** İtmede tek özne — hat çalıştırıcısı — 12 ad alanının hepsinde yetkilidir. Çekmede her
ortamda bir ajan vardır ve yalnız kendi ortamının 4 ad alanında yetkilidir.
**DD9.** Otuz günde 60 elle değişiklik olur; üreteç kendi yazılmıştır, tohum 31337. Üç sınıf:
dağıtımın her koşumda yazdığı alan, dağıtımın hiç yazmadığı alan, ajanın sahiplenmediği nesne.
**DD10.** Elle değişikliklerin bir bölümü gerçek bir kesintiyi kapatan **doğru** müdahaledir; bir
bölümü kısa ömürlüdür — operatör dakikalar içinde kendisi geri alır.
**DD11.** Dağıtım takvimi yazılıdır: dört servis sırayla 720, 480, 2880 ve 4320 dakikada bir dağıtır.
**DD12.** On ikinci günde 24 saatlik bir hat kesintisi olur; o pencerede dağıtım koşmaz, ajan koşar.

#### Yetki Yüzeyi ve Bağlantının Yönü

```js
// yetki.mjs — itme ve cekme duzenlerinde yetki yuzeyi sayimi. Rastgelelik yok; sayim
// bir modelin nesne agaci uzerinde yapilir, gercek bir kume degil.
const SERVIS = ["toplayici", "dogrulayici", "faturalama", "isemri"];
const ORTAM = ["gelistirme", "hazirlik", "uretim"];
const NESNE = ["dagitim", "hizmet", "yapilandirma", "sir-basvurusu", "zamanli-is", "ag-kurali"];
const YAZMA = ["yaz", "sil", "yama"], ISLEM = ["oku", ...YAZMA];

const adAlani = ORTAM.flatMap((o) => SERVIS.map((s) => `${o}/${s}`));
const ornek = adAlani.flatMap((a) => NESNE.map((n) => `${a}/${n}`));   // nesne ornekleri

const duzen = {
  itme: {                                        // hat kumeye yazar
    ozne: "hat calistiricisi", kapsam: adAlani,
    disaAcik: ORTAM.length,                      // her ortamin denetim duzlemi disaridan erisilir
    hatElinde: ORTAM.length,                     // hattin tuttugu kume kimligi sayisi
    baglantiYonu: "hat -> kume (disaridan iceriye)",
  },
  cekme: {                                       // ajan depoyu okur
    ozne: "ortam basina bir ajan", kapsam: adAlani.filter((a) => a.startsWith("uretim/")),
    disaAcik: 0, hatElinde: 0,
    baglantiYonu: "kume -> depo (iceriden disariya)",
  },
};

const say = (k) => ({ yetkiKaydi: k.length * NESNE.length * ISLEM.length,
  yazmaKaydi: k.length * NESNE.length * YAZMA.length,
  yazilabilirOrnek: ornek.filter((o) => k.some((a) => o.startsWith(a + "/"))).length });

console.log(`model: ${SERVIS.length} servis x ${ORTAM.length} ortam = ${adAlani.length} ad alani, ` +
  `${NESNE.length} nesne turu, ${ornek.length} nesne ornegi, ${ISLEM.length} islem`);
console.log("\n" + "duzen".padEnd(7) + "ozne".padEnd(23) + "ad alani".padStart(8) +
  "yetki".padStart(7) + "yazma".padStart(7) + "ele gecerse yazilabilir".padStart(24));
for (const [ad, d] of Object.entries(duzen)) {
  const s = say(d.kapsam);
  console.log(ad.padEnd(7) + d.ozne.padEnd(23) + String(d.kapsam.length).padStart(8) +
    String(s.yetkiKaydi).padStart(7) + String(s.yazmaKaydi).padStart(7) +
    String(s.yazilabilirOrnek).padStart(24));
}
console.log("\n" + "duzen".padEnd(7) + "disa acik denetim duzlemi".padStart(25) +
  "hattin elindeki kume kimligi".padStart(29) + "  baglanti yonu");
for (const [ad, d] of Object.entries(duzen))
  console.log(ad.padEnd(7) + String(d.disaAcik).padStart(25) + String(d.hatElinde).padStart(29) +
    "  " + d.baglantiYonu);
```

```
model: 4 servis x 3 ortam = 12 ad alani, 6 nesne turu, 72 nesne ornegi, 4 islem

duzen  ozne                   ad alani  yetki  yazma ele gecerse yazilabilir
itme   hat calistiricisi            12    288    216                      72
cekme  ortam basina bir ajan         4     96     72                      24

duzen  disa acik denetim duzlemi hattin elindeki kume kimligi  baglanti yonu
itme                           3                            3  hat -> kume (disaridan iceriye)
cekme                          0                            0  kume -> depo (iceriden disariya)
```

Tablonun ilk okunuşu yanıltıcıdır. Çekme düzeninde ajan başına 96 yetki kaydı görünüyor, itmede
288; ama ortam başına bir ajan olduğu için toplam yetki kaydı çekmede de 3 × 96 = **288**'dir.
Yetki yüzeyi küçülmüyor, **bölünüyor**. Değişen sayı son sütundur: ele geçen bir hat çalıştırıcısı
72 nesne örneğinin hepsine yazabilir, ele geçen bir ajan yalnız kendi ortamının 24'üne. Üç ortamın
üretim olanı ayrı bir öznenin arkasındadır ve geliştirme ortamındaki bir sorun oraya erişemez.

İkinci tablo daha keskin bir farkı gösteriyor. İtmede üç denetim düzlemi dışarıdan erişilebilir
olmalıdır ve hat üç küme kimliğini elinde tutar; çekmede ikisi de sıfırdır, çünkü bağlantıyı küme
kurar. Bunun işletim karşılığı somuttur: itmede hattın koştuğu yer güvenlik sınırının içindeymiş
gibi davranmak zorundadır — hat çalıştırıcısı üç üretim ortamına yazabilen bir öznedir. Çekmede
hattın tek yetkisi depoya yazmaktır; kümeler onu tanımaz bile.

Bu bir kapı sayımı değildir, ama kapıların üzerinde durduğu zemini belirler: itmede depo tabanlı
istenen durum ilkesi bir **sözleşmedir** — hat isterse kapıları atlayıp doğrudan yazabilir. Çekmede
ilke bir **kısıttır**: kümenin dışından yazacak yetki yoktur.

#### Sapmayı Kim, Ne Zaman Kapatıyor

```js
// sapma.mjs — 30 gunluk elle degisiklik akisi iki duzende sayilir: itme (hat kumeye yazar)
// ve cekme (ajan depoyu okur). Model kosturulur; uretec kendi yazildi, tohum gorunur.
const uretec = (t) => { let s = t >>> 0;                     // ayni dogrusal eslesmeli uretec
  return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; };
const SURE = 30 * 1440, KESINTI = [17820, 19260];            // 12. gun, 24 saatlik hat kesintisi
const TAKVIM = { toplayici: 720, dogrulayici: 480, faturalama: 2880, isemri: 4320 };
const SERVIS = Object.keys(TAKVIM);

function olaylar(tohum = 31337, adet = 60) {
  const r = uretec(tohum), l = [];
  for (let i = 0; i < adet; i++) {
    const p = r();
    l.push({ t: Math.floor(r() * SURE), servis: SERVIS[Math.floor(r() * 4)],
      sinif: p < 0.45 ? "yazilan" : p < 0.85 ? "yazilmayan" : "kapsamdisi",
      dogru: r() < 0.2,                                       // gercek kesintiyi kapatan duzeltme
      omur: r() < 0.25 ? 5 + Math.floor(r() * 25) : Infinity });// operatorun kendi geri aldigi
  }
  return l;
}

const sonrakiDagitim = (servis, t) => {                        // kesinti penceresi atlanir
  const p = TAKVIM[servis];
  for (let k = Math.floor(t / p) + 1; k * p <= SURE; k++)
    if (!(k * p >= KESINTI[0] && k * p < KESINTI[1])) return k * p;
  return null;
};

function kos(l, U) {                                           // U=null ise itme duzeni
  const s = { kapanan: 0, marjinal: 0, gecikme: 0, gorulmeyen: 0, kacan: 0,
    yanlis: 0, kalan: 0, pencereAcik: 0, pencereKapanan: 0 };
  for (const o of l) {
    const kapanis = U === null ? (o.sinif === "yazilan" ? sonrakiDagitim(o.servis, o.t) : null)
      : (o.sinif === "kapsamdisi" ? null : (Math.floor(o.t / U) + 1) * U);
    if (o.omur !== Infinity && (kapanis === null || o.t + o.omur < kapanis)) { s.gorulmeyen++; continue; }
    const bitis = (kapanis === null || kapanis > SURE) ? SURE : kapanis;
    if (o.t < KESINTI[1] && bitis > KESINTI[0]) s.pencereAcik++;   // kesinti boyunca acik sapma
    if (kapanis !== null && kapanis >= KESINTI[0] && kapanis < KESINTI[1]) s.pencereKapanan++;
    if (kapanis === null || kapanis > SURE) { s.kalan++; if (o.sinif === "kapsamdisi") s.kacan++; continue; }
    s.kapanan++; s.gecikme += kapanis - o.t;
    if (o.sinif === "yazilmayan") s.marjinal++;                // itmenin hic kapatamayacagi sinif
    if (o.dogru) s.yanlis++;                                   // dogru mudahale geri alindi/ezildi
  }
  s.ortGecikme = s.kapanan ? s.gecikme / s.kapanan : 0;
  s.yayilma = U === null ? 3 : U / 2 + 1;                      // isleme -> uygulama gecikmesi
  return s;
}

const G = [12, 9, 10, 13, 12, 9, 9, 8, 10];
const yaz = (h) => h.map((v, j) => j ? String(v).padStart(G[j]) : String(v).padEnd(G[0])).join("");

const l = olaylar();
console.log(`tohum 31337 | ${l.length} elle degisiklik / 30 gun | ` +
  `${l.filter((o) => o.dogru).length} tanesi gercek bir kesintiyi kapatan dogru duzeltme`);
console.log(`sinif: yazilan ${l.filter((o) => o.sinif === "yazilan").length}, ` +
  `yazilmayan ${l.filter((o) => o.sinif === "yazilmayan").length}, ` +
  `kapsam disi ${l.filter((o) => o.sinif === "kapsamdisi").length}`);
console.log("\n" + yaz(["duzen", "kapanan", "marjinal", "ort gecikme", "gorulmeyen", "kacan",
  "yanlis", "kalan", "yayilma"]));
console.log(yaz(["itme", ...(({ kapanan, marjinal, ortGecikme, gorulmeyen, kacan, yanlis, kalan,
  yayilma }) => [kapanan, marjinal, ortGecikme.toFixed(0), gorulmeyen, kacan, yanlis, kalan,
  yayilma])(kos(l, null))]));
for (const U of [1, 5, 15, 60, 240]) {
  const x = kos(l, U);
  console.log(yaz([`cekme U=${U}`, x.kapanan, x.marjinal, x.ortGecikme.toFixed(0), x.gorulmeyen,
    x.kacan, x.yanlis, x.kalan, x.yayilma.toFixed(1)]));
}
const kacanDagitim = SERVIS.reduce((a, sv) => a + Object.keys([...Array(SURE / TAKVIM[sv] | 0)])
  .filter((k) => (+k + 1) * TAKVIM[sv] >= KESINTI[0] && (+k + 1) * TAKVIM[sv] < KESINTI[1]).length, 0);
const i = kos(l, null), c = kos(l, 15);
console.log(`\n24 saatlik hat kesintisi: ${kacanDagitim} planli dagitim kacti, ` +
  `ajan ${(KESINTI[1] - KESINTI[0]) / 15} tur kostu`);
console.log(`pencere boyunca acik sapma — itme ${i.pencereAcik}, cekme(U=15) ${c.pencereAcik}; ` +
  `pencerede kapanan — itme ${i.pencereKapanan}, cekme ${c.pencereKapanan}`);
```

```
tohum 31337 | 60 elle degisiklik / 30 gun | 10 tanesi gercek bir kesintiyi kapatan dogru duzeltme
sinif: yazilan 32, yazilmayan 17, kapsam disi 11

duzen         kapanan  marjinal  ort gecikme  gorulmeyen    kacan   yanlis   kalan   yayilma
itme               26         0         1206          16        6        4      18         3
cekme U=1          49        17            1           5        6        9       6       1.5
cekme U=5          49        17            3           5        6        9       6       3.5
cekme U=15         48        17            8           6        6        9       6       8.5
cekme U=60         42        14           30          12        6        8       6      31.0
cekme U=240        39        12          135          15        6        7       6     121.0

24 saatlik hat kesintisi: 5 planli dagitim kacti, ajan 96 tur kostu
pencere boyunca acik sapma — itme 11, cekme(U=15) 7; pencerede kapanan — itme 0, cekme 3
```

İtme satırı bir kapı gibi görünmez, çünkü değildir. Altmış elle değişikliğin 26'sı kapandı, ama
kapanma bir fark etme değil bir **üstüne yazmadır**: bir sonraki dağıtım o alanı yeniden yazdığı
için sapma silinir. Ortalama süre 1206 dakikadır — yirmi saat — ve bu sürenin tamamı dağıtım
takvimine bağlıdır. Kimse sapmayı fark etmedi, kimse bir kayıt tutmadı; dönem sonunda 18 kalem
sapmış durumda kaldı.

Uzlaştırma turunun kapı defteri bunun karşısında durur. On beş dakikalık periyotta 48 sapma kapandı,
ortalama gecikme 8 dakika. **Marjinal** sütun bu kapının yalnız kendisinin yaptığı işi verir:
dağıtımın hiç yazmadığı alanlardaki 17 sapma. Bunlar itme düzeninde otuz gün boyunca hiç
kapanmayacak kalemlerdir — elle eklenmiş bir ağ kuralı, elle büyütülmüş bir kopya sayısı, elle
kapatılmış bir zamanlı iş. Kapının değeri 48'de değil, **17**'dedir.

Kaçırdığı da nettir: ajanın sahiplenmediği nesne türlerindeki 6 sapma bütün periyotlarda kaçtı ve
periyodu küçültmek bunu değiştirmedi. Kapsam bir ayar değil bir tanımdır; ajan neyi yönetmiyorsa
onu göremez. İkinci kaçırma sınıfı `gorulmeyen` sütunudur: iki tur arasında yapılıp operatörün
kendisi tarafından geri alınan değişiklikler. U=1'de 5, U=240'ta 15 tanesi hiç görülmedi. Uzun
periyotlu bir ajan yalnız yavaş değildir; kısa ömürlü müdahalelerin **hiçbir kaydını tutmaz**.

#### Uzlaştırma Turunun Yanlış Tuttuğu

`yanlis` sütunu bu kapının bedelidir. Otuz günde 10 elle değişiklik gerçek bir kesintiyi kapatan
doğru müdahaledir. Çekme düzeninde bunların 9'u ortalama dokuz dakika içinde geri alındı — ajan
depodaki yazıya bakar, müdahalenin niye yapıldığını bilmez ve kesintiyi geri getirir. İtmede aynı
sayı 4'tür ve geri alma ortalama otuz dört saat sonra, sessizce olur.

İkisi de yanlış tutmadır, ama biçimleri farklıdır ve karışıklık burada başlar. Çekmede yanlış
tutma **hızlı ve görünürdür**: ajan bir kayıt bırakır, müdahaleyi yapan kişi dokuz dakika içinde
geri alındığını görür ve doğru davranışı öğrenir — düzeltmeyi depoya işlemek. İtmede yanlış tutma
**yavaş ve sessizdir**: müdahale otuz dört saat çalışır, sonra bir dağıtım onu ezer ve kimse
ilişkiyi kurmaz. Kapının sayısı çekmede iki kattır; kapının verdiği zarar itmede daha büyüktür,
çünkü öğretmez.

Bu kapının doğru ayarı periyodu uzatmak değildir. U=240 satırında yanlış geri alma 9'dan 7'ye
iner, ama marjinal değer de 17'den 12'ye düşer ve görülmeyen sapma 15'e çıkar. Kapının doğru ayarı
**kapsamdır**: acil müdahalenin yapılacağı alanlar ajanın sahiplenmediği kümeye alınırsa hem
yanlış geri alma hem de öğrenme kaybolur. Ölçüm bu takasın fiyatını verir — o alanlardaki sapmalar
da artık kimsenin kapatmadığı 6 kalemin yanına eklenir.

#### Hat Düştüğünde ve Çekmenin Bedeli

Yirmi dört saatlik hat kesintisi iki düzenin dayanıklılığını ayırır. O pencerede 5 planlı dağıtım
kaçtı; itme düzeninde pencere boyunca açık kalan 11 sapmanın **hiçbiri** kapanmadı, çünkü kapatan
mekanizma dağıtımın kendisiydi. Ajan aynı pencerede 96 tur koştu ve açık 7 sapmanın 3'ünü kapattı.
Ayrım şudur: itmede hat hem yeni değişikliği taşır hem sapmayı kapatır, dolayısıyla hattın
kesintisi ikisini birden durdurur. Çekmede hat yalnız yeni değişikliği taşır; **son bilinen durumu
korumak hattan bağımsızdır.** Yeni yayın iki düzende de durur — bu bir üstünlük değil, işlerin
ayrılmasıdır.

Çekmenin faturası son sütundadır. İtmede bir işlemenin uygulanmaya dönüşmesi 3 dakika sürer; çekmede
periyodun yarısı artı uygulama, yani U=15'te 8,5 ve U=240'ta 121 dakika. Uzlaştırma turunun marjinal
değerini yüksek tutmak için periyodu kısaltmak gerekir, ama kısa periyot yayılmayı iyileştirirken
depoya ve denetim düzlemine sabit bir okuma yükü bindirir. İkinci kalem ajanın kendisidir: ortam
başına bir ajan, üç ayrı sürüm yükseltme penceresi, üç ayrı yetki kaydı kümesi ve ajanın kendisi
düştüğünde bunu fark edecek ayrı bir gözlem. Depo tabanlı istenen durum ilkesi ajanla birlikte
gelmez; **ajan ilkenin uygulanma biçimlerinden yalnız biridir** ve kendi bakımını ister.

#### Özet

- Toplam yetki kaydı iki düzende de 288'dir; çekmede yetki üç özneye bölünür ve ele geçen bir
  öznenin yazabildiği nesne örneği 72'den 24'e iner. Dışa açık denetim düzlemi 3'ten 0'a düşer.
- İtmede sapma fark edilmez, üstüne yazılır: 60 elle değişikliğin 26'sı ortalama 1206 dakika sonra
  silindi, 18'i dönem sonunda hâlâ duruyordu.
- Uzlaştırma turunun marjinal değeri 17 sapmadır — dağıtımın hiç yazmadığı alanlar. Kapının değeri
  kapattığı 48'de değil, itmenin asla kapatamayacağı 17'dedir.
- Kapı iki sınıfı kaçırır: ajanın sahiplenmediği 6 nesne (periyottan bağımsız) ve iki tur arasında
  yapılıp geri alınan değişiklikler (U=1'de 5, U=240'ta 15).
- Doğru bir acil müdahaleyi çekme 9 kez ortalama dokuz dakikada geri aldı, itme 4 kez otuz dört
  saat sonra sessizce ezdi; çekmenin yanlış tutması daha sık, itmeninki daha zararlıdır çünkü
  öğretmez.
- 24 saatlik hat kesintisinde itme 11 açık sapmanın 0'ını, ajan 7 açık sapmanın 3'ünü kapattı;
  bedeli yayılma gecikmesinin 3 dakikadan 8,5 dakikaya çıkması ve ortam başına bir ajanın bakımıdır.

#### Sonraki Adım

İki ders istenen durumun depoda durmasını ve oradan kümeye taşınmasını ölçtü. Kalan bir kalem
vardır ve depoya girmesi baştan sorunludur: servislerin ihtiyaç duyduğu sırlar. Veritabanı
bağlantısı, ölçüm ağının uzak uçlarına giden kimlik bilgileri ve imzalama anahtarları depoda düz
metin olarak duramaz — ama depoda durmazlarsa istenen durum eksik kalır ve ajan uygulayamaz.
Sonraki ders şifreli sır kaydını ölçer: anahtar nerede duruyor, kaç özne çözebiliyor, anahtar
döndürüldüğünde kaç kayıt yeniden şifreleniyor ve kaç dosyaya dokunuyor, geçmişte kalan şifreli
değerler ne kadar süre okunabilir kalıyor.

### Sır Yönetimi

Source: https://academia.sh/tr/kurslar/ci-cd/sir-yonetimi

Sırlar depoda şifreli tutulduğunda anahtarın nerede durduğu, kaç öznenin kaç kaydı çözebildiği ve anahtar döndürüldüğünde kaç kaydın yeniden şifrelenip kaç dosyaya dokunulduğu gerçek şifrelemeyle sayılır. Şifreli kaydın geçmişte kalması ölçülür: eski anahtar sızarsa geçmişteki 35 sürüm çözülüyor ve 11'i bugünkü değerdir.

Önceki iki ders istenen durumun depoda durmasını ve oradan kümeye taşınmasını ölçtü. İkisinde de
sessizce dışarıda bırakılan bir kalem vardı: servislerin ihtiyaç duyduğu **sırlar**. Toplayıcının
ölçüm ağı uçlarına bağlanmak için taşıdığı kimlik bilgisi, faturalamanın veritabanı parolası, iş
emri servisinin arşiv anahtarı — bunlar istenen durumun parçasıdır, ama depoya düz metin
konamazlar. Depoya konmazlarsa istenen durum eksiktir ve uzlaştırıcı ajan uygulayamaz.

Çözüm biçimi bellidir: sır depoda **şifreli** durur, çözme yetkisi ayrı bir eksende yönetilir. Bu
dersin ölçtüğü şey o eksendir — anahtar nerede duruyor, kaç özne kaç kaydı çözebiliyor, anahtar
döndürüldüğünde ne oluyor ve depoda kalan şifreli kaydın **geçmişi** ne anlama geliyor. Sır
sızıntısının hangi yollardan gerçekleştiği daha önce taranmıştı — kayıt satırları, ortam
değişkenleri, yapı çıktısı katmanları — ve o tarama burada tekrarlanmaz.

Ölçüm **bölgesel ölçüm ağı** üzerindedir ve **kurgudur**. Şifreleme `node:crypto` ile gerçekten
koşar; **sır değerlerinin tamamı uydurmadır** ve `kurgu-` önekiyle yazılır.

**DD13.** Envanter 33 sır kaydıdır: dört servis, üç ortam, servis başına iki-üç sır türü.
**DD14.** Şifreleme AES-256-GCM'dir, anahtar `scrypt` ile türetilir. Başlangıç vektörü kayıt
kimliğinden türetilir ki çıktı yeniden üretilebilsin; gerçek bir dağıtımda rastgele üretilir.
**DD15.** Üç anahtar yerleşimi karşılaştırılır: tek anahtar, ortam başına anahtar, ortam-servis
başına anahtar.
**DD16.** Depoyu okuyabilen 29 özne vardır; 14'ü hiçbir anahtar taşımaz. Her öznenin ihtiyaç
duyduğu (ortam, servis) çiftleri yazılıdır.
**DD17.** 180 günlük geçmişte her kayıt bir ile dört kez değişmiştir; her değişiklik ayrı bir
işlemede durur.
**DD18.** Üretim nöbeti iki servisin sırlarına ihtiyaç duyar; 180 günde 24 gece olayı olur ve
anahtarsız kalınan her olay 35 dakika eskalasyon ekler.

#### Anahtar Nerede Duruyor, Kim Çözebiliyor

```js
// sir.mjs — depoda sifreli sir kaydi: envanter, anahtar yerlesimi, cozme yetkisi.
// SIR DEGERLERI KURGUDUR, gercek bir sir yazilmaz. Sifreleme node:crypto ile gercekten kosar.
// IV kayit kimligi ve anahtar surumunden turetilir ki cikti yeniden uretilebilsin.
import { scryptSync, createCipheriv, createDecipheriv, createHash } from "node:crypto";

export const ORTAM = ["gelistirme", "hazirlik", "uretim"];
export const SERVIS = { toplayici: ["veritabani", "olcum-agi-jetonu", "onbellek"],
  dogrulayici: ["veritabani", "imzalama"], faturalama: ["veritabani", "odeme-jetonu", "imzalama"],
  isemri: ["veritabani", "bildirim-jetonu", "arsiv"] };

export const KAYIT = ORTAM.flatMap((o) => Object.entries(SERVIS).flatMap(([s, tl]) =>
  tl.map((t) => ({ id: `${o}/${s}/${t}`, ortam: o, servis: s,
    deger: `kurgu-${t}-${s}-${o}-0001` }))));             // kurgu deger; gercek sir degil

export const sayac = { turetme: 0, sifreleme: 0, cozme: 0 };
const kasa = new Map();
export const anahtar = (ad, surum = 1) => {               // parola da kurgudur
  const k = `${ad}#${surum}`;
  if (!kasa.has(k)) { sayac.turetme++;
    kasa.set(k, scryptSync(`kurgu-parola-${k}`, `tuz-${ad}`, 32, { N: 16384 })); }
  return kasa.get(k);
};

export const YERLESIM = { tek: () => "hepsi", ortam: (k) => k.ortam,
  "ortam-servis": (k) => `${k.ortam}/${k.servis}` };

export const sifrele = (kayit, ad, surum, deger = kayit.deger) => {
  const iv = createHash("sha256").update(`${kayit.id}#${ad}#${surum}#${deger}`).digest().subarray(0, 12);
  const c = createCipheriv("aes-256-gcm", anahtar(ad, surum), iv);
  const g = Buffer.concat([c.update(deger, "utf8"), c.final()]);
  sayac.sifreleme++;
  return { iv, govde: g, etiket: c.getAuthTag(), ad, surum };
};

export const coz = (k) => {
  const d = createDecipheriv("aes-256-gcm", anahtar(k.ad, k.surum), k.iv);
  d.setAuthTag(k.etiket); sayac.cozme++;
  return Buffer.concat([d.update(k.govde), d.final()]).toString("utf8");
};

// ozneler: her biri belirli (ortam, servis) ciftlerine ihtiyac duyar. depo okuru anahtarsizdir.
export const OZNE = [
  { ad: "ajan/gelistirme", n: 1, ihtiyac: [["gelistirme", "*"]] },
  { ad: "ajan/hazirlik", n: 1, ihtiyac: [["hazirlik", "*"]] },
  { ad: "ajan/uretim", n: 1, ihtiyac: [["uretim", "*"]] },
  { ad: "olcum ekibi", n: 6, ihtiyac: [["gelistirme", "toplayici"], ["gelistirme", "dogrulayici"],
    ["hazirlik", "toplayici"]] },
  { ad: "fatura ekibi", n: 4, ihtiyac: [["gelistirme", "faturalama"], ["hazirlik", "faturalama"]] },
  { ad: "uretim nobeti", n: 2, ihtiyac: [["uretim", "faturalama"], ["uretim", "toplayici"]] },
  { ad: "depo okuru", n: 14, ihtiyac: [] },
];
const uyar = (k, [o, s]) => k.ortam === o && (s === "*" || k.servis === s);
export const gerekli = (z) => KAYIT.filter((k) => z.ihtiyac.some((c) => uyar(k, c)));

export function yetki(yer) {                              // yerlesime gore cozulebilen kayitlar
  const f = YERLESIM[yer];
  return OZNE.map((z) => {
    const tasidigi = new Set(gerekli(z).map(f));          // ozneye verilen anahtar adlari
    return { z, cozer: KAYIT.filter((k) => tasidigi.has(f(k))), gerek: gerekli(z).length };
  });
}

if (import.meta.url === `file://${process.argv[1]}`) {
  const ozneSayisi = OZNE.reduce((a, z) => a + z.n, 0);
  console.log(`envanter: ${KAYIT.length} sir kaydi (kurgu deger), ${ozneSayisi} depo okuru ozne`);
  const ornek = sifrele(KAYIT[0], "gelistirme", 1);
  console.log(`ornek kayit ${KAYIT[0].id} -> ${ornek.govde.toString("base64")} ` +
    `| cozulen: ${coz(ornek)}`);
  const B = [14, 9, 13, 15, 12, 12, 15];
  const yz = (h) => h.map((v, j) => j ? String(v).padStart(B[j]) : String(v).padEnd(B[0])).join("");
  console.log("\n" + yz(["yerlesim", "anahtar", "cozme cifti", "ihtiyac cifti", "fazlalik",
    "engellenen", "en genis ozne"]));
  for (const yer of Object.keys(YERLESIM)) {
    const y = yetki(yer);
    const anahtarlar = new Set(KAYIT.map(YERLESIM[yer])).size;
    const cift = y.reduce((a, x) => a + x.z.n * x.cozer.length, 0);
    const gerek = y.reduce((a, x) => a + x.z.n * x.gerek, 0);
    for (const x of y) if (x.cozer.length) coz(sifrele(x.cozer[0], YERLESIM[yer](x.cozer[0]), 1));
    console.log(yz([yer, anahtarlar, cift, gerek, cift - gerek,
      ozneSayisi * KAYIT.length - cift, Math.max(...y.map((x) => x.cozer.length))]));
  }
  console.log(`\nislem sayaci: ${sayac.turetme} anahtar turetme, ` +
    `${sayac.sifreleme} sifreleme, ${sayac.cozme} cozme`);
}
```

```
envanter: 33 sir kaydi (kurgu deger), 29 depo okuru ozne
ornek kayit gelistirme/toplayici/veritabani -> qvyyakRtf8xnLCnzTl5E7FgDkdh/5gAmv4t9dwQTT9kMOGXo4eVrlZUW | cozulen: kurgu-veritabani-toplayici-gelistirme-0001

yerlesim        anahtar  cozme cifti  ihtiyac cifti    fazlalik  engellenen  en genis ozne
tek                   1          495            117         378         462             33
ortam                 3          275            117         158         682             22
ortam-servis         12          117            117           0         840             11

islem sayaci: 8 anahtar turetme, 19 sifreleme, 19 cozme
```

Şifreleme gerçekten koştu: örnek kayıt şifrelendi, geri çözüldü ve kurgu değer aynen döndü. Asıl
tablo aşağıdadır ve tek bir soruyu üç kez yanıtlar — **kaç özne kaç kaydı çözebiliyor.** Ölçü
birimi (özne, kayıt) çiftidir; 29 özne ve 33 kayıtla olası en büyük değer 957'dir.

Anahtar deponun içinde durmaz — durursa şifrelemenin anlamı kalmaz. Anahtar öznelerin elindedir ve
tablo o dağıtımın sonucunu sayar. Tek anahtarlı yerleşimde anahtarı taşıyan 15 özne 33 kaydın
hepsini çözebilir: 495 çift. Bu öznelerin gerçekten ihtiyaç duyduğu çift sayısı **117**'dir ve bu
sayı üç yerleşimde de aynıdır, çünkü ihtiyaç anahtar yerleşiminden bağımsızdır. Aradaki 378 çift
saf fazlalıktır — kimsenin istemediği, yalnız anahtar kaba olduğu için verilmiş yetki.

Ortam başına anahtara geçildiğinde fazlalık 378'den 158'e iner; ortam-servis başına anahtarda
**sıfırlanır** ve çözebilen özne tam olarak ihtiyacı kadarını çözer. En geniş öznenin yetkisi de
33'ten 11'e düşer: bir kişinin elindeki anahtarın açtığı kayıt sayısı üç kat azalır. Bunun bedeli
anahtar sayısıdır — 1'den 12'ye — ve bu sayı bir sonraki bölümün konusudur.

#### Anahtar Döndürmenin Maliyeti ve Geçmişte Kalan Kayıt

```js
// dondur.mjs — anahtar dondurmenin maliyeti ve sifreli kaydin gecmiste kalmasi.
// Gecmisteki sifreli degerler gercekten cozulur; degerler kurgudur.
import { createHash } from "node:crypto";
import { KAYIT, YERLESIM, OZNE, yetki, sifrele, coz, sayac } from "./sir.mjs";

const DOSYA = (k) => `${k.ortam}/${k.servis}/sirlar`;      // kayitlar dosyalara boyle dagilir
const kez = (k) => 1 + (createHash("sha256").update(k.id).digest()[0] % 4);
const deger = (k, i) => `kurgu-${k.id.split("/")[2]}-${k.servis}-${k.ortam}-` +
  String(i).padStart(4, "0");

// 180 gunluk gecmis: her kayit kez(k) kez degisti; her degisiklik ayri bir islemede duruyor.
const gecmis = KAYIT.flatMap((k) => Array.from({ length: kez(k) }, (_, i) =>
  ({ kayit: k, ac: deger(k, i + 1) })));
const guncel = new Map(KAYIT.map((k) => [k.id, deger(k, kez(k))]));

const Y = [16, 10, 14, 10, 16];
const yz = (h) => h.map((v, j) => j ? String(v).padStart(Y[j]) : String(v).padEnd(Y[0])).join("");
console.log(`gecmis: ${gecmis.length} sifreli kayit surumu / ${KAYIT.length} kayit, ` +
  `${new Set(KAYIT.map(DOSYA)).size} dosya`);
console.log("\n" + yz(["yerlesim", "anahtar", "tek dondurme", "dosya", "hepsi dondurme"]));
for (const [yer, f] of Object.entries(YERLESIM)) {
  const hedef = f(KAYIT.find((k) => k.ortam === "uretim" && k.servis === "faturalama"));
  const etkilenen = KAYIT.filter((k) => f(k) === hedef);
  console.log(yz([yer, new Set(KAYIT.map(f)).size, etkilenen.length,
    new Set(etkilenen.map(DOSYA)).size, `${KAYIT.length} / ${new Set(KAYIT.map(DOSYA)).size} dosya`]));
}

// ortam yerlesiminde uretim anahtari 1 -> 2 dondurulur. Guncel kayitlar yeniden sifrelenir.
const f = YERLESIM.ortam;
const uretim = KAYIT.filter((k) => k.ortam === "uretim");
const gecmisSifreli = gecmis.map((g) => ({ ...g, kutu: sifrele(g.kayit, f(g.kayit), 1, g.ac) }));
const yeni = uretim.map((k) => sifrele(k, f(k), 2, guncel.get(k.id)));
console.log(`\nuretim anahtari 1 -> 2: ${yeni.length} kayit yeniden sifrelendi, ` +
  `${new Set(uretim.map(DOSYA)).size} dosyaya dokunuldu, ${new Set(uretim.map(DOSYA)).size} yeni isleme`);

// eski anahtar sizarsa gecmis ne veriyor
const eski = gecmisSifreli.filter((g) => g.kutu.ad === "uretim" && g.kutu.surum === 1);
const acilan = eski.map((g) => coz(g.kutu));
const dogru = acilan.every((a, i) => a === eski[i].ac);
const hala = eski.filter((g, i) => acilan[i] === guncel.get(g.kayit.id)).length;
const idx = eski.findIndex((g, i) => acilan[i] === guncel.get(g.kayit.id));
console.log(`eski anahtar (surum 1) sizarsa: gecmisteki ${eski.length} surum cozuluyor ` +
  `(hepsi dogru: ${dogru}); bunlarin ${hala} tanesi ${uretim.length} kaydin BUGUNKU degeri`);
console.log(`ornek: ${eski[idx].kayit.id} eski islemeden -> ${acilan[idx]}`);

// cozme yetkisi kapisinin yanlis tuttugu: gece nobetinde anahtarsiz kalinan olaylar
const uretec = (t) => { let s = t >>> 0;
  return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; };
const r = uretec(90210), SRV = ["toplayici", "dogrulayici", "faturalama", "isemri"];
const olay = Array.from({ length: 24 }, () => SRV[Math.floor(r() * 4)]);
const NOBET = ["faturalama", "toplayici"];                 // uretim nobetinin ihtiyac duydugu
console.log("\n" + yz(["yerlesim", "gece olayi", "anahtarsiz", "ek dakika", "engellenen cift"]));
for (const yer of Object.keys(YERLESIM)) {
  const tasir = (s) => yer === "ortam-servis" ? NOBET.includes(s) : true;
  const yok = olay.filter((s) => !tasir(s)).length;
  const cift = yetki(yer).reduce((a, x) => a + x.z.n * x.cozer.length, 0);
  console.log(yz([yer, olay.length, yok, yok * 35,
    OZNE.reduce((a, z) => a + z.n, 0) * KAYIT.length - cift]));
}
console.log(`\nislem sayaci: ${sayac.turetme} anahtar turetme, ${sayac.sifreleme} sifreleme, ` +
  `${sayac.cozme} cozme`);
```

```
gecmis: 93 sifreli kayit surumu / 33 kayit, 12 dosya

yerlesim           anahtar  tek dondurme     dosya  hepsi dondurme
tek                      1            33        12   33 / 12 dosya
ortam                    3            11         4   33 / 12 dosya
ortam-servis            12             3         1   33 / 12 dosya

uretim anahtari 1 -> 2: 11 kayit yeniden sifrelendi, 4 dosyaya dokunuldu, 4 yeni isleme
eski anahtar (surum 1) sizarsa: gecmisteki 35 surum cozuluyor (hepsi dogru: true); bunlarin 11 tanesi 11 kaydin BUGUNKU degeri
ornek: uretim/toplayici/veritabani eski islemeden -> kurgu-veritabani-toplayici-uretim-0004

yerlesim        gece olayi    anahtarsiz ek dakika engellenen cift
tek                     24             0         0             462
ortam                   24             0         0             682
ortam-servis            24            11       385             840

islem sayaci: 4 anahtar turetme, 104 sifreleme, 35 cozme
```

Döndürme tablosu anahtar sayısının ikinci yüzünü verir. Tek anahtarlı yerleşimde bir anahtarı
döndürmek 33 kaydın hepsini yeniden şifreler ve 12 dosyaya dokunur; ortam-servis yerleşiminde aynı
işlem 3 kayıt ve 1 dosyadır. Ama son sütun takası dürüst gösteriyor: **bütün** anahtarları döndürmek
üç yerleşimde de 33 kayıt ve 12 dosyadır. İnce anahtar bölmesi toplam işi azaltmaz, tek bir
sızıntının yankısını daraltır.

Üretim anahtarı 1'den 2'ye döndürüldüğünde 11 kayıt yeniden şifrelendi, 4 dosyaya dokunuldu ve 4
yeni işleme üretildi. Buraya kadar her şey beklenendir. Dersin çekirdeği bir sonraki satırdadır ve
tahmin değil, **çalıştırılmış bir çözmedir**: eski anahtar sızarsa geçmişteki 35 şifreli sürümün
hepsi çözülüyor — `hepsi dogru: true` satırı bunun sınandığını gösterir — ve bu 35 sürümün **11
tanesi 11 üretim kaydının bugünkü değeridir.**

Sonuç şudur: anahtar döndürmek geçmişi kapatmaz. Yeni işlemede yeni şifreli değer durur, ama eski
işlemede eski şifreli değer olduğu gibi kalır ve o değer eski anahtarla açıldığında **bugün de
geçerli olan sırrı** verir. Örnek satır bunu somutlaştırıyor: toplayıcının üretim veritabanı
kaydının eski işlemeden çözülen değeri, bugün çalışan servisin kullandığı değerin aynısıdır.
Geçmişi gerçekten kapatan tek işlem, sırrın **değerini** kaynağında değiştirmektir — parolayı
veritabanında döndürmek, jetonu ölçüm ağı tarafında iptal etmek. Anahtar döndürme 4 dosyaya
dokunur; değer döndürme her tüketiciye dokunur ve depo tarafındaki 4 dosya bu işin en ucuz
parçasıdır.

#### Çözme Yetkisi Kapısının Defteri

Bu konudaki kapı bir gözden geçirme ya da uzlaştırma turu değil, bir **çözme yetkisidir**. Defteri
üç kalemde okunur.

**Tuttuğu ve yalnız kendisinin tuttuğu.** Ortam başına anahtar yerleşiminde engellenen (özne,
kayıt) çifti 682'dir. Bu sayının tamamı marjinaldir, çünkü 29 öznenin hepsi depoyu zaten
okuyabiliyor — depo erişim kapısı bu çiftlerin hiçbirini durdurmuyor. Şifreleme kalksa 682 okuma
serbest kalırdı; kaba bir yerleşimde bile kapı tek başına iş görüyor. Ortam-servis yerleşiminde
aynı sayı 840'a çıkar.

**Kaçırdığı.** İki sınıf var. Birincisi geçmiştir: 35 şifreli sürüm kapının hiç devreye girmediği
bir yoldan okunabilir, çünkü eski anahtar sızdığında ortada bir yetki denetimi yoktur. İkincisi
fazlalıktır: ortam yerleşiminde 158 çift, anahtarı meşru taşıyan öznelerin ihtiyacından fazlasını
açar. Kapı bunları tutmaz, açıkça geçirir — ölçüm ekibinin 6 üyesi geliştirme ortamının faturalama
sırlarını da çözebilir.

**Yanlış tuttuğu ve eklediği süre.** Son tablo takası kapatıyor. Ortam-servis yerleşimi fazlalığı
sıfırlar, ama 24 gece olayının 11'inde üretim nöbetçisi ihtiyaç duyduğu anahtarı taşımıyordu ve her
olay 35 dakikalık eskalasyon ekledi: 385 dakika. Ortam yerleşiminde bu sayı sıfırdır, karşılığında
158 fazla çift açıktır. Karar bu iki sayı arasında verilir ve "daha ince anahtar daha güvenlidir"
cümlesi tek başına karar taşımaz. Kapının işletim yükü de sayılıdır: 33 kaydın 180 günlük geçmişi
104 şifreleme işlemi, doğrulaması 35 çözme işlemi eder; her sır değişikliği bir şifreleme, her
uzlaştırma turu ajan tarafında bir çözme ister.

#### Özet

- 29 özne ve 33 kayıtla olası 957 (özne, kayıt) çiftinden tek anahtarlı yerleşim 495'ini açıyor,
  ortam başına anahtar 275'ini, ortam-servis başına anahtar 117'sini; ihtiyaç üç yerleşimde de 117.
- Fazlalık yetki 378'den 158'e, oradan 0'a iniyor; en geniş öznenin çözebildiği kayıt 33'ten 11'e.
- Bir anahtarı döndürmek tek anahtarlı yerleşimde 33 kayıt ve 12 dosya, ortam-servis yerleşiminde
  3 kayıt ve 1 dosyadır; ama bütün anahtarları döndürmek üç yerleşimde de 33 kayıt ve 12 dosyadır.
- Üretim anahtarı döndürüldükten sonra eski anahtar sızarsa geçmişteki 35 şifreli sürüm çözülüyor
  ve 11'i 11 üretim kaydının bugünkü değeri — anahtar döndürme geçmişi kapatmaz, değer döndürme
  kapatır.
- Çözme yetkisi kapısının marjinal değeri ortam yerleşiminde 682 engellenen çifttir ve tamamı
  marjinaldir, çünkü 29 öznenin hepsi depoyu okuyabiliyor.
- Kapının bedeli ölçüldü: ortam-servis yerleşiminde 24 gece olayının 11'i anahtarsız kaldı ve 385
  dakika eskalasyon ekledi; ortam yerleşiminde gecikme 0, açık fazlalık 158 çifttir.

#### Sonraki Adım

Sırlar depoda şifreli durabiliyor ve kimin neyi çözebildiği sayılabiliyor. Bu, depodaki istenen
durumun **içeriğine** dair bir soruydu. Bir sonraki ders içeriğin başka bir yüzüne döner: bir yayının
gerçekte hangi bileşenlerden oluştuğu. Uygulama kendi kodundan ibaret değildir — doğrudan ve dolaylı
bağımlılıklar, çalışma zamanı kitaplıkları, taban katman içeriği. Ölçülecek şey bu envanterin
kapsamıdır: kaç bağımlılık görünüyor, kaçı dolaylı, envanteri üretmek ne kadar sürüyor ve envanteri
üretmeyen bir hatta kaç bileşen hiç sayılmamış oluyor.

### Yazılım Malzeme Listesi

Source: https://academia.sh/tr/kurslar/ci-cd/yazilim-malzeme-listesi

Yapı çıktısının içindeki bileşenler bir envanter olarak üretilir ve üç sayıyla ölçülür: envanterin gördüğü doğrudan, geçişli ve derleme zamanı bileşenleri, hiç göremediği üç sınıf ve bir kusur duyurusu geldiğinde yanıtın kaç sorguya indiği.

Önceki ders sırrın depoda şifreli durabildiğini ölçtü: anahtar bir yerde, çözebilen özne sayılı,
döndürme belli sayıda kaydı yeniden yazıyor. Depoya giren yapılandırma artık bildirilmiştir — ama
bildirilen yalnız yapılandırmadır. Hattın ürettiği çıktının **içinde ne olduğu** hâlâ hiçbir yerde
yazmaz.

Bu ders o beyanı üretir. Ölçü üç sayıdır: envanter kaç bileşeni görüyor, kaçını hiç göremiyor,
bir kusur duyurusu geldiğinde yanıt kaç sorguya iniyor. M22/K02 taramanın yakaladığı ve kaçırdığı
kusurları saymıştı; o sayım tekrarlanmaz. Buradaki nesne envanterin kendisidir — **hattın ürettiği
bir çıktı** olarak.

**DD19.** Kurgu bölgesel ölçüm ağı dört servistir ve 64 bileşenlik bir havuzu paylaşır; ağaç `node`
ile diske yazılır ve bir modeldir. **DD20.** Envanter üreteci yalnız beyan dosyalarını okur:
bildirim, çözüm ve derleme. **DD21.** Beyan edilmeyen üç yol vardır — kaynağa kopyalanmış, ikiliye
gömülü, koşum anında inen; üretecin tohumu `20260214` görünürdür.

#### Envanteri Üretmek

```js
// olcum-agi/envanter.mjs — kurgu bagimlilik agaci diske yazilir, envanter uretilir
import { mkdirSync, rmSync, writeFileSync, readFileSync, statSync } from "node:fs";
import { join, dirname } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-envanter");
rmSync(KOK, { recursive: true, force: true });
const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(20260214);                                   // tohum gorunur
const sec = (d) => d[Math.floor(r() * d.length)];

// Kurgu bolgesel olcum agi: dort servis. Havuz paylasilir, ayni bilesen birden cok serviste gecer.
const HAVUZ = Array.from({ length: 64 }, (_, i) => `bilesen-${String(i + 1).padStart(2, "0")}`);
const SURUM = ["1.4", "2.0", "2.3", "2.6", "3.1", "3.4"];
const SERVIS = [                        // kopya/gomulu/kosum: beyan edilmeyen bilesenler
  { ad: "toplayici", dogrudan: 9, arac: 5, kopya: 2, gomulu: 1, kosum: 1, ickopya: 1 },
  { ad: "dogrulayici", dogrudan: 7, arac: 4, kopya: 1, gomulu: 1, kosum: 0, ickopya: 1 },
  { ad: "faturalama", dogrudan: 12, arac: 6, kopya: 2, gomulu: 2, kosum: 2, ickopya: 1 },
  { ad: "is-emri", dogrudan: 6, arac: 4, kopya: 1, gomulu: 0, kosum: 1, ickopya: 0 },
];
const yaz = (yol, icerik) => {
  mkdirSync(dirname(yol), { recursive: true });
  writeFileSync(yol, icerik);
};

for (const s of SERVIS) {
  const alinan = new Set();
  const yeni = () => { let a; do { a = sec(HAVUZ); } while (alinan.has(a)); alinan.add(a); return a; };
  const dogrudan = {}, cozum = {}, araclar = {};
  for (let i = 0; i < s.dogrudan; i++) {
    const ad = yeni();
    cozum[ad] = { surum: (dogrudan[ad] = sec(SURUM)), gerektiren: "" };
    for (let k = 0, n = Math.floor(r() * 4); k < n; k++)       // gecisli cocuklar
      cozum[yeni()] = { surum: sec(SURUM), gerektiren: ad };
  }
  for (let i = 0; i < s.arac; i++) araclar[yeni()] = sec(SURUM);
  yaz(join(KOK, s.ad, "bildirim.json"), JSON.stringify({ servis: s.ad, dogrudan }, null, 1));
  yaz(join(KOK, s.ad, "cozum.json"), JSON.stringify(cozum, null, 1));
  yaz(join(KOK, s.ad, "derleme.json"), JSON.stringify({ araclar }, null, 1));

  for (let i = 0; i < s.kopya; i++)                            // kaynaga kopyalanmis ucuncu taraf
    yaz(join(KOK, s.ad, "kaynak", `k${i}.js`), `// kopyalanmis: ${yeni()}@${sec(SURUM)}\n`);
  for (let i = 0; i < s.ickopya; i++)                          // kendi kodu, ucuncu taraf degil
    yaz(join(KOK, s.ad, "kaynak", `i${i}.js`), `// kopyalanmis: ic-kitaplik-olcum@1.0\n`);
  for (let i = 0; i < s.gomulu; i++)                           // ikilinin icine gomulu
    yaz(join(KOK, s.ad, "cikti", "gomulu", `g${i}.bin`), Buffer.concat(
      [Buffer.from(`GOMULU:${yeni()}@${sec(SURUM)}\n`), Buffer.alloc(2048, "x")]));
  const inen = Array.from({ length: s.kosum }, () => `${yeni()}@${sec(SURUM)}`);
  yaz(join(KOK, s.ad, "cikti", "indirilenler.json"), JSON.stringify({ inen }, null, 1));
  yaz(join(KOK, s.ad, "cikti", "govde.js"), `// ${s.ad} yapi ciktisi\n`);
}

// Envanter ureteci YALNIZ beyan dosyalarini okur; okunan dosya ve bayt sayilir.
let dosya = 0, bayt = 0;
const oku = (y) => { dosya++; bayt += statSync(y).size; return JSON.parse(readFileSync(y)); };
const envanter = [];
for (const s of SERVIS) {
  const bildirim = oku(join(KOK, s.ad, "bildirim.json")).dogrudan;
  const cozum = oku(join(KOK, s.ad, "cozum.json"));
  for (const [ad, k] of Object.entries(cozum))
    envanter.push({ servis: s.ad, ad, surum: k.surum,
      tur: ad in bildirim ? "dogrudan" : "gecisli", gerektiren: k.gerektiren });
  for (const [ad, sur] of Object.entries(oku(join(KOK, s.ad, "derleme.json")).araclar))
    envanter.push({ servis: s.ad, ad, surum: sur, tur: "derleme", gerektiren: "(derleme)" });
}
const yolEnv = join(KOK, "envanter.json");
yaz(yolEnv, JSON.stringify(envanter, null, 1));
yaz(join(KOK, "maliyet.json"), JSON.stringify({ dosya, bayt }));

const say = (t) => envanter.filter((k) => k.tur === t).length;
console.log(`tohum 20260214; servis ${SERVIS.length}, havuz ${HAVUZ.length} bilesen`);
console.log(`envanteri uretmek icin okunan beyan dosyasi ${dosya}, ${bayt} bayt`);
console.log(`envanter kaydi ${envanter.length} -> dogrudan ${say("dogrudan")}, ` +
  `gecisli ${say("gecisli")}, derleme zamani ${say("derleme")}`);
console.log(`envanter dosyasi ${statSync(yolEnv).size} bayt, ` +
  `benzersiz bilesen ${new Set(envanter.map((k) => k.ad)).size}`);
```

```
tohum 20260214; servis 4, havuz 64 bilesen
envanteri uretmek icin okunan beyan dosyasi 12, 7124 bayt
envanter kaydi 107 -> dogrudan 34, gecisli 54, derleme zamani 19
envanter dosyasi 12659 bayt, benzersiz bilesen 56
```

Dört servis toplam **34 doğrudan bağımlılık** bildirir. Envanter 107 kayıt üretir: o 34'ün yanına
**54 geçişli** bileşen ve **19 derleme zamanı aracı** gelir. Beyan edilenle taşınan arasındaki
oran buradadır — ekip 34 ad yazar, çıktıya 88 bileşen girer. Yalnız doğrudan bağımlılıklara bakan
bir kapı, beyan edilen 88 koşum bileşeninin **34'ünü** (%39) görür.

Üretimin maliyeti ortamdan bağımsız niceliklerle yazılır: **12 beyan dosyası, 7.124 bayt okuma,
107 kayıt, 12.659 baytlık envanter.** Süre basılmaz: süre makineye bağlıdır, okunan dosya sayısı
değildir.

107 kaydın arkasında **56 benzersiz bileşen** vardır: aynı bileşen birden çok serviste geçer.
"Hangi servisler etkileniyor" sorusunun tek bir dosyadan yanıtlanabilmesi buna dayanır.

#### Envanterin Görmediği

Üreteç beyan dosyalarını okur. Beyan dosyası **niyeti** anlatır: neyin çekilmesi istendiğini. Çıktı
ağacı ise **sonucu** taşır. İkisi aynı olmak zorunda değildir.

**DD22.** Derleme zamanı araçları üretimde koşmaz; koşan bileşen kümesine girmez. **DD23.** Denetçi
ağacı gezer ve `kopyalanmis:` ya da `GOMULU:` başlığı taşıyan her dosyayı beyan dışı sayar; kendi
kodunun kopyası da aynı başlığı taşır.

```js
// olcum-agi/kapsam.mjs — envanterin gormedigi: cikti agaci gezilir, beyanla karsilastirilir
import { readdirSync, readFileSync, writeFileSync, statSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-envanter");
const envanter = JSON.parse(readFileSync(join(KOK, "envanter.json")));
const maliyet = JSON.parse(readFileSync(join(KOK, "maliyet.json")));   // ureticin okudugu
const SERVIS = ["toplayici", "dogrulayici", "faturalama", "is-emri"];

const dolas = (kok, alt = "") => readdirSync(join(kok, alt), { withFileTypes: true })
  .flatMap((g) => g.isDirectory() ? dolas(kok, join(alt, g.name)) : [join(alt, g.name)]);

// Denetci beyan dosyalarini degil, agacin kendisini okur.
let gezilenDosya = 0, gezilenBayt = 0;
const bulgu = [];
for (const s of SERVIS) {
  for (const yol of dolas(join(KOK, s))) {
    if (yol.endsWith(".json")) continue;                       // beyan dosyalari denetcinin girdisi degil
    const tam = join(KOK, s, yol);
    gezilenDosya++; gezilenBayt += statSync(tam).size;
    const bas = readFileSync(tam).subarray(0, 120).toString("latin1");
    const k = bas.match(/kopyalanmis: (\S+)@(\S+)/);
    if (k) bulgu.push({ servis: s, ad: k[1], surum: k[2], nasil: "kaynaga kopyalanmis" });
    const g = bas.match(/GOMULU:(\S+)@(\S+)/);
    if (g) bulgu.push({ servis: s, ad: g[1], surum: g[2], nasil: "ikiliye gomulu" });
  }
  const { inen } = JSON.parse(readFileSync(join(KOK, s, "cikti", "indirilenler.json")));
  for (const kayit of inen) {
    const [ad, surum] = kayit.split("@");
    bulgu.push({ servis: s, ad, surum, nasil: "kosum aninda inen" });
  }
}

// Beyan edilmis kosum bileseni: derleme araclari uretimde kosmaz, kosum kumesine girmez.
const beyanli = new Set(envanter.filter((k) => k.tur !== "derleme").map((k) => `${k.servis}|${k.ad}`));
const ucuncuTaraf = (ad) => ad.startsWith("bilesen-");
const beyansiz = bulgu.filter((b) => ucuncuTaraf(b.ad) && !beyanli.has(`${b.servis}|${b.ad}`));
const yanlisTutulan = bulgu.filter((b) => !ucuncuTaraf(b.ad));

const say = (nasil) => beyansiz.filter((b) => b.nasil === nasil).length;
const kosanBeyanli = envanter.filter((k) => k.tur !== "derleme").length;
const kosanToplam = kosanBeyanli + beyansiz.length;

console.log(`envanterin gordugu kosum bileseni ${kosanBeyanli}, ` +
  `derleme zamani ${envanter.length - kosanBeyanli}`);
console.log(`envanterin gormedigi ${beyansiz.length} -> ` +
  `kaynaga kopyalanmis ${say("kaynaga kopyalanmis")}, ` +
  `ikiliye gomulu ${say("ikiliye gomulu")}, kosum aninda inen ${say("kosum aninda inen")}`);
console.log(`kapsam %${(100 * kosanBeyanli / kosanToplam).toFixed(0)} ` +
  `(${kosanBeyanli}/${kosanToplam} kosan bilesen)`);

const SUTUN = [28, 22, 10, 16, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(SUTUN[i])).join(""));
console.log("");
satir(["kapi", "okudugu", "tuttugu", "yalniz kendisi", "yanlis tuttugu"]);
satir(["beyan dosyalarini okur", `${maliyet.dosya} dosya / ${maliyet.bayt} bayt`, 0, 0, 0]);
satir(["cikti agacini gezer", `${gezilenDosya} dosya / ${gezilenBayt} bayt`,
  beyansiz.length, beyansiz.length, yanlisTutulan.length]);

console.log(`yanlis tutulan ${yanlisTutulan.length} kayit: ` +
  `${yanlisTutulan.map((b) => b.servis).join(", ")} -> ${yanlisTutulan[0].ad}`);

// Gercek kosan kume sonraki bloga birakilir: beyanli + beyansiz.
writeFileSync(join(KOK, "gercek.json"), JSON.stringify([
  ...envanter.filter((k) => k.tur !== "derleme")
    .map(({ servis, ad, surum }) => ({ servis, ad, surum, gorunur: true })),
  ...beyansiz.map(({ servis, ad, surum }) => ({ servis, ad, surum, gorunur: false })),
]));
```

```
envanterin gordugu kosum bileseni 88, derleme zamani 19
envanterin gormedigi 14 -> kaynaga kopyalanmis 6, ikiliye gomulu 4, kosum aninda inen 4
kapsam %86 (88/102 kosan bilesen)

kapi                        okudugu               tuttugu   yalniz kendisi  yanlis tuttugu
beyan dosyalarini okur      12 dosya / 7124 bayt  0         0               0
cikti agacini gezer         17 dosya / 8685 bayt  14        14              3
yanlis tutulan 3 kayit: toplayici, dogrulayici, faturalama -> ic-kitaplik-olcum
```

Koşan 102 bileşenin 88'i envanterde görünür; **14'ü görünmez** — 6'sı kaynağa kopyalanmış, 4'ü
ikiliye gömülü, 4'ü koşum anında inen. **Kapsam %86'dır.** Eksik %14 ölçünün en pahalı parçasıdır,
çünkü envanter bu bileşenleri "yok" diye değil, hiç göstermeyerek raporlar.

Tablo iki kapıyı yan yana koyar. Beyan dosyalarını okuyan kapı beyan dışı bileşenlerin
**sıfırını** tutar; tutamaz, çünkü baktığı yer beyanın kendisidir. **Kapı olarak marjinal değeri
sıfırdır** ve kapı olarak durması gerekmez: envanteri üretmesi yeterlidir, geçişe izin verme
yetkisi ona hiçbir şey kazandırmaz. **Kaldırılması daha iyi olan kapı budur.**

Çıktı ağacını gezen kapı 14'ün 14'ünü tutar ve **hepsini yalnız kendisi tutar** — marjinal değeri
tuttuğunun tamamıdır. Okuma bedeli küçüktür: 12 dosya ve 7.124 bayta karşı 17 dosya ve 8.685
bayt, farklı bir okuma, daha büyük bir okuma değil. **Asıl bedel yanlış tutmadır:** 17 bulgunun
3'ü kendi kodunun kopyasıdır. Her altı bulgudan biri boşuna bir incelemedir ve bu oranın ürettiği
davranış bilinir — liste bir kez toptan "bilinen" diye işaretlenir, sonraki gerçek bulgu da aynı
işaretin altında kalır.

#### Duyuru Geldiğinde

Envanterin değeri üretildiği anda değil, sorulduğu anda ölçülür. Soru hep aynı biçimdedir: şu
bileşenin şu sürümünün altındakiler açık — bizde var mı, nerede?

**DD24.** Kusur duyurusu bir bileşen adı ve bir eşik sürümüdür; eşiğin altındaki sürümler açık
sayılır. Duyuru çekilişinin tohumu `884301` görünürdür.

```js
// olcum-agi/duyuru.mjs — kusur duyurusu geldiginde yanit: envanterli ve envantersiz yol
import { readFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-envanter");
const SERVIS = ["toplayici", "dogrulayici", "faturalama", "is-emri"];
const SURUM = ["1.4", "2.0", "2.3", "2.6", "3.1", "3.4"];
const oku = (ad) => JSON.parse(readFileSync(join(KOK, ad)));
const envanter = oku("envanter.json"), maliyet = oku("maliyet.json");
const gercek = oku("gercek.json");                 // onceki blogun denetimi: beyanli + beyansiz

const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(884301);                          // duyuru cekilisinin tohumu gorunur
const ADLAR = [...new Set(gercek.map((k) => k.ad))].sort();
const DUYURU = Array.from({ length: 9 }, () => ({
  ad: ADLAR[Math.floor(r() * ADLAR.length)],
  esik: SURUM[1 + Math.floor(r() * (SURUM.length - 1))],       // bu surumun altindakiler acik
}));
const etkilenen = (kume, d) => new Set(kume
  .filter((k) => k.ad === d.ad && SURUM.indexOf(k.surum) < SURUM.indexOf(d.esik))
  .map((k) => k.servis));

console.log(`gercek kosan kayit ${gercek.length} ` +
  `(gorunmeyen ${gercek.filter((k) => !k.gorunur).length}), duyuru ${DUYURU.length}; tohum 884301`);
console.log("");
console.log("duyuru".padEnd(24) + "envanter yaniti".padEnd(17) + "gercek".padEnd(9) + "durum");

let dogru = 0, eksik = 0, etkiliDuyuru = 0;
for (const d of DUYURU) {
  const yanit = etkilenen(gercek.filter((k) => k.gorunur), d), ger = etkilenen(gercek, d);
  if (ger.size) etkiliDuyuru++;
  yanit.size === ger.size ? dogru++ : eksik++;
  console.log(`${d.ad} < ${d.esik}`.padEnd(24) + `${yanit.size} servis`.padEnd(17) +
    `${ger.size} servis`.padEnd(9) + (yanit.size === ger.size ? "tam" : "eksik"));
}

const BEYAN = SERVIS.length * 3;                   // depo basina bildirim, cozum, derleme
console.log("");
console.log("yol".padEnd(30) + "duyuru basina".padEnd(26) + `${DUYURU.length} duyuru icin`);
console.log("envanter dosyasina sorgu".padEnd(30) + `1 sorgu / ${envanter.length} kayit`.padEnd(26) +
  `1 uretim (${maliyet.dosya} dosya) + ${DUYURU.length} sorgu`);
console.log("depolari tek tek acmak".padEnd(30) + `${SERVIS.length} depo / ${BEYAN} dosya`.padEnd(26) +
  `${DUYURU.length * SERVIS.length} depo acilisi, ${DUYURU.length * BEYAN} dosya`);
console.log("");
console.log(`etkilenen servisi olan duyuru ${etkiliDuyuru}/${DUYURU.length}; ` +
  `envanterin tam yanitladigi ${dogru}, eksik yanitladigi ${eksik}`);
```

```
gercek kosan kayit 102 (gorunmeyen 14), duyuru 9; tohum 884301

duyuru                  envanter yaniti  gercek   durum
bilesen-47 < 2.6        0 servis         1 servis eksik
bilesen-49 < 3.4        0 servis         1 servis eksik
bilesen-59 < 2.3        0 servis         0 servis tam
bilesen-41 < 2.0        0 servis         0 servis tam
bilesen-12 < 2.0        0 servis         0 servis tam
bilesen-12 < 2.3        0 servis         0 servis tam
bilesen-05 < 3.1        1 servis         1 servis tam
bilesen-58 < 2.3        0 servis         0 servis tam
bilesen-15 < 2.6        1 servis         2 servis eksik

yol                           duyuru basina             9 duyuru icin
envanter dosyasina sorgu      1 sorgu / 107 kayit       1 uretim (12 dosya) + 9 sorgu
depolari tek tek acmak        4 depo / 12 dosya         36 depo acilisi, 108 dosya

etkilenen servisi olan duyuru 4/9; envanterin tam yanitladigi 6, eksik yanitladigi 3
```

Dokuz duyurunun **beşi hiçbir servisi etkilemez.** En sık verilen yanıt "bizde yok"tur ve
envanterin değeri öncelikle buradadır: o yanıt tek sorguda gelir. Envantersiz yol aynı yanıta
ulaşmak için her duyuruda **dört depoyu açar, on iki beyan dosyası okur** — dokuz duyuru için 36
depo açılışı ve 108 dosya.

Bu, kapının eklediği sürenin yönünü belirler. Envanter üretimi hatta bir aşama ekler ve o aşama
**her değişiklikte** 12 dosya okur; buna karşılık yanıt **duyuru başına** dört depo açılışından bir
sorguya düşer. **Kapının bedeli değişiklik başına, getirisi duyuru başınadır**; oranı duyuru
sıklığı belirler, kapının kendisi değil.

Son satır kapsamın bedelidir. Envanter 9 duyurunun **6'sını tam, 3'ünü eksik** yanıtlar. İkisinde
yanıt "0 servis", gerçek "1 servis"tir; duyuru ilgisiz diye kapatılır. Üçüncüsü daha kötüdür:
`bilesen-15` için envanter "1 servis" der, gerçek "2 servis"tir. Sıfır bir yanıt bazen sorgulanır,
**bir sayı sorgulanmaz** — iş yapılmış görünür. Görünmeyen 14 bileşenin bedeli budur ve çıktı
ağacını gezen kapı kaldırıldığında geri gelir.

#### Özet

- Dört servis 34 doğrudan bağımlılık bildirir; envanter 107 kayıt üretir — 34 doğrudan, 54
  geçişli, 19 derleme zamanı aracı. Yalnız doğrudan bağımlılığa bakan bir kapı beyan edilen 88
  koşum bileşeninin 34'ünü görür.
- Üretim maliyeti ortamdan bağımsız yazılır: 12 beyan dosyası, 7.124 bayt okuma, 107 kayıt,
  12.659 baytlık envanter, 56 benzersiz bileşen.
- Koşan 102 bileşenin 88'i envanterde görünür, 14'ü görünmez: 6 kaynağa kopyalanmış, 4 ikiliye
  gömülü, 4 koşum anında inen. Kapsam %86'dır.
- Beyan dosyalarını okuyan kapı beyan dışı bileşenin sıfırını tutar ve kapı olarak kaldırılabilir;
  çıktı ağacını gezen kapı 14'ünü tutar, hepsini yalnız kendisi tutar, bedeli 17 bulgunun 3'ünü
  yanlış tutmasıdır.
- Yanıt duyuru başına 4 depo açılışından 1 sorguya iner: 9 duyuru için 36 depo açılışına karşı
  bir üretim ve 9 sorgu. Envanter 6 duyuruyu tam, 3'ünü eksik yanıtlar; eksiklerden biri "0"
  değil "1" der.

#### Sonraki Adım

Envanter artık var, kapsamı sayılı ve sorgulanabilir. Ama 12.659 baytlık bu dosya ile tarif ettiği
çıktı arasında **hiçbir bağ yoktur.** Envanter ayrı bir dosyadır: yanına başka bir envanter
konabilir, içindeki bir kayıt değiştirilebilir, bambaşka bir yapı çıktısı aynı envanterle
eşleştirilebilir. 107 kaydın hiçbiri, tarif ettiği baytların gerçekten o baytlar olduğunu söylemez.

Aynı boşluk zincirin öteki halkalarında da vardır: kaynak işlemesi ile yapı çıktısı arasında, yapı
çıktısı ile dağıtılan şey arasında. Bir sonraki ders bu halkaları gerçek bir anahtar çiftiyle
bağlar ve iki soruyu sayıya çevirir: doğrulama zincirin hangi noktasında yapılırsa kaç sahte çıktı
geçer ve o anahtarı elinde tutan kaç özne vardır?

### Yapı Çıktısı İmzalama ve Doğrulama

Source: https://academia.sh/tr/kurslar/ci-cd/yapi-ciktisi-imzalama-ve-dogrulama

Kaynak işlemesinden dağıtılan çıktıya uzanan zincir gerçek bir anahtar çiftiyle kurulur ve iki soru sayıya çevrilir: doğrulama zincirin hangi noktasında yapılırsa kaç sahte çıktı geçer ve o anahtara erişen kaç özne imzalayabilir.

Önceki ders envanteri üretti ve bir boşlukla bitti: 107 kayıtlık dosya ile tarif ettiği çıktı
arasında hiçbir bağ yoktu. Aynı boşluk zincirin bütün halkalarında vardır. Hattın okuduğu kaynak
işlemesi ile ürettiği yapı çıktısı arasında, o çıktı ile depoya yazılan bayt arasında, depodaki
bayt ile dağıtılan şey arasında — her geçişte nesne el değiştirir ve **el değiştirdiğinin
kanıtı yoktur.**

Bu ders o bağı kurar ve iki soruyu sayıya çevirir: doğrulama zincirin hangi noktasında yapılırsa
kaç sahte çıktı geçer ve imzayı üretebilen kaç özne vardır. M22/K02 imzalama zincirini deponun
içinde kurmuş, doğrulanmayan halkanın açtığı yolu saymıştı; o kurulum tekrarlanmaz. Buradaki eksen
**doğrulamanın yeri** ve **anahtarın kimde durduğu**dur.

**DD25.** İmzalama gerçek bir anahtar çiftiyle yapılır, ama anahtar her koşumda yeniden üretilir;
basılan şey doğrulama sonucudur, imzanın kendisi değil. Gerçek anahtar ya da sır yazılmaz.
**DD26.** Zincir dört halkadır: kaynak işlemesi, yapı çıktısı, envanter ve beyan; beyan üç özeti
bağlar, imza beyanı bağlar.

#### Zincirin Dört Halkası

```js
// olcum-agi/zincir.mjs — kaynak islemesi, yapi ciktisi, envanter, beyan: zincir kurulur ve sinanir
import { generateKeyPairSync, sign, verify, createHash } from "node:crypto";

const ozet = (b) => createHash("sha256").update(b).digest();
const kisa = (b) => ozet(b).toString("hex").slice(0, 12);
// Kurgu anahtar cifti her kosumda yeniden uretilir; gercek anahtar ya da sir yazilmaz.
const { publicKey: acik, privateKey: gizli } = generateKeyPairSync("ed25519");
const { privateKey: baskaGizli } = generateKeyPairSync("ed25519");

// Halka 1: kaynak islemesi — agac icerigi ozetlenir. Halka 2: yapi ciktisi. Halka 3: envanter.
const kaynak = Buffer.from(["app.js", "ayar.json", "olcum.js"]
  .map((y) => `${y}\n// kurgu bolgesel olcum agi\n`).join(""));
const cikti = Buffer.concat([Buffer.from("govde\n"), Buffer.alloc(24576, "u")]);
const envanter = Buffer.from(                     // onceki dersin envanteri, 107 kayit
  Array.from({ length: 107 }, (_, i) => `bilesen-${i}@2.3`).join("\n"));

// Halka 4: beyan uc ozeti baglar; imza beyani baglar.
const beyanYaz = (surum, k, c, e) => Buffer.from([`servis=toplayici`, `surum=${surum}`,
  `kaynak=${kisa(k)}`, `cikti=${kisa(c)}`, `envanter=${kisa(e)}`].join("\n"));
const BEYAN = beyanYaz(41, kaynak, cikti, envanter);
const IMZA = sign(null, ozet(BEYAN), gizli);
console.log(`beyan ${BEYAN.length} bayt, imza ${IMZA.length} bayt; ` +
  `cikti ${cikti.length} bayt, envanter ${envanter.length} bayt`);

// Dogrulayici uc ayri sinama yapar: imza, cikti ozeti, envanter ozeti.
const dogrula = (b, i, ac, c, e) => ({
  imza: verify(null, ozet(b), ac, i),
  cikti: b.includes(`cikti=${kisa(c)}`),
  envanter: b.includes(`envanter=${kisa(e)}`),
});

const bozukCikti = Buffer.from(cikti); bozukCikti[9000] = 0x41;   // tek bayt
const bozukEnvanter = Buffer.from(envanter.toString().replace("bilesen-3@2.3", "bilesen-3@1.4"));
const SENARYO = [
  ["bozulmamis", BEYAN, IMZA, acik, cikti, envanter],
  ["beyanda surum degistirilmis", beyanYaz(39, kaynak, cikti, envanter), IMZA, acik, cikti, envanter],
  ["ciktinin tek bayti degismis", BEYAN, IMZA, acik, bozukCikti, envanter],
  ["envanter degistirilmis", BEYAN, IMZA, acik, cikti, bozukEnvanter],
  ["baska anahtarla imzalanmis", BEYAN, sign(null, ozet(BEYAN), baskaGizli), acik, cikti, envanter],
];

const SUTUN = [32, 8, 13, 17, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(SUTUN[i])).join(""));
console.log("");
satir(["senaryo", "imza", "cikti ozeti", "envanter ozeti", "kabul"]);
for (const [ad, b, i, ac, c, e] of SENARYO) {
  const s = dogrula(b, i, ac, c, e);
  satir([ad, s.imza, s.cikti, s.envanter, s.imza && s.cikti && s.envanter]);
}
```

```
beyan 86 bayt, imza 64 bayt; cikti 24582 bayt, envanter 1601 bayt

senaryo                         imza    cikti ozeti  envanter ozeti   kabul
bozulmamis                      true    true         true             true
beyanda surum degistirilmis     false   true         true             false
ciktinin tek bayti degismis     true    false        true             false
envanter degistirilmis          true    true         false            false
baska anahtarla imzalanmis      false   true         true             false
```

Bağın bedeli iki kalemdir: **86 baytlık beyan ve 64 baytlık imza.** Bağladıkları 24.582 baytlık
çıktı ile 1.601 baytlık envanterdir; 150 bayt, 26 kilobaytı yerine çiviler.

Tablo zincirin ayrıştığı yeri gösterir. **Üç sınamanın her biri tam bir senaryoyu yakalar ve
ötekiler o senaryoyu kaçırır.** İmza sınaması beyandaki sürüm değiştirildiğinde ve imza başka bir
anahtardan geldiğinde düşer — dört sahtelikten ikisini yakalar. Ama çıktının tek baytı
değiştiğinde imza hâlâ geçerlidir: beyan bozulmamıştır, bozulan beyanın **tarif ettiği** şeydir.
Aynı biçimde envanterdeki tek bir sürüm numarası değiştirildiğinde de imza doğrulanır.

Bu, kapının marjinal değerinin zincirin içine uygulanmış hâlidir. İmza sınaması tek başına
kullanıldığında dört sahtelikten ikisi geçer. Çıktı özeti sınaması yalnız kendi senaryosunu
yakalar, envanter özeti sınaması yalnız kendininkini. Üçü birlikte dördünü de tutar ve bunun
bedeli üç özet hesabıdır.

Bir ayrıntı üç sınamayı da belirler: **ikisi nesnenin kendisini ister.** İmza yalnız beyanla
sınanabilir, ama çıktı özeti çıktının baytlarını, envanter özeti envanterin baytlarını yeniden
okumayı gerektirir. Nesnesi elinde olmayan bir doğrulayıcı o iki sınamayı yapamaz — ve doğrulamanın
nerede yapıldığı sorusu tam olarak buradan çıkar.

#### Doğrulama Nerede Yapılıyor

**DD27.** Dağıtım denemeleri kurgudur; her deneme gerçekten imzalanır, sahtelik **imzadan sonra**
uygulanır ve tohum `31071` görünürdür. **DD28.** Otuzuncu denemeden sonra anahtar döndürülür ve
servisin yetkili öznesi değişir.

```js
// olcum-agi/dagitim.mjs — dogrulama nerede yapilirsa kac sahte cikti geciyor
import { generateKeyPairSync, sign, verify, createHash } from "node:crypto";

const ozet = (b) => createHash("sha256").update(b).digest();
const kisa = (b) => ozet(b).toString("hex").slice(0, 12);
const A = generateKeyPairSync("ed25519");        // servisin ilk yetkili oznesi
const B = generateKeyPairSync("ed25519");        // guvenilen ama bu servis icin yetkisiz ozne
const C = generateKeyPairSync("ed25519");        // donduruldukten sonraki yetkili ozne
const der = (k) => k.export({ type: "spki", format: "der" }).toString("base64");
const GUVENILEN = [A, B, C].map((k) => k.publicKey);   // dogrulayicinin tanidigi anahtarlar

const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(31071);                         // tohum gorunur
const SAHTE = ["depoda cikti degistirilmis", "envanter degistirilmis",
  "baska oznenin imzasi", "eski surumun yeniden dagitimi"];

// Kurgu dagitim denemeleri: her biri gercekten imzalanir, sahtelik imzadan SONRA uygulanir.
// 30. denemeden sonra anahtar donduruldu: yetkili ozne A degil C.
const DENEME = [];
for (let i = 0, surum = 40; i < 60; i++) {
  const tur = r() < 0.6 ? "gecerli" : SAHTE[Math.floor(r() * SAHTE.length)];
  const yeni = tur === "eski surumun yeniden dagitimi" ? surum - 3 : ++surum;
  let cikti = Buffer.alloc(4096, String.fromCharCode(97 + (i % 26)));
  let env = Buffer.from(`envanter-${i}\n` + "kayit\n".repeat(107));
  const imzalayan = tur === "baska oznenin imzasi" ? B : (i < 30 ? A : C);
  const beyan = Buffer.from(`servis=toplayici\nsurum=${yeni}\n` +
    `cikti=${kisa(cikti)}\nenvanter=${kisa(env)}`);
  const imza = sign(null, ozet(beyan), imzalayan.privateKey);
  if (tur === "depoda cikti degistirilmis") cikti = Buffer.alloc(4096, "z");
  if (tur === "envanter degistirilmis") env = Buffer.concat([env, Buffer.from("ek\n")]);
  DENEME.push({ tur, surum: yeni, cikti, env, beyan, imza, acik: imzalayan.publicKey });
}

// Bes disiplin: zincir ayni, degisen sey dogrulamanin YERI ve ozne listesi.
const ESKI = [A].map((k) => der(k.publicKey)), GUNCEL = [A, C].map((k) => der(k.publicKey));
const DISIPLIN = [
  ["dogrulama yok", {}],
  ["yalniz hat sonunda", { hat: 1 }],
  ["dagitim aninda: imza ve ozetler", { imza: 1, ozet: 1 }],
  ["+ ozne listesi (guncellenmemis)", { imza: 1, ozet: 1, ozne: ESKI, taze: 1 }],
  ["+ ozne listesi (guncel)", { imza: 1, ozet: 1, ozne: GUNCEL, taze: 1 }],
];

const SAHTE_SAY = DENEME.filter((d) => d.tur !== "gecerli").length;
const GECERLI_SAY = DENEME.length - SAHTE_SAY;
const SUTUN = [35, 20, 20, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(SUTUN[i])).join(""));
satir(["disiplin", "kabul edilen sahte", "durdurulan gecerli", "gecen sahte turu"]);
for (const [ad, k] of DISIPLIN) {
  let sonSurum = 40, gecerliGecen = 0;
  const gecen = [];
  for (const d of DENEME) {
    // Hat sonu dogrulamasi: cikti uretildigi anda dogruydu; sahtelik sonra uygulandi.
    if (k.imza && !GUVENILEN.some((a) => verify(null, ozet(d.beyan), a, d.imza))) continue;
    if (k.ozet && !(d.beyan.includes(`cikti=${kisa(d.cikti)}`) &&
      d.beyan.includes(`envanter=${kisa(d.env)}`))) continue;
    if (k.ozne && !k.ozne.includes(der(d.acik))) continue;
    if (k.taze && d.surum <= sonSurum) continue;
    sonSurum = Math.max(sonSurum, d.surum);
    d.tur === "gecerli" ? gecerliGecen++ : gecen.push(d.tur);
  }
  satir([ad, `${gecen.length}/${SAHTE_SAY}`, `${GECERLI_SAY - gecerliGecen}/${GECERLI_SAY}`,
    [...new Set(gecen)].map((t) => t.split(" ")[0]).join(", ") || "-"]);
}

console.log("");
console.log(`gecerli ${GECERLI_SAY}; ` +
  SAHTE.map((t) => `${t.split(" ")[0]} ${DENEME.filter((d) => d.tur === t).length}`).join(", "));
```

```
disiplin                           kabul edilen sahte  durdurulan gecerli  gecen sahte turu
dogrulama yok                      18/18               0/42                envanter, eski, baska, depoda
yalniz hat sonunda                 18/18               0/42                envanter, eski, baska, depoda
dagitim aninda: imza ve ozetler    9/18                0/42                eski, baska
+ ozne listesi (guncellenmemis)    0/18                18/42               -
+ ozne listesi (guncel)            0/18                0/42                -

gecerli 42; depoda 4, envanter 5, baska 6, eski 3
```

İlk iki satır bu dersin en sert sayısıdır: **doğrulama yokken de, yalnız hat sonunda yapılırken de
on sekiz sahtenin on sekizi geçer.** Hat kendi ürettiği çıktıyı doğrular ve doğru bulur — çünkü o
anda çıktı gerçekten doğrudur. Sahtelik imzadan sonra uygulanır. **Yakalamak istediği olaydan önce
duran bir kapının marjinal değeri sıfırdır** ve bu, kapının zayıf olmasından değil, yanlış yerde
olmasından gelir.

Üçüncü satır doğrulamayı dağıtım anına taşır ve sayı yarıya iner: 18 sahteden 9'u geçer. Tutulan
dokuz tanesi depoda değiştirilen çıktı (4) ile değiştirilen envanterdir (5) — her ikisi de nesnenin
baytları yeniden okunduğu için yakalanır. Geçen dokuz tanesi ise **imzası geçerli olanlardır.**
Altısında imza başka bir öznenin geçerli anahtarındandır; doğrulayıcı "tanıdığım bir anahtar
imzalamış" der ve geçirir. Üçünde her şey gerçektir, yalnız eskidir: geçmiş bir sürümün beyanı ve
imzası bugün de doğrulanır.

Dördüncü satır iki kuralı ekler — imzalayan servisin yetkili öznesi olmalı, sürüm son dağıtılandan
büyük olmalı — ve sahte sayısı **sıfıra** iner. Ama aynı satırda ikinci sayı belirir: **42 geçerli
dağıtımın 18'i durdurulur.** Anahtar otuzuncu denemeden sonra döndürülmüştür ve doğrulayıcının
özne listesi eski anahtarda kalmıştır. Kapı sahtenin tamamını tutar ve geçerli dağıtımların
%43'ünü de tutar.

Bu, kapının üçüncü sayısını yazar. Doğrulamanın kendisi bir özet hesabı ve bir liste aramasıdır;
eklediği süre ölçüsüzdür. Bekleten şey doğrulama değil, **listenin tazeliğidir**: anahtar
döndükten sonra liste güncellenene kadar her geçerli dağıtım durur. Ardışık on sekiz geçerli
dağıtımı durduran bir kapının ekipte ürettiği davranış bilinir — kapı bir kez atlanır ve atlama
kalıcılaşır. Son satır aynı zinciri güncel listeyle koşar: 0 sahte, 0 yanlış durdurma.

#### Anahtar Kaç Öznede

Dördüncü satır bir soruyu açıkta bırakır: "yetkili özne" tekil bir şey midir? İmzayı üretebilen,
gizli anahtara erişebilen herkestir.

**DD29.** Saklama modelleri kurgudur; anahtara erişen özne sayısı modelin girdisidir.

```js
// olcum-agi/anahtar.mjs — anahtarin kendisi bir kapidir: kac ozne imzalayabiliyor
import { generateKeyPairSync, sign, verify, createHash } from "node:crypto";

const ozet = (b) => createHash("sha256").update(b).digest();
const der = (k) => k.export({ type: "spki", format: "der" }).toString("base64");
const beyan = (i) => Buffer.from(`servis=toplayici\nsurum=${100 + i}`);

// Kurgu saklama modelleri: anahtar nerede duruyorsa, oraya erisen her ozne imzalayabilir.
const SAKLAMA = [
  ["gizli anahtar depoda", 23],
  ["hat calistiricisinda ortam degiskeni", 11],
  ["imzalama hizmeti, cagri yetkisi uc ozne", 3],
  ["donanimda, tek otomatik ozne", 1],
];

const SUTUN = [42, 8, 14, 15, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(SUTUN[i])).join(""));
satir(["saklama", "ozne", "paylasilan", "ozne basina", "ayirt edilen"]);

for (const [ad, n] of SAKLAMA) {
  // (a) Paylasilan tek anahtar: n ozne ayni gizli anahtarla imzalar.
  const tek = generateKeyPairSync("ed25519");
  const paylasilan = Array.from({ length: n }, (_, i) => sign(null, ozet(beyan(i)), tek.privateKey))
    .filter((im, i) => verify(null, ozet(beyan(i)), tek.publicKey, im)).length;

  // (b) Ozne basina anahtar: yetkili liste tek oznedir, otekiler kendi anahtarlariyla imzalar.
  const anahtarlar = Array.from({ length: n }, () => generateKeyPairSync("ed25519"));
  const LISTE = [der(anahtarlar[0].publicKey)];
  const oznece = anahtarlar
    .map((a, i) => [a, sign(null, ozet(beyan(i)), a.privateKey), i])
    .filter(([a, im, i]) => LISTE.includes(der(a.publicKey)) &&
      verify(null, ozet(beyan(i)), a.publicKey, im)).length;

  // Ayirt edilen imzalayan: dogrulayicinin n imzada gordugu farkli acik anahtar sayisi.
  const gorulen = (d) => new Set(d).size;
  satir([ad, n, `${paylasilan}/${n}`, `${oznece}/${n}`,
    `${gorulen(Array(n).fill(der(tek.publicKey)))} -> ${gorulen(anahtarlar.map((a) => der(a.publicKey)))}`]);
}
```

```
saklama                                   ozne    paylasilan    ozne basina    ayirt edilen
gizli anahtar depoda                      23      23/23         1/23           1 -> 23
hat calistiricisinda ortam degiskeni      11      11/11         1/11           1 -> 11
imzalama hizmeti, cagri yetkisi uc ozne   3       3/3           1/3            1 -> 3
donanimda, tek otomatik ozne              1       1/1           1/1            1 -> 1
```

Paylaşılan tek anahtarla, anahtara erişen **her öznenin** imzası doğrulanır: 23 özneden 23'ü,
11'den 11'i. Doğrulayıcı hiçbirini reddedemez, çünkü hepsi aynı anahtardır. Son sütun bunu
niceliğe çevirir — doğrulayıcı 23 imzada **tek bir açık anahtar** görür. İmzadan imzalayana giden
yol yoktur; "yetkili özne imzaladı" cümlesi ölçülemez bir cümledir.

Özne başına anahtar ve tek adlık bir yetkili liste, kabul sayısını **özne sayısından bağımsız
kılar**: 23'te de 11'de de 3'te de kabul 1'dir. Ayırt edilen imzalayan sayısı ise özne sayısına
eşitlenir. Aradaki fark aynı algoritmayla, aynı imza uzunluğuyla elde edilir; değişen tek şey
anahtarın kaç öznede durduğudur.

M22/K02 imzalamanın güveni ortadan kaldırmadığını, anahtara taşıdığını göstermişti. Buradaki tablo
o taşınmanın **büyüklüğünü** verir. Anahtar depoda dururken imza "depoyu okuyabilen 23 özneden biri
bunu imzaladı" der; donanımda tek bir otomatik öznede dururken "o özne imzaladı" der. İki cümle
arasındaki fark 23 katıdır ve önceki dersteki envanter kapsamı gibi, kapının gücünün üst sınırını
kapının kendisi değil, dışındaki bir sayı belirler.

#### Özet

- Zincir dört halkadır ve bağın bedeli 86 baytlık beyan ile 64 baytlık imzadır; bağladıkları
  24.582 baytlık çıktı ve 1.601 baytlık envanterdir.
- Üç sınamanın her biri tam bir senaryoyu yakalar: imza sınaması dört sahtelikten ikisini tutar,
  çıktı özeti ile envanter özeti kalan ikisini ayrı ayrı tutar. İkisi nesnenin baytlarını yeniden
  okumayı gerektirir.
- Doğrulama yokken 18 sahtenin 18'i geçer; yalnız hat sonunda doğrulanırken de 18'i geçer, çünkü
  sahtelik imzadan sonra uygulanır. Yanlış yerde duran kapının marjinal değeri sıfırdır.
- Dağıtım anında imza ve özetler sınandığında 9 sahte geçer: 6'sı başka öznenin geçerli imzası,
  3'ü geçmiş sürümün yeniden dağıtımı. Özne listesi ve tazelik kuralıyla sahte sayısı 0'a iner.
- Güncellenmemiş özne listesi sahtenin tamamını tutar ama 42 geçerli dağıtımın 18'ini de durdurur;
  bekleten şey doğrulama değil, listenin tazeliğidir.
- Paylaşılan anahtarla 23 öznenin 23'ünün imzası kabul edilir ve doğrulayıcı tek bir açık anahtar
  görür; özne başına anahtarla kabul 1'e, ayırt edilen imzalayan 23'e çıkar.

#### Sonraki Adım

Zincir artık kurulu, dağıtım anında sınanıyor ve anahtar tek bir öznede duruyor. Ölçülen sayılar
bir noktaya kadar sağlam: 0 sahte, 0 yanlış durdurma, 150 baytlık bağ. Ama o tek özne bir insan
değil, **hattın kendisidir** — ve bu bölümdeki bütün sayılar hattın dürüst çalıştığı varsayımı
üstünde durur.

O varsayım şimdiye kadar hiç sayılmadı. Hattın koştuğu iş akışı tanımı depodan gelir ve depoyu
değiştirebilen kaç özne vardır? Hat çalıştırıcısı imzalama anahtarına, yapı çıktısı deposuna ve
dağıtım hedefine aynı anda erişir; bir aşamanın çektiği bir bağımlılık o erişimlerin hepsini
kullanabilir mi? Sonraki ders bu kursun kapılarını **kapıları koyan makinenin üstüne** çevirir ve
aynı üç sayıyı hattın kendisi için sorar: ne tutuyor, ne kaçırıyor, ne kadar bekletiyor?

### Hat Güvenliği

Source: https://academia.sh/tr/kurslar/ci-cd/hat-guvenligi

Hattın kendisi ölçülür: dokuz iş adımının kaç sırra ve hedefe eriştiği, en az yetkiye inince düşen erişim ve bedeli, aynı çalıştırıcıda art arda koşan işler arasında taşınan durum ve bilinen kötüye kullanım girişimlerinde her kısıtlamanın kapattığı, kaçırdığı ve durdurduğu meşru iş.

Önceki ders çıktıyı imzaladı ve dağıtım anında doğruladı: on sekiz sahtenin on sekizi durdu,
anahtar tek bir öznede kaldı. O özne bir insan değildi, **hattın kendisiydi.** Bu kursta ölçülen
her kapı, kapıyı koşturan makinenin dürüst çalıştığı varsayımına dayandı ve o varsayım hiç
sayılmadı.

Bu ders onu sayar: hattın kaç sırra eriştiğini, iki iş arasında ne taşındığını, kötüye kullanım
girişimlerinin kaçının kapandığını. Ölçü yine kapıdır — önünde duran artık bir değişiklik değil,
hattın kendi adımı.

**DD31.** Kurgu bölgesel ölçüm ağının hattı dokuz adımdır; 12 sır ve 6 hedef `node` ile
modellenir ve model olduğu yazılır — **sır değerleri yazılmaz, yalnız adları geçer.** **DD32.**
Bir adımın *ihtiyacı* gerçekten kullandığıdır, *bildirileni* hat tanımına yazılan; ikisi iki
adımda ayrışır.

#### Hattın Yetki Yüzeyi

```js
// olcum-agi/yetki.mjs — hattin yetki yuzeyi: hangi adim hangi sirra ve hedefe erisiyor (model)
const SIR = ["kod-deposu-okuma", "paket-deposu-okuma", "paket-deposu-yazma", "nesne-deposu-yazma",
  "imza-anahtari", "dagitim-jetonu-gelistirme", "dagitim-jetonu-hazirlik", "dagitim-jetonu-uretim",
  "veritabani-hazirlik", "veritabani-uretim", "bildirim-jetonu", "toplayici-anahtari"];
const HEDEF = ["gelistirme", "hazirlik", "uretim", "yapi-cikti-deposu", "nesne-deposu", "bildirim-kanali"];

// Kurgu bolgesel olcum agi hatti: dokuz adim. `ihtiyac` adimin gercekten kullandigi,
// `bildirilen` hat tanimina yazilan kume. Ikisi iki adimda ayrisir.
const ADIM = [
  ["bicim-cozumlemesi", [], []],
  ["derleme", ["kod-deposu-okuma", "paket-deposu-okuma"], []],
  ["birim-sinamasi", [], []],
  ["sozlesme-sinamasi", [], []],
  ["tumlesim-sinamasi", ["veritabani-hazirlik"], ["hazirlik"]],
  ["paketleme", ["paket-deposu-yazma"], ["yapi-cikti-deposu"]],
  ["envanter-uretimi", ["paket-deposu-okuma"], ["yapi-cikti-deposu"]],
  ["imzalama", ["imza-anahtari"], ["yapi-cikti-deposu"]],
  ["dagitim", ["dagitim-jetonu-uretim", "nesne-deposu-yazma", "bildirim-jetonu"],
    ["uretim", "nesne-deposu", "bildirim-kanali"]],
].map(([ad, sir, hedef]) => ({ ad, sir, hedef }));
const EKSIK = { "envanter-uretimi": ["paket-deposu-okuma"], dagitim: ["bildirim-jetonu"] };
const bildirilen = (a) => a.sir.filter((s) => !(EKSIK[a.ad] ?? []).includes(s));

const topla = (f) => ADIM.reduce((t, a) => t + f(a), 0);
const tekSir = ADIM.length * SIR.length, tekHedef = ADIM.length * HEDEF.length;
const azSir = topla((a) => bildirilen(a).length), azHedef = topla((a) => a.hedef.length);
const enGenis = Math.max(...ADIM.map((a) => bildirilen(a).length + a.hedef.length));

const SUTUN = [26, 12, 14, 22, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(SUTUN[i])).join(""));
satir(["yerlesim", "kimlik", "sir erisimi", "hedef erisimi", "ele gecen adimin ulastigi"]);
satir(["tek hat kimligi", 1, `${tekSir}/${tekSir}`, `${tekHedef}/${tekHedef}`,
  `${SIR.length} sir + ${HEDEF.length} hedef`]);
satir(["adim basina kimlik", ADIM.length, `${azSir}/${tekSir}`, `${azHedef}/${tekHedef}`,
  `en cok ${enGenis} kayit`]);

const sirsiz = topla((a) => (a.sir.length === 0 ? 1 : 0));
const duran = ADIM.filter((a) => a.sir.length > bildirilen(a).length).map((a) => a.ad);
console.log("");
console.log(`dusen erisim ${tekSir + tekHedef - azSir - azHedef}/${tekSir + tekHedef} ` +
  `(%${(100 * (1 - (azSir + azHedef) / (tekSir + tekHedef))).toFixed(0)}); bedeli ` +
  `${ADIM.length} kimlik tanimi ve ${azSir + azHedef} baglama kaydi`);
console.log(`hic sir kullanmayan adim ${sirsiz}/${ADIM.length} — ` +
  `tek kimlikte yine ${SIR.length} sirra erisiyor`);
console.log(`gercek ihtiyac ${topla((a) => a.sir.length)} sir, bildirilen ${azSir}; ` +
  `bildirimi eksik ${duran.length} adim (${duran.join(", ")}) -> ${duran.length} mesru is durur`);
```

```
yerlesim                  kimlik      sir erisimi   hedef erisimi         ele gecen adimin ulastigi
tek hat kimligi           1           108/108       54/54                 12 sir + 6 hedef
adim basina kimlik        9           7/108         7/54                  en cok 5 kayit

dusen erisim 148/162 (%91); bedeli 9 kimlik tanimi ve 14 baglama kaydi
hic sir kullanmayan adim 3/9 — tek kimlikte yine 12 sirra erisiyor
gercek ihtiyac 9 sir, bildirilen 7; bildirimi eksik 2 adim (envanter-uretimi, dagitim) -> 2 mesru is durur
```

Tek hat kimliğinde erişim sayısı **162'dir**: dokuz adımın her biri on iki sırrın ve altı hedefin
hepsine ulaşır. Adım başına kimlikte aynı hat **14 erişimle** koşar; düşen 148 erişim yüzeyin
**%91'idir**, bedeli dokuz kimlik tanımı ve on dört bağlama kaydıdır.

Dokuz adımın **üçü hiç sır kullanmıyor** — biçim çözümlemesi, birim sınaması, sözleşme sınaması —
ama tek kimlikte o üçü de on iki sırra erişiyordu. Bu bir kusur değil bir varsayılandır: kimlik
hatta verilir, adıma değil. Ele geçen adımın ulaştığı kayıt 18'den **en çok 5'e** iner.

Son satır kapının öteki yüzüdür: gerçek ihtiyaç dokuz sır, bildirilen yedi, iki iş durur.
**Yanlış tutmanın kaynağı kısıtlama değil, yine bir listedir** — `hat-tasarimi/08`'de tetikleyici
filtresi, `hat-tasarimi/05`'te önbellek anahtarı aynı yerden bozulmuştu.

#### İki İş Arasında Taşınan

**DD33.** Çalıştırıcıda dört durum türü taşınabilir: önbellek, ortam değişkeni, artık dosya,
kimlik belleği; bırakma ve okuma çekilişleri tohum `70413+i`'den gelir. **DD34.** Koşum tabanı
120 saniye, önbellek isabeti 45 saniye kazandırır.

```js
// olcum-agi/yalitim.mjs — ayni calistiricida art arda kosan isler arasinda tasinan durum (model)
const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const TUR = ["onbellek", "ortam degiskeni", "artik dosya", "kimlik bellegi"];

// Kurgu is dizisi: on iki is, dort servis, biri dis katkidan gelir. Her is bazi durumlari
// birakir, bazilarini okur; birakma ve okuma cekilisleri tohumdan gelir.
const IS = Array.from({ length: 12 }, (_, i) => {
  const r = uretec(70413 + i);
  return { no: i + 1, disKatki: i === 6,
    birakir: TUR.filter((_, k) => k === 0 || r() < 0.45),      // onbellek her zaman birakilir
    okur: TUR.filter(() => r() < 0.5) };
});

// Uc yalitim duzeyi: hangi tur is arasinda silinir.
const DUZEY = [
  ["paylasilan calistirici", []],
  ["is basina temiz calisma alani", ["artik dosya"]],
  ["is basina yeni calistirici", TUR],
];

// Bedel: isabet dusunce kosum uzar. Taban 120 s, isabet 45 s kazandirir.
const SUTUN = [31, 10, 9, 8, 8, 14, 10, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(SUTUN[i])).join(""));
satir(["yalitim", "onbellek", "ortam", "artik", "kimlik", "toplam / disa", "isabet", "on iki is"]);

for (const [ad, silinen] of DUZEY) {
  const sayim = Object.fromEntries(TUR.map((t) => [t, 0]));
  let disaTasan = 0, isabet = 0;
  const kalan = new Map();                        // tur -> birakan isin numarasi
  for (const is of IS) {
    for (const t of silinen) kalan.delete(t);     // is baslamadan once temizlenen turler
    for (const t of is.okur) {
      if (!kalan.has(t)) continue;
      sayim[t] += 1;                              // onceki isin durumu bu ise gecti
      if (t === "onbellek") isabet += 1;
      if (is.disKatki || kalan.get(t) === 7) disaTasan += 1;   // dis katkiya ya da ondan
    }
    for (const t of is.birakir) kalan.set(t, is.no);
  }
  const toplam = TUR.reduce((a, t) => a + sayim[t], 0);
  satir([ad, ...TUR.map((t) => sayim[t]), `${toplam} / ${disaTasan}`,
    `${isabet}/${IS.length}`, `${IS.length * 120 - isabet * 45} s`]);
}
```

```
yalitim                        onbellek  ortam    artik   kimlik  toplam / disa isabet    on iki is
paylasilan calistirici         6         9        8       5       28 / 5        6/12      1170 s
is basina temiz calisma alani  6         9        0       5       20 / 4        6/12      1170 s
is basina yeni calistirici     0         0        0       0       0 / 0         0/12      1440 s
```

Paylaşılan çalıştırıcıda on iki iş arasında **28 taşıma** olur; beşinin bir ucunda dış katkıdan
gelen iş vardır. Yalnız altısı önbellektir — dokuzu ortam değişkeni, sekizi artık dosya, **beşi
kimlik belleği**, yani bir öncekinin çözdüğü jetonun bellekte kalması. Yetki yüzeyindeki "adım
başına kimlik" kazancı burada delinir: yetkiyi adıma bağlamak, adımın bıraktığını sonrakinin
okumasını engellemiyor.

Orta satır en sık uygulanan yalıtımdır: çalışma alanını temizlemek artık dosyaların **sekizini
sıfıra indirir**, ama ortam değişkeni ve kimlik belleği kalır — toplam 20, dışa taşan dört,
**taşımanın %71'i hâlâ açık.**

İş başına yeni çalıştırıcı dördünü birden kapatır: **28 taşımanın 28'i sıfırlanır.** Bedeli tek
kalemdir — isabet altıdan sıfıra düşer, on iki iş 1.170 yerine **1.440 saniye** sürer: 270
saniye, **%23.** Çalıştırıcı türlerinin para hesabı `hat-tasarimi/06`'da ölçüldü ve tekrarlanmaz;
buradaki yenilik, oradaki "temiz başlama" üstünlüğünün bir de **taşıma sayısı** olarak
okunmasıdır.

#### Bilinen Kötüye Kullanım Kümesi

**DD35.** Kötüye kullanım kümesi kurgudur: üç sınıfta 40 girişim ve 60 meşru iş, tohum `56092`.
Bir girişim, yollarından en az biri açıkken başarılıdır. **DD36.** "Yetkili özne" yolu hiçbir
kısıtlamanın kapatmadığı yoldur.

```js
// olcum-agi/kotuye-kullanim.mjs — bilinen kotuye kullanim kumesi ve kisitlamalarin defteri (model)
const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(56092);                          // tohum gorunur
const SINIF = [
  ["dis katkidan sirra erisim", "dis-katki-sirri", "tasinan-durum", 0.40],
  ["bagimlilik uzerinden kod kosturma", "kurulum-betigi", "tasinan-durum", 0.30],
  ["hat tanimini degistirme", "tanim-degisikligi", "dis-katki-sirri", 0.25],
];

// Kurgu defterler. Bir girisim, yollarindan en az biri acikken basarili olur.
const GIRISIM = Array.from({ length: 40 }, () => {
  const [ad, yol, ikinci, p] = SINIF[Math.floor(r() * SINIF.length)];
  const yollar = [yol];
  if (r() < p) yollar.push(ikinci);
  if (r() < 0.15) yollar.push("yetkili-ozne");    // hicbir kisitlamanin kapatmadigi yol
  return { sinif: ad, yollar };
});
const MESRU = Array.from({ length: 60 }, () => ({
  disKatki: r() < 0.35, sirIster: r() < 0.30,
  kurulumBetigi: r() < 0.12, tanimDegisikligi: r() < 0.18,
}));

const KISIT = [
  ["dis katkida sir verilmez", "dis-katki-sirri", (m) => m.disKatki && m.sirIster],
  ["dis katki isleri elle onay bekler", "dis-katki-sirri", (m) => m.disKatki],
  ["bagimlilik kurulum betigi kosturulmaz", "kurulum-betigi", (m) => m.kurulumBetigi],
  ["hat tanimi degisikligi ayri onay ister", "tanim-degisikligi", (m) => m.tanimDegisikligi],
  ["is basina yeni calistirici", "tasinan-durum", () => false],
];
const gecen = (aktif) => GIRISIM.filter((g) =>
  g.yollar.some((y) => !aktif.some(([, kapali]) => kapali === y))).length;
const TAM = gecen(KISIT);

const SUTUN = [40, 12, 11, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(SUTUN[i])).join(""));
satir(["kisitlama", "kapattigi", "marjinal", "durdurdugu mesru is"]);
for (const k of KISIT)
  satir([k[0], GIRISIM.filter((g) => g.yollar.includes(k[1])).length,
    gecen(KISIT.filter((o) => o !== k)) - TAM, MESRU.filter(k[2]).length]);

const sifir = KISIT.filter((k) => gecen(KISIT.filter((o) => o !== k)) === TAM);
console.log("");
console.log(`girisim ${GIRISIM.length} (${SINIF.map(([a]) =>
  GIRISIM.filter((g) => g.sinif === a).length).join("/")}), mesru is ${MESRU.length}; tohum 56092`);
console.log(`kisitlama yokken gecen ${GIRISIM.length}, tam kumede ${TAM} ` +
  `— hepsi yetkili ozne yolundan`);
console.log(`marjinali sifir ${sifir.length}: ${sifir.map((k) => k[0]).join(" | ")}; ` +
  `ikisi birden kaldirilinca gecen ` +
  `${gecen(KISIT.filter((o) => o[1] !== "dis-katki-sirri"))}/${GIRISIM.length}`);
```

```
kisitlama                               kapattigi   marjinal   durdurdugu mesru is
dis katkida sir verilmez                16          0          7
dis katki isleri elle onay bekler       16          0          16
bagimlilik kurulum betigi kosturulmaz   14          12         8
hat tanimi degisikligi ayri onay ister  13          12         9
is basina yeni calistirici              5           3          0

girisim 40 (13/14/13), mesru is 60; tohum 56092
kisitlama yokken gecen 40, tam kumede 5 — hepsi yetkili ozne yolundan
marjinali sifir 2: dis katkida sir verilmez | dis katki isleri elle onay bekler; ikisi birden kaldirilinca gecen 19/40
```

Tam kısıtlama kümesi kırk girişimin **otuz beşini** kapatır. Geçen beşi aynı yoldan geçer:
**yetkili öznenin kendi yetkisini kullanması.** Hiçbir kısıtlama onu kapatmaz, çünkü kapatmak
yetkiyi kaldırmak demektir; `depo-tabanli-dagitim/05`'teki "anahtara erişen imzalayabilir"
sınırının karşılığı budur — %12,5.

Marjinal sütunu kısıtlamaları ayırır: kurulum betiği yasağı **12**, hat tanımı onayı **12**
girişimi tek başına kapatır. İş başına yeni çalıştırıcı 5 kapatır, 3'ü marjinaldir ve **hiçbir
meşru işi durdurmaz** — bedeli yalnız 270 saniyedir.

Son satır **kaldırılması daha iyi olan kısıtlamayı** verir. İki kısıtlamanın marjinali sıfırdır,
çünkü aynı yolu kapatırlar; ikisi birden kaldırılınca geçen girişim **19'a** çıkar, yani yol
kapalı kalmalıdır. Hangisiyle kapatılacağını meşru iş sütunu söyler: "sır verilmez" 7, "elle onay
bekler" **16** işi durdurur. Aynı korumayı iki kat pahalıya alan kaldırılır.

#### Özet

- Tek hat kimliğinde 162 erişimin 162'si açıktır, adım başına kimlikte 14'ü; düşen 148 erişim
  yüzeyin %91'idir, bedeli 9 kimlik tanımı ve 14 bağlama kaydıdır.
- Dokuz adımın üçü hiç sır kullanmaz ama tek kimlikte on iki sırra erişir; ele geçen adımın
  ulaştığı kayıt 18'den en çok 5'e iner. Bildirimi eksik 2 adım 2 meşru işi durdurur.
- Paylaşılan çalıştırıcıda 28 taşıma olur (6/9/8/5), 5'inin ucunda dış katkı vardır; çalışma
  alanı temizliği 20'ye, yeni çalıştırıcı 0'a indirir. Bedeli 1.170 yerine 1.440 saniyedir.
- Tam kısıtlama kümesi 40 girişimin 35'ini kapatır; geçen 5'in hepsi, hiçbir kısıtlamanın
  kapatmadığı yetkili özne yolundandır.
- Marjinali sıfır iki kısıtlama aynı yolu kapatır; ikisi birden kaldırılınca geçen girişim 19'a
  çıkar. Yol kapalı kalmalı, 16 meşru işi durduranla değil 7'sini durduranla.

#### Kurs Kapanışı

| Ders | Kapı | Tuttuğu / kaçırdığı / yanlış tuttuğu | Eklediği süre |
|---|---|---|---|
| `hat-tasarimi/01` | birleştirme | 90 çatışma / 10 uyuşmazlık / 2 | 23 dk/değişiklik |
| `hat-tasarimi/02` | elle onay | 5, marjinal 2 / 38 / 26 | 226,5 dk bekleme |
| `hat-tasarimi/03` | sözleşme sınaması | 18, marjinal 1 / 119 / 17 | 6.138 çalıştırıcı dk |
| `hat-tasarimi/04` | saklama kuralı | 38, marjinal 1 / 2 / 47 | 338 dk kurtarma |
| `hat-tasarimi/05` | tümleşim sınaması | 3, marjinal 2 / 2 önbellekten / — | 768 s, sekiz koşum |
| `hat-tasarimi/06` | temiz çalıştırıcı | 8, marjinal 2 / 0 / 0 | 40 s/koşum |
| `hat-tasarimi/07` | şema doğrulayıcısı | 4 hata / 2 hata / 0 | 80 s, yirmi değişiklik |
| `hat-tasarimi/08` | yol filtresi | 7 gereksiz koşum / 0 / 10 | 3,1 saat gecikme |
| `yayin-stratejileri/01` | tümleştirme | 34, marjinal 24 / 8 / 2–54 | 490–1.638 kapı dk |
| `yayin-stratejileri/02` | ortam terfisi | 7, marjinal 7 ve 1 / 7 / 5 | 3 sa 56 dk bekleme |
| `yayin-stratejileri/03` | yüzde artırma | 200/200 belirgin / %18–60 silik / %27–32 | 30–70 dk merdiven |
| `yayin-stratejileri/04` | geri alma | 18, tek turda 11 / 7 yayın, 491 satır / 178 | 40.320 kopya-dk |
| `yayin-stratejileri/05` | bayrak koşulu | 0, 76 fatura / 4 yol / 1 bekleyen | 18 okuma noktası |
| `yayin-stratejileri/06` | ön koşul denetimi | 5, marjinal 2 / 1 / 2 | 12 tur, 17 tur bedel |
| `yayin-stratejileri/07` | uyumluluk denetimi | 1, marjinal 1 / 1 / 1 | 3 tur, üç şema adımı |
| `depo-tabanli-dagitim/01` | gözden geçirme | 24, marjinal 13 / 11 / 7 | 796 saat, doksan gün |
| `depo-tabanli-dagitim/02` | uzlaştırma turu | 48, marjinal 17 / 6 / 9 | 8,5 dk yayılma |
| `depo-tabanli-dagitim/03` | çözme yetkisi | 682 çift, hepsi marjinal / 35 / 11 | 385 dk eskalasyon |
| `depo-tabanli-dagitim/04` | çıktı ağacı denetimi | 14, hepsi marjinal / 0 / 3 | 12 dosya/değişiklik |
| `depo-tabanli-dagitim/05` | dağıtımda doğrulama | 18 sahte, hepsi marjinal / 0 / 18 | özet ve liste araması |
| `depo-tabanli-dagitim/06` | en az yetki, yalıtım | 35, marjinal 12-12-3 / 5 özne / 16 | 270 s, on iki iş |

Kursun kuralı tablodan okunur: **bir kapı yalnız kendisinin yakaladığı kusur kadar değerlidir;
tuttuğu, kaçırdığı ve beklettiği birlikte yazılmadıkça kapı savunulamaz.** Tuttuğu sayısı tek
başına karar taşımaz — biçim kapısı 51 tutup 51'ini tek başına tutar, sözleşme kapısı 18 tutup
1'ini.

Marjinali sıfır çıkan kapılar kursta dört yerde ölçüldü: `hat-tasarimi/08`'in zaman tetikleyicisi
(84 boş koşum, gecikmeye katkı 0,0 saniye), `depo-tabanli-dagitim/04`'ün beyan dosyalarını okuyan
kapısı (beyan dışı bileşenin sıfırını tutar), `depo-tabanli-dagitim/05`'in hat sonu doğrulaması
(18 sahtenin 18'ini geçirir) ve buradaki "elle onay bekler" (16 meşru iş). Dördü de
kaldırılabilir; sebep zayıf olmaları değil, **başkasının zaten tuttuğunu tutmalarıdır.** Ölçü
ters yönde de çalışır: `hat-tasarimi/07`'nin gözden geçirmesi 714 dakikada tek bir hata tutar,
ama o hata bir kapıyı kaldıran değişikliktir.

Hat artık uygulamayı üretime taşıyor ve yolun her adımı sayılı. Altındaki makineler, ağ ve
depolar ise hâlâ başka bir yolla kuruluyor: hiçbir kapıdan geçmeyen, kaydı tutulmayan, geri alma
birimi tanımsız bir yolla. **M22/K05 Kod Olarak Altyapı** bunu sorar — onlar da aynı kapılardan
geçebilir mi ve geçtiklerinde bu tablonun yirmi bir satırı onlar için ne gösterir?

