---
title: 'Test Süreci ve Otomasyon Altyapısı'
source: 'https://academia.sh/tr/kurslar/test-sureci'
language: tr
updated: '2026-08-17T04:43:00+00:00'
license: 'CC BY-SA 4.0'
lessons: 11
outcomes:
    - 'Risk temelli test planı hazırlar'
    - 'Test otomasyonunu sürekli tümleştirme hattına yerleştirir'
    - 'Kalite göstergelerini yorumlayarak süreç iyileştirir'
---

# Test Süreci ve Otomasyon Altyapısı

Sınırlı zamanın testler arasında paylaştırılması ve kırmızı bir sonucun karara dönüşmesi: kapsamın, ölçütlerin ve sorumlulukların plana yazılması, riskin önceliklendirmeyi belirlemesi, yapılandırılmış serbest sınamanın betikli testin göremediğini bulması, tamamlanma tanımının yazılması, kullanıcı kabul testinin yönetilmesi, aşamalı test kapılarının sürekli tümleştirme hattına yerleştirilmesi, ortam çeşitliliğinin maliyeti, test ortamında gizli değer ile veri güvenliği, sonuçların ekip için okunur kılınması, kalite göstergelerinin yorumlanması ve yinelenen hataların kaynağına inilmesi.

## Planlama

Sınırlı zamanın nereye gideceğine karar verme: kapsamın, giriş ile çıkış ölçütlerinin ve sorumlulukların yazıya dökülmesi, riskin olasılık ile etki çarpımından çıkan sırayla önceliklendirilmesi, yapılandırılmış serbest sınamanın betikli testin göremediği kusurları bulması, tamamlanma tanımının kabul ölçütüne çevrilmesi ve son kullanıcı doğrulamasının kapsam ile geri bildirim yönetimi.

### Test Planı

Source: https://academia.sh/tr/kurslar/test-sureci/test-plani

Sınırlı bir sürüm bütçesinin dört kursta yazılmış test kümesi arasında paylaştırılması: envanterin bedel nicelikleriyle birlikte listelenmesi, giriş ile çıkış ölçütlerinin sayıya bağlanması, sorumluluğun koşan test sayısını belirlemesi ve dört planın aynı kusur kümesi üzerinde kaçan hata ile geri bildirim süresi bakımından karşılaştırılması.

İşlevsel Olmayan Test kursunun kapanışı dört kursluk birikimi tek bir soruyla kapattı: hangi
test ne zaman koşacak. Bu testlerin bazısı her değişiklikte koşacak kadar ucuz, bazısı sürüm
başına bir kez koşacak kadar pahalı; sınırlı zamanın aralarında nasıl paylaştırılacağı
hiçbir derste ele alınmadı.

Bu kursun her dersi aynı biçimi izler: sınırlı bir kaynak dağıtılır, dağıtımın ürettiği
**kaçan hata** (escaped defect) ile **geri bildirim süresi** ölçülür ve kırmızı bir sonucun
hangi karara dönüştüğü yazılır. Kaçan hata, Tümleşim, Sözleşme ve Uçtan Uca Test kursundaki
**kaçan gerçek hata** ile aynı kavramdır; bu kursta kısa biçim kullanılır.

#### Planın Üç Sütunu

Bir **test planı** (test plan) üç şeyi yazar: **kapsam** hangi kusur sınıflarının
sınanacağını, **ölçüt** planın ne zaman başlayıp ne zaman kapanacağını, **sorumluluk** her
kalemin sahibini söyler. Üçü de birer sayıya bağlanmadıkça plan bir niyet metnidir.

Ölçütün iki yüzü vardır. **Giriş ölçütü** (entry criteria) bir aşamanın koşmaya
başlayabilmesi için gereken koşuldur; sağlanmadan koşan test bilgi üretmez, zaman harcar.
**Çıkış ölçütü** (exit criteria) planın kapandığını söyler ve bu derste kapsanan kusur
sınıfı payına bağlanır.

#### Envanter: Dört Kursun Testleri Bir Liste

Kütüphane ödünç sisteminde dört kurs boyunca on iki test biçimi yazıldı. Her birinin bedeli
kaynak dersin kapanış tablosunda **koşumdan bağımsız** bir nicelikle kayıtlı: kaç süreç, kaç
istek ya da sorgu, kaç elle inceleme adımı. Bunlar iki varsayımla dakikaya çevrilir.

**TS1 (varsayım) — koşum dakikası: süreç başına 0,5 dk, çağrı başına 0,0002 dk.** Süreç
payı, Tümleşim, Sözleşme ve Uçtan Uca Test kursundaki hazır olma yoklamasının altı adımından
gelir; çağrı payı istek başına 12 milisaniyedir.
**TS2 (varsayım) — elle inceleme adımı başına 6 dk insan zamanı.**

```js
// kutuphane/envanter.mjs — dort kursta yazilmis test kumesi ve kursun ortak kusur kumesi.
// Bedel nicelikleri M21/K03 ile M21/K04 kapanis tablolarindan alinmistir; dakikaya cevrim
// TS1 (kosum) ve TS2 (insan) varsayimlariyla yapilir.
const HAM = [
  // kod, ad, seviye, surec, cagri, elle adim, yakaladigi kusur sinifi
  ["T01", "birim testi kumesi",       "birim",        0,   266,  0, "kural-siniri"],
  ["T02", "tumlesim testi",           "tumlesim",     1,   772,  0, "sema-uyusmazligi"],
  ["T03", "veritabani goc testi",     "tumlesim",     1,    21,  0, "goc-veri-kaybi"],
  ["T04", "sozlesme testi",           "sozlesme",     2,   100,  0, "sozlesme-alani"],
  ["T05", "uctan uca tarayici testi", "uctan-uca",    2, 16477,  0, "arayuz-durumu"],
  ["T06", "gorsel dogrulama",         "uctan-uca",    1,   784,  1, "gorsel-sapma"],
  ["T07", "yuk testi",                "basarim",      9, 55000,  0, "veri-buyumesi"],
  ["T08", "statik guvenlik taramasi", "guvenlik",     0,    75,  7, "sql-birlestirme"],
  ["T09", "bagimlilik taramasi",      "guvenlik",     0,    10,  5, "bilinen-acik"],
  ["T10", "erisilebilirlik denetimi", "guvenlik",     0,    14,  6, "erisilebilirlik"],
  ["T11", "hata enjeksiyonu",         "dayaniklilik", 1,    13,  5, "bozulmus-yanit"],
  ["T12", "kurtarma dogrulamasi",     "dayaniklilik", 2,     7, 10, "kurtarma-eksigi"],
];

export const TESTLER = HAM.map(([kod, ad, seviye, surec, cagri, elle, sinif]) => ({
  kod, ad, seviye, surec, cagri, elle, sinif,
  kosum: 0.5 * surec + 0.0002 * cagri,            // TS1
  insan: 6 * elle,                                // TS2
  dk: 0.5 * surec + 0.0002 * cagri + 6 * elle,
}));

// Otomatik kumenin hicbir uyesinin yakalamadigi iki sinif.
export const ORTUSMEYEN = ["senaryo-disi", "anlam-sapmasi"];

const AGIRLIK = [["kural-siniri", 9], ["sema-uyusmazligi", 6], ["goc-veri-kaybi", 4],
  ["sozlesme-alani", 5], ["arayuz-durumu", 8], ["gorsel-sapma", 3], ["veri-buyumesi", 4],
  ["sql-birlestirme", 3], ["bilinen-acik", 3], ["erisilebilirlik", 4], ["bozulmus-yanit", 3],
  ["kurtarma-eksigi", 2], ["senaryo-disi", 5], ["anlam-sapmasi", 4]];

export function kusurlar(tohum, n) {
  let x = tohum;
  const rast = () => { x = (1103515245 * x + 12345) % 2147483648; return x / 2147483648; };
  const toplam = AGIRLIK.reduce((a, [, w]) => a + w, 0);
  const liste = [];
  for (let i = 1; i <= n; i += 1) {
    let p = rast() * toplam, sinif = AGIRLIK[AGIRLIK.length - 1][0];
    for (const [s, w] of AGIRLIK) { p -= w; if (p < 0) { sinif = s; break; } }
    liste.push({ no: i, sinif, olasilik: 1 + Math.floor(rast() * 5),
      etki: 1 + Math.floor(rast() * 5), duzeltme: 15 + 15 * Math.floor(rast() * 8) });
  }
  return liste;
}
```

İkinci yapı **kusur kümesidir** ve bu kursun bütün derslerinin ortak girdisidir: on dört
sınıf, tohumdan türeyen olasılık, etki ve düzeltme dakikası. Sınıf adları kaynak derslerin
yakaladığı kusurlardan alınmıştır. İkisini — `senaryo-disi` ve `anlam-sapmasi` — envanterdeki
hiçbir test görmez; ikincisi Tümleşim, Sözleşme ve Uçtan Uca Test kursunun API testi
dersinde "yakalamadığı" sütununa yazılan anlamsal uyuşmazlıktır.

#### Dört Plan, Aynı Kusur Kümesi

Plan bir sıralı test listesidir. Bir kusur, sınıfını gören ilk testin bittiği dakikada
yakalanır; hiçbir test görmüyorsa kaçar. **Kaçak oranı** (defect leakage rate) kaçan
kusurun küme içindeki payıdır.

**TS3 (varsayım) — sürüm başına test bütçesi 120 dk.**
**TS4 (varsayım) — çıkış ölçütü: kapsanan kusur sınıfı payı en az %70.**
**TS5 (varsayım) — kaçan hatanın düzeltme maliyeti, yakalanmışın 6 katı.**
**TS6 (varsayım) — ufuk 20 sürüm adayı; kusur kümesi bu ufuk boyunca ortaya çıkar.**
**TS7 (varsayım) — adayların dörtte biri duman testinde kırmızıdır.**

```js
// kutuphane/plan.mjs — test planini bir veri yapisi olarak kurar ve ayni kusur kumesi
// uzerinde dort plani karsilastirir. Girdi: kutuphane/envanter.mjs
import { TESTLER, ORTUSMEYEN, kusurlar } from "./envanter.mjs";

const BUTCE = 120, ADAY = 20, KAT = 6, TOHUM = 20260731, N = 60;   // TS3, TS6, TS5
const SAHIP = { T01: "gelistirme", T02: "gelistirme", T03: "gelistirme", T04: "gelistirme",
  T05: "test", T06: "test", T07: "test", T08: "guvenlik", T09: "guvenlik",
  T10: "urun", T11: "isletme", T12: "isletme" };
const KUSUR = kusurlar(TOHUM, N);
const SINIF = [...new Set([...TESTLER.map((t) => t.sinif), ...ORTUSMEYEN])];
const v = (x, n = 2) => x.toFixed(n).replace(".", ",");
const g = (x, n) => String(x).padStart(n);

// Bir plan sirali bir test listesidir. Kacan hata = plandaki hicbir testin gormedigi kusur.
// Geri bildirim suresi = yakalanan kusurun yakalandigi birikimli dakikanin ortalamasi.
function olc(sira) {
  const an = new Map();
  let t = 0;
  for (const test of sira) { t += test.dk; an.set(test.sinif, t); }
  const yakalanan = KUSUR.filter((k) => an.has(k.sinif));
  const kacan = KUSUR.filter((k) => an.has(k.sinif) === false);
  return { test: sira.length, sinif: an.size, sure: t, kacan: kacan.length,
    oran: (100 * kacan.length) / KUSUR.length,
    geri: yakalanan.reduce((a, k) => a + an.get(k.sinif), 0) / (yakalanan.length || 1),
    duzeltme: kacan.reduce((a, k) => a + k.duzeltme, 0) };
}

const ucuz = [...TESTLER].sort((a, b) => a.dk - b.dk);
const sigan = [];
for (const t of ucuz) if (sigan.reduce((a, x) => a + x.dk, 0) + t.dk <= BUTCE) sigan.push(t);
const olcutlu = ucuz.slice(0, Math.ceil(0.7 * SINIF.length));       // cikis olcutu: %70 sinif
const sahipli = TESTLER.filter((t) => SAHIP[t.kod] !== "urun" && SAHIP[t.kod] !== "guvenlik");
const PLAN = [["P1 tam kapsam", TESTLER], ["P2 butceye sigan (olcutsuz)", sigan],
  ["P3 cikis olcutu >= %70", olcutlu], ["P4 tam kapsam, 3 kalem sahipsiz", sahipli]];

console.log("test envanteri — bedel nicelikleri M21/K03 ve M21/K04 kapanis tablolarindan");
console.log("kod  test                      seviye        kosum  insan  toplam  sinif            sahip");
for (const t of TESTLER) console.log(t.kod + "  " + t.ad.padEnd(26) + t.seviye.padEnd(12) +
  g(v(t.kosum), 6) + g(v(t.insan, 1), 7) + g(v(t.dk), 8) + "  " + t.sinif.padEnd(17) + SAHIP[t.kod]);
console.log("toplam".padEnd(44) + g(v(TESTLER.reduce((a, t) => a + t.kosum, 0)), 6) +
  g(v(TESTLER.reduce((a, t) => a + t.insan, 0), 1), 7) +
  g(v(TESTLER.reduce((a, t) => a + t.dk, 0)), 8) + "  " + TESTLER.length + "/" + SINIF.length + " sinif");

console.log(`\nkusur kumesi: tohum ${TOHUM}, ${N} kusur, ${SINIF.length} sinif`);
console.log("hicbir testin gormedigi sinif: " +
  ORTUSMEYEN.map((s) => `${s} (${KUSUR.filter((k) => k.sinif === s).length})`).join(", "));

console.log(`\nplan karsilastirmasi — butce TS3 = ${BUTCE} dk/surum, ufuk TS6 = ${ADAY} surum`);
console.log("plan                          test  sinif   sure   butce  kacan  kacak%  geri bd");
for (const [ad, sira] of PLAN) {
  const o = olc(sira);
  console.log(ad.padEnd(30) + g(o.test, 4) + g(o.sinif, 7) + g(v(o.sure, 1), 7) +
    g(o.sure <= BUTCE ? "sigar" : "asiyor", 8) + g(o.kacan, 7) + g(v(o.oran, 1), 8) + g(v(o.geri, 1), 9));
}

console.log("\ncikis olcutu taramasi: kapsanan sinif payi kac dakikaya mal olur");
console.log("olcut   gereken sinif   en ucuz sure   butce    kacan hata");
for (const p of [50, 60, 70, 80, 90]) {
  const gerek = Math.ceil((p / 100) * SINIF.length);
  if (gerek > TESTLER.length) { console.log(g("%" + p, 5) + g(gerek, 16) + g("ulasilamaz", 15) +
    g("-", 8) + g("-", 14)); continue; }
  const o = olc(ucuz.slice(0, gerek));
  console.log(g("%" + p, 5) + g(gerek, 16) + g(v(o.sure, 1), 15) +
    g(o.sure <= BUTCE ? "sigar" : "asiyor", 8) + g(o.kacan, 14));
}

const duman = TESTLER[0].dk + TESTLER[1].dk, ps = olc(sigan).sure;
let y = 4242;                                                       // TS7: aday tohumu
const kirmizi = Array.from({ length: ADAY }, () =>
  (y = (1103515245 * y + 12345) % 2147483648) / 2147483648 < 0.25).filter(Boolean).length;
console.log(`\ngiris olcutu: duman testi (T01+T02, ${v(duman)} dk) yesil degilse P2 kosmaz`);
console.log(`${ADAY} surum adayi, tohum 4242: ${kirmizi} aday duman testinde kirmizi`);
console.log(`olcutsuz  P2 ${ADAY} kez kosar ${v(ADAY * ps, 1)} dk; ${kirmizi} kosum bilgi uretmez -> bosa ${v(kirmizi * ps, 1)} dk`);
console.log(`olcutlu   P2 ${ADAY - kirmizi} kez kosar ${v((ADAY - kirmizi) * ps, 1)} dk + duman ${v(ADAY * duman, 1)} dk = ${v((ADAY - kirmizi) * ps + ADAY * duman, 1)} dk`);

console.log(`\nkacan hata kati TS5'in duyarliligi: ${ADAY} surumluk toplam bedel (dk)`);
console.log("kat   P1 tam kapsam   P2 butceye sigan   P3 olcutlu   en ucuz plan");
for (const kat of [2, 6, 20]) {
  const b = PLAN.slice(0, 3).map(([, s]) => { const o = olc(s); return ADAY * o.sure + kat * o.duzeltme; });
  const ad = ["P1", "P2", "P3"];
  console.log(g(kat, 3) + g(v(b[0], 0), 15) + g(v(b[1], 0), 19) + g(v(b[2], 0), 13) +
    g(ad[b.indexOf(Math.min(...b))], 10));
}
```

```
test envanteri — bedel nicelikleri M21/K03 ve M21/K04 kapanis tablolarindan
kod  test                      seviye        kosum  insan  toplam  sinif            sahip
T01  birim testi kumesi        birim         0,05    0,0    0,05  kural-siniri     gelistirme
T02  tumlesim testi            tumlesim      0,65    0,0    0,65  sema-uyusmazligi gelistirme
T03  veritabani goc testi      tumlesim      0,50    0,0    0,50  goc-veri-kaybi   gelistirme
T04  sozlesme testi            sozlesme      1,02    0,0    1,02  sozlesme-alani   gelistirme
T05  uctan uca tarayici testi  uctan-uca     4,30    0,0    4,30  arayuz-durumu    test
T06  gorsel dogrulama          uctan-uca     0,66    6,0    6,66  gorsel-sapma     test
T07  yuk testi                 basarim      15,50    0,0   15,50  veri-buyumesi    test
T08  statik guvenlik taramasi  guvenlik      0,02   42,0   42,02  sql-birlestirme  guvenlik
T09  bagimlilik taramasi       guvenlik      0,00   30,0   30,00  bilinen-acik     guvenlik
T10  erisilebilirlik denetimi  guvenlik      0,00   36,0   36,00  erisilebilirlik  urun
T11  hata enjeksiyonu          dayaniklilik  0,50   30,0   30,50  bozulmus-yanit   isletme
T12  kurtarma dogrulamasi      dayaniklilik  1,00   60,0   61,00  kurtarma-eksigi  isletme
toplam                                       24,21  204,0  228,21  12/14 sinif

kusur kumesi: tohum 20260731, 60 kusur, 14 sinif
hicbir testin gormedigi sinif: senaryo-disi (4), anlam-sapmasi (6)

plan karsilastirmasi — butce TS3 = 120 dk/surum, ufuk TS6 = 20 surum
plan                          test  sinif   sure   butce  kacan  kacak%  geri bd
P1 tam kapsam                   12     12  228,2  asiyor     10    16,7     40,8
P2 butceye sigan (olcutsuz)      9      9   89,2   sigar     18    30,0     16,5
P3 cikis olcutu >= %70          10     10  125,2  asiyor     14    23,3     26,0
P4 tam kapsam, 3 kalem sahipsiz   9      9  120,2  asiyor     20    33,3     13,7

cikis olcutu taramasi: kapsanan sinif payi kac dakikaya mal olur
olcut   gereken sinif   en ucuz sure   butce    kacan hata
  %50               7           28,7   sigar            24
  %60               9           89,2   sigar            18
  %70              10          125,2  asiyor            14
  %80              12          228,2  asiyor            10
  %90              13     ulasilamaz       -             -

giris olcutu: duman testi (T01+T02, 0,71 dk) yesil degilse P2 kosmaz
20 surum adayi, tohum 4242: 3 aday duman testinde kirmizi
olcutsuz  P2 20 kez kosar 1783,8 dk; 3 kosum bilgi uretmez -> bosa 267,6 dk
olcutlu   P2 17 kez kosar 1516,2 dk + duman 14,2 dk = 1530,4 dk

kacan hata kati TS5'in duyarliligi: 20 surumluk toplam bedel (dk)
kat   P1 tam kapsam   P2 butceye sigan   P3 olcutlu   en ucuz plan
  2           5974               4424         4544        P2
  6           8794               9704         8624        P3
 20          18664              28184        22904        P1
```

Bütün sayılar belirlenimlidir: aynı tohum aynı kusur kümesini verir. Envanterin ilk okuması
bir dengesizliği gösteriyor — 228,21 dakikalık planın 204'ü insan zamanıdır; en pahalı üç
kalem (T12, T08, T10) neredeyse tümüyle elle inceleme adımlarıdır.

#### İki Dağıtım, İki Sonuç

P1 bütün kusur sınıflarını kapsar ve sürüm başına 228,2 dakika ister — bütçeyi 108,2 dakika
aşar. P2 aynı bütçeye sığmak için en ucuz testleri sırayla alır; dokuz test, 89,2 dakika. Bu
iki dağıtımın aynı kusur kümesi üzerindeki sonucu farklıdır: P1'de 10 kusur kaçar (%16,7),
P2'de 18 kusur kaçar (%30,0). Buna karşılık P2'nin geri bildirim süresi 16,5 dakikadır,
P1'inki 40,8 — çünkü P1'in ucundaki pahalı kalemler yakaladıkları kusuru geç haber verir.
**Bir dağıtım kaçan hatayı azaltırken geri bildirim süresini uzatır.** Bu kursun bütün
derslerinde tekrarlanan takas budur.

Kaçan hatanın bedeli TS5'e bağlıdır ve bu varsayım ölçülmemiştir; duyarlılık taraması
bunu görünür kılar. Kat 2 iken en ucuz plan P2, kat 6 iken P3, kat 20 iken P1'dir. Yani
planın gerekçesi tek bir varsayımın değerine bağlıdır — ama üç planın yalnız biri, P2,
bütçeye sığar. Bütçe bir maliyet kalemi değil, bir sınırdır.

#### Çıkış Ölçütünün Tavanı

Çıkış ölçütü taraması ölçütü bir fiyatla eşler. %50'lik bir ölçüt 28,7 dakikaya, %60 89,2
dakikaya mal olur ve ikisi de bütçeye sığar. TS4'ün seçtiği %70 ise 125,2 dakika ister —
bütçeyi 5,2 dakika aşar. Ölçüt ile bütçe çelişir ve planın birini seçmesi gerekir.

%90 satırı ayrı bir şey söyler: **ulaşılamaz.** On dört sınıfın on ikisini gören test var,
ikisini gören yok; hiçbir bütçe bu tavanı kaldırmaz, çünkü tavan zamandan değil kapsamdan
gelir. Plan bu iki sınıfı ya kapsam dışı diye açıkça yazar ya da onları görecek bir sınama
biçimi ekler. Kapsam dışının ürettiği kaçan hata bu kümede 10 kusurdur.

#### Giriş Ölçütü ve Sorumluluk

Giriş ölçütü koşumun kendisini değil, koşumun ne zaman başlayacağını düzenler. Duman testi
(T01 ile T02, 0,71 dakika) yeşil değilken P2'yi koşturmak yirmi adayda 267,6 dakikayı bilgi
üretmeyen koşumlara harcar. Ölçüt konduğunda duman testinin yirmi kez koşması 14,2 dakika
tutar ve toplam 1.530,4 dakikaya iner — 253,4 dakikalık bir kazanç, ucuz bir yoklamanın
pahalı bir aşamayı koruması.

Sorumluluk sütunu planın en sessiz sütunudur. P4, P1 ile aynı kapsamı yazar ama üç kalemin
sahibi ekip dışındadır ve o kalemler koşmaz. Kâğıtta 12 sınıf kapsanır, gerçekte 9; kaçan
hata 10'dan 20'ye çıkar ve kaçak oranı %33,3 olur — bütçeye sığan ölçütsüz plandan daha
kötü bir sonuç, üstelik plan tam kapsam iddiasındayken. **Sahibi yazılmamış kalem kapsamda
değildir.**

Kırmızı sonucun ne yaptığı da plana yazılır: T01–T05 kırmızıya döndüğünde sürüm adayı
ilerlemez, T06 ile T10 sürümü durdurmadan bir kayıt açar, T12'nin kırmızısı işletmenin
kararına girer. Hiçbir kararı değiştirmeyen test bütçeden pay almamalıdır.

#### Özet

- Test planı üç sütundur: kapsam, ölçüt ve sorumluluk; üçü de sayıya bağlanmadan plan karar
  taşımaz.
- Dört kursun on iki testi 228,21 dakikadır ve bunun 204'ü insan zamanıdır; bütçe (TS3)
  120 dakika olduğu için tam kapsam bir plan değil bir istektir.
- Aynı kusur kümesinde tam kapsam 10 kusur kaçırıp 40,8 dakikada haber verir; bütçeye sığan
  plan 18 kusur kaçırıp 16,5 dakikada haber verir.
- Çıkış ölçütü bir fiyatla gelir: %70 ölçütü bütçeyi aşan 125,2 dakika ister, %90 hiçbir
  bütçeyle ulaşılamaz çünkü iki kusur sınıfını hiçbir test görmez.
- Giriş ölçütü ucuz bir yoklamayı pahalı aşamanın önüne koyar: 267,6 dakikalık boşa koşum
  14,2 dakikalık duman testiyle önlenir.
- Sahipsiz üç kalem tam kapsamlı planı dokuz teste indirir, kaçak oranını %16,7'den %33,3'e
  çıkarır.

#### Sonraki Adım

Bütçeye sığan plan testleri **en ucuzdan** seçti; bu sıralamanın tek gerekçesi bütçeye çok
kalem sığdırmaktı. Oysa kusur kümesindeki her kusurun bir olasılığı ve bir etkisi var ve
ucuzluk bunların ikisiyle de ilgili değil. Aynı 120 dakika başka bir sırayla dağıtılsaydı
kaç kusur yakalanırdı? Sonraki ders sıralamayı bir karar hâline getirir: olasılık ile
etkinin çarpımına göre seçilen alt küme, rastgele seçilenle, en ucuz önce seçilenle ve en
çok kod dokunanı önce seçenle aynı bütçede karşılaştırılır.

### Risk Temelli Önceliklendirme

Source: https://academia.sh/tr/kurslar/test-sureci/risk-temelli-onceliklendirme

Aynı bütçenin beş ayrı sırayla dağıtılması: olasılık ile etkinin çarpımından türeyen sınıf riski, risk sırasının dakikaya bölünmesiyle ortaya çıkan yoğunluk sırası, rastgele ve en ucuz ile en çok bileşene dokunan sıralarla karşılaştırma, tahmin hatasının duyarlılığı ve sıranın yirmi sürümlük geri dönüşü.

Önceki ders bütçeye sığan planı en ucuz testleri sırayla alarak kurdu; o sıralamanın tek
gerekçesi yüz yirmi dakikaya çok kalem sığdırmaktı. Oysa her kusurun bir olasılığı ve bir
etkisi var ve bir testin ucuzluğu bunların ikisiyle de ilgili değil. Aynı bütçe başka bir
sırayla dağıtılsaydı ne değişirdi.

Bu ders sıralamayı bir karara dönüştürür. **Risk temelli önceliklendirme**
(risk-based prioritization) sınırlı bütçeyi kusur sınıflarının riskine göre dağıtır; risk
burada iki niceliğin çarpımıdır — bir kusurun ortaya çıkma **olasılığı** (likelihood) ve
ortaya çıktığında verdiği **etki** (impact). Dağıtılan kaynak yine sürüm başına 120 dakikadır
(TS3); ölçülen şey yine kaçan hata ile geri bildirim süresidir.

#### Risk Puanı ve Onun Tahmini

Bir kusurun **risk puanı** olasılık ile etkinin çarpımıdır; bir **sınıf riski** o sınıftaki
kusurların risk puanları toplamıdır. Kusur kümesindeki olasılık ve etki değerleri birinci
dersin tohumundan gelir, uydurulmaz.

**TS8 (varsayım) — risk puanı olasılık × etkidir, iki nicelik eşit ağırlıklıdır.**
**TS9 (varsayım) — plan gerçek sınıf riskini bilmez, tahmin eder; tahmin gerçeğin
$[1-e,\,1+e]$ katıdır ve taban durumda $e = 0{,}50$'dir.** Gerçek sınıf riski ancak kusurlar
ortaya çıktıktan sonra bilinir; sıralama öncesinde yapılır.
**TS10 (varsayım) — bir testin ayağa kaldırdığı süreç sayısı, dokunduğu bileşen sayısının
gözlenebilir vekilidir.** "En çok kod dokunan önce" sırası bu vekille kurulur; süreç sayıları
kapanış tablolarından okunmuştur.

```js
// kutuphane/envanter.mjs — birinci dersin envanteri ve kusur kumesi, kisa yazimla.
// Bedel nicelikleri M21/K03 ve M21/K04 kapanis tablolarindan; dakikaya cevrim TS1-TS2.
const HAM = [
  ["T01", "birim testi kumesi", "birim", 0, 266, 0, "kural-siniri"],
  ["T02", "tumlesim testi", "tumlesim", 1, 772, 0, "sema-uyusmazligi"],
  ["T03", "veritabani goc testi", "tumlesim", 1, 21, 0, "goc-veri-kaybi"],
  ["T04", "sozlesme testi", "sozlesme", 2, 100, 0, "sozlesme-alani"],
  ["T05", "uctan uca tarayici testi", "uctan-uca", 2, 16477, 0, "arayuz-durumu"],
  ["T06", "gorsel dogrulama", "uctan-uca", 1, 784, 1, "gorsel-sapma"],
  ["T07", "yuk testi", "basarim", 9, 55000, 0, "veri-buyumesi"],
  ["T08", "statik guvenlik taramasi", "guvenlik", 0, 75, 7, "sql-birlestirme"],
  ["T09", "bagimlilik taramasi", "guvenlik", 0, 10, 5, "bilinen-acik"],
  ["T10", "erisilebilirlik denetimi", "guvenlik", 0, 14, 6, "erisilebilirlik"],
  ["T11", "hata enjeksiyonu", "dayaniklilik", 1, 13, 5, "bozulmus-yanit"],
  ["T12", "kurtarma dogrulamasi", "dayaniklilik", 2, 7, 10, "kurtarma-eksigi"],
];
export const TESTLER = HAM.map(([kod, ad, seviye, surec, cagri, elle, sinif]) => ({
  kod, ad, seviye, surec, cagri, elle, sinif, dk: 0.5 * surec + 0.0002 * cagri + 6 * elle,
}));
export const ORTUSMEYEN = ["senaryo-disi", "anlam-sapmasi"];
const AGIRLIK = [["kural-siniri", 9], ["sema-uyusmazligi", 6], ["goc-veri-kaybi", 4],
  ["sozlesme-alani", 5], ["arayuz-durumu", 8], ["gorsel-sapma", 3], ["veri-buyumesi", 4],
  ["sql-birlestirme", 3], ["bilinen-acik", 3], ["erisilebilirlik", 4], ["bozulmus-yanit", 3],
  ["kurtarma-eksigi", 2], ["senaryo-disi", 5], ["anlam-sapmasi", 4]];
export function kusurlar(tohum, n) {
  let x = tohum;
  const rast = () => { x = (1103515245 * x + 12345) % 2147483648; return x / 2147483648; };
  const toplam = AGIRLIK.reduce((a, [, w]) => a + w, 0);
  const liste = [];
  for (let i = 1; i <= n; i += 1) {
    let p = rast() * toplam, sinif = AGIRLIK[AGIRLIK.length - 1][0];
    for (const [s, w] of AGIRLIK) { p -= w; if (p < 0) { sinif = s; break; } }
    liste.push({ no: i, sinif, olasilik: 1 + Math.floor(rast() * 5),
      etki: 1 + Math.floor(rast() * 5), duzeltme: 15 + 15 * Math.floor(rast() * 8) });
  }
  return liste;
}
```

#### Aynı Bütçe, Beş Sıra

Sıra bir test listesidir; bütçe dolana kadar sırayla alınır, sığmayan atlanır. Beş sıra
karşılaştırılır: tahmini riske göre azalan (S1), tahmini riskin dakikaya bölümüne göre azalan
(S2), en ucuz önce (S3, birinci dersin sırası), rastgele (S4), en çok bileşene dokunan önce
(S5).

```js
// kutuphane/risk.mjs — ayni butce, bes sira, ayni kusur kumesi. Girdi: kutuphane/envanter.mjs
import { TESTLER, ORTUSMEYEN, kusurlar } from "./envanter.mjs";

const BUTCE = 120, TOHUM = 20260731, N = 60, KAT = 6, ADAY = 20;   // TS3, TS5, TS6
const KUSUR = kusurlar(TOHUM, N);
const SINIF = [...new Set([...TESTLER.map((t) => t.sinif), ...ORTUSMEYEN])];
const v = (x, n = 1) => x.toFixed(n).replace(".", ",");
const g = (x, n) => String(x).padStart(n);
const uret = (tohum) => { let x = tohum;
  return () => { x = (1103515245 * x + 12345) % 2147483648; return x / 2147483648; }; };

// TS8: risk puani = olasilik x etki; sinif riski o siniftaki kusurlarin risk toplamidir.
const GERCEK = new Map(SINIF.map((s) => [s, KUSUR.filter((k) => k.sinif === s)
  .reduce((a, k) => a + k.olasilik * k.etki, 0)]));
// TS9: plan gercek riski bilmez, tahmin eder; tahmin [1-e, 1+e] carpaniyla sapar.
const tahmin = (e, r) => new Map(SINIF.map((s) => [s, GERCEK.get(s) * (1 - e + 2 * e * r())]));

// Sirali listeden butceye sigani al; sigmayan atlanir, sira bozulmaz.
const sec = (sira) => { const c = []; let t = 0;
  for (const x of sira) if (t + x.dk <= BUTCE) { c.push(x); t += x.dk; } return c; };
function olc(c) {
  const an = new Map(); let t = 0;
  for (const x of c) { t += x.dk; an.set(x.sinif, t); }
  const yak = KUSUR.filter((k) => an.has(k.sinif)),
    kac = KUSUR.filter((k) => an.has(k.sinif) === false);
  return { n: c.length, kod: c.map((x) => x.kod).join(" "), sure: t, yak: yak.length,
    kac: kac.length, oran: (100 * kac.length) / KUSUR.length,
    risk: kac.reduce((a, k) => a + k.olasilik * k.etki, 0),
    geri: yak.reduce((a, k) => a + an.get(k.sinif), 0) / (yak.length || 1),
    duz: kac.reduce((a, k) => a + k.duzeltme, 0) };
}
const risksira = (T) => [...TESTLER].sort((a, b) => T.get(b.sinif) - T.get(a.sinif));
const yogunsira = (T) => [...TESTLER].sort((a, b) => T.get(b.sinif) / b.dk - T.get(a.sinif) / a.dk);

const TOP = [...GERCEK.values()].reduce((a, x) => a + x, 0);
const TABAN = ORTUSMEYEN.reduce((a, s) => a + GERCEK.get(s), 0);
console.log(`sinif riski (TS8) ve tahmin (TS9, e = %50) — tohum ${TOHUM}, ${N} kusur, ${SINIF.length} sinif`);
console.log("sinif             kusur  gercek  tahmin  test     dk  tahmin/dk");
const T50 = tahmin(0.5, uret(8125));
for (const s of SINIF) {
  const t = TESTLER.find((x) => x.sinif === s);
  console.log(s.padEnd(18) + g(KUSUR.filter((k) => k.sinif === s).length, 5) + g(GERCEK.get(s), 8) +
    g(v(T50.get(s)), 8) + "  " + (t ? t.kod : "yok ") + g(t ? v(t.dk, 2) : "-", 7) +
    g(t ? v(T50.get(s) / t.dk, 2) : "-", 11));
}
console.log(`toplam risk ${TOP}; hicbir testin gormedigi iki sinif ${TABAN} puan -> erisilebilir risk ${TOP - TABAN}`);

const S = { S1: risksira(T50), S2: yogunsira(T50),
  S3: [...TESTLER].sort((a, b) => a.dk - b.dk),
  S5: [...TESTLER].sort((a, b) => b.surec - a.surec || b.cagri - a.cagri) };
const AD = { S1: "S1 risk azalan (tahmin)", S2: "S2 tahmin/dk azalan", S3: "S3 en ucuz once",
  S5: "S5 en cok bilesen once" };
const r4 = uret(4242), rast = [];
for (let i = 0; i < 200; i += 1) { const p = [...TESTLER];
  for (let j = p.length - 1; j > 0; j -= 1) { const q = Math.floor(r4() * (j + 1));
    [p[j], p[q]] = [p[q], p[j]]; }
  rast.push(olc(sec(p))); }
const ort = (f) => rast.reduce((a, o) => a + f(o), 0) / rast.length;

// Son sutun kararin geri donusudur: ADAY surumluk kosum + kacan hatanin KAT'li duzeltmesi.
console.log(`\nayni butce TS3 = ${BUTCE} dk, ayni kusur kumesi; son sutun TS5-TS6 ile ${ADAY} surumluk`);
console.log("sira                          test   sure  kacan  kacak%  kacan risk  geri bd  toplam dk");
const w = (x) => (Number.isInteger(x) ? String(x) : v(x, 2));
const yaz = (ad, o) => console.log(ad.padEnd(30) + g(w(o.n), 5) + g(v(o.sure), 7) +
  g(w(o.kac), 7) + g(v(o.oran), 8) + g(v(o.risk), 12) + g(v(o.geri), 9) +
  g(v(ADAY * o.sure + KAT * o.duz, 0), 11));
for (const k of ["S1", "S2", "S3", "S5"]) yaz(AD[k], olc(sec(S[k])));
yaz("S4 rastgele (200 permutasyon)", { n: ort((o) => o.n), sure: ort((o) => o.sure),
  kac: ort((o) => o.kac), oran: ort((o) => o.oran), risk: ort((o) => o.risk),
  geri: ort((o) => o.geri), duz: ort((o) => o.duz) });
console.log(`S4 yayilimi: kacan risk en iyi ${Math.min(...rast.map((o) => o.risk))}, ` +
  `en kotu ${Math.max(...rast.map((o) => o.risk))}; 200 permutasyonun ` +
  `${rast.filter((o) => o.risk <= olc(sec(S.S2)).risk).length} tanesi S2 kadar iyi`);
for (const k of ["S1", "S5"]) console.log(k + " secilen: " + olc(sec(S[k])).kod);

console.log("\ntahmin hatasi TS9'un duyarliligi — e basina 200 cizim, ortalama kacan risk");
console.log("e      S1 ortalama  S2 ortalama   S2 > S3 olan cizim  en kotu S2");
const S3R = olc(sec(S.S3)).risk;
for (const e of [0, 0.25, 0.5, 1]) {
  const r = uret(9001); let a = 0, b = 0, yenik = 0, kotu = 0;
  for (let i = 0; i < 200; i += 1) { const T = tahmin(e, r);
    const o1 = olc(sec(risksira(T))), o2 = olc(sec(yogunsira(T)));
    a += o1.risk; b += o2.risk; kotu = Math.max(kotu, o2.risk); if (o2.risk > S3R) yenik += 1; }
  console.log(g("%" + Math.round(100 * e), 4) + g(v(a / 200), 12) + g(v(b / 200), 13) +
    g(yenik, 21) + g(kotu, 12));
}
console.log(`karsilastirma tabani: S3 en ucuz once, kacan risk ${S3R}`);
```

```
sinif riski (TS8) ve tahmin (TS9, e = %50) — tohum 20260731, 60 kusur, 14 sinif
sinif             kusur  gercek  tahmin  test     dk  tahmin/dk
kural-siniri          7      63    40,8  T01   0,05     766,99
sema-uyusmazligi      7      65    60,2  T02   0,65      92,04
goc-veri-kaybi        3      15    21,6  T03   0,50      42,85
sozlesme-alani        7      75   101,7  T04   1,02      99,68
arayuz-durumu         4      39    24,0  T05   4,30       5,58
gorsel-sapma          2      23    33,2  T06   6,66       4,99
veri-buyumesi         6      51    64,5  T07  15,50       4,16
sql-birlestirme       3      51    57,3  T08  42,02       1,36
bilinen-acik          3      33    28,4  T09  30,00       0,95
erisilebilirlik       4      32    32,5  T10  36,00       0,90
bozulmus-yanit        3      21    12,9  T11  30,50       0,42
kurtarma-eksigi       1       9     5,3  T12  61,00       0,09
senaryo-disi          4      33    49,1  yok       -          -
anlam-sapmasi         6      60    78,7  yok       -          -
toplam risk 570; hicbir testin gormedigi iki sinif 93 puan -> erisilebilir risk 477

ayni butce TS3 = 120 dk, ayni kusur kumesi; son sutun TS5-TS6 ile 20 surumluk
sira                          test   sure  kacan  kacak%  kacan risk  geri bd  toplam dk
S1 risk azalan (tahmin)           9  106,7     17    28,3       156,0     48,9       8434
S2 tahmin/dk azalan               9  100,7     18    30,0       155,0     18,2       9574
S3 en ucuz once                   9   89,2     18    30,0       185,0     16,5       9704
S5 en cok bilesen once            9  119,7     20    33,3       197,0     61,4      11484
S4 rastgele (200 permutasyon)  8,48  111,7  21,36    35,6       207,1     75,8      11220
S4 yayilimi: kacan risk en iyi 155, en kotu 377; 200 permutasyonun 17 tanesi S2 kadar iyi
S1 secilen: T04 T07 T02 T08 T01 T06 T10 T05 T03
S5 secilen: T07 T05 T04 T12 T06 T02 T03 T01 T09

tahmin hatasi TS9'un duyarliligi — e basina 200 cizim, ortalama kacan risk
e      S1 ortalama  S2 ortalama   S2 > S3 olan cizim  en kotu S2
  %0       155,0        155,0                    0         155
 %25       155,7        156,4                    0         174
 %50       161,8        161,6                    4         186
%100       175,5        167,3                   20         204
karsilastirma tabani: S3 en ucuz once, kacan risk 185
```

#### Sayı Saymak ile Risk Saymak

Altmış kusurdan yakalananlar 43, 42, 42, 40 ve ortalama 38,64'tür; kaçan hata **sayısı** beş
sırayı zor ayırır: 17, 18, 18, 20 ve ortalama 21,36. Kaçan hatanın **risk toplamı** ise
ayırır: 156, 155, 185, 197 ve ortalama 207,1. Farkı yaratan, birinin her
kusuru bir sayması, ötekinin ağırlığıyla saymasıdır. S2 ile S3 aynı sayıda kusuru kaçırır,
ama S3'ün kaçırdıkları 30 risk puanı daha ağırdır.

Karşılaştırmanın tabanı da tabloda: toplam 570 riskin 93 puanı hiçbir testin görmediği iki
sınıfa aittir, yani sıranın oynayabileceği aralık 477 puandır. S2 bu aralığın 62 puanını
kaçırır, S3 ise 92 puanını — yalnız sıra değiştirerek 1,48 kat fark. Tabana bakmadan yazılan
bir "kaçan risk 155" cümlesi bu oranı gizler.

#### Risk Sırası ile Yoğunluk Sırası

S1 ile S2 aynı tahmini kullanır ve neredeyse aynı kaçan riske varır (156'ya karşı 155). Ayrım
başka yerdedir: S1'in geri bildirim süresi 48,9 dakika, S2'ninki 18,2 — 2,7 kat. Nedeni
seçilen sıralarda görünür. S1 dördüncü sıraya 42,02 dakikalık statik güvenlik taramasını
koyar, çünkü o sınıfın tahmini riski yüksektir; S2 aynı testi sona atar, çünkü dakikaya
bölündüğünde tahmin/dk değeri 1,36'dır. Yüksek riskli ama pahalı bir kalem sıranın başına
konduğunda kendinden sonraki her testin haber verme anını geciktirir.

**Risk sırası tek başına yanlış sıradır; bölen dakikadır.** T01'in tahmin/dk değeri 766,99,
T12'ninki 0,09 — hiçbir risk sıralamasının tek başına göremeyeceği bir fark.

S5 bu noktayı tersinden gösterir: en çok bileşene dokunan sıra ilk iki kalemine yük testi ile
kurtarma doğrulamasını alır, 120 dakikanın 76,5'ini iki testle harcar ve 20 kusur kaçırır.
Çok koda dokunmak, çok risk görmek değildir. S4 ise sıranın kendisinin bir kazanç olduğunu
gösterir: iki yüz permütasyonun ortalaması 207,1, en kötüsü 377, yalnız 17'si S2 kadar iyi.
Sırası yazılmamış plan, ortalamada rastgele plandır.

#### Tahminin Yanılma Payı

Sıralama gerçek riske değil tahmine dayanır ve tahmin yanılır. Duyarlılık taraması her hata
düzeyi için iki yüz çizim yapar. Hata sıfırken iki sıra da 155'te buluşur — üst sınır budur.
Hata ±%50'ye çıktığında S2'nin ortalaması 161,6 olur ve iki yüz çizimin yalnız dördü en ucuz
önce sırasının 185'inden kötüdür. Hata ±%100 iken ortalama 167,3'te kalır, yenilen çizim
sayısı 20'ye çıkar, en kötü çizim 204 olur.

Kazanç yavaş erir, çünkü sıra tahminin **değerini** değil **düzenini** kullanır: bir sınıfın
riski iki katı yanlış tahmin edilse bile listedeki yeri çoğu zaman aynı kalır. Buradan çıkan
kural, tahminin kaynağının yazılmasıdır — kusur geçmişindeki sınıf dağılımı, değişiklik
yoğunluğu, etkilenen kullanıcı sayısı. Kaynağı yazılmamış bir risk sırası bir tercih
listesidir; ±%100 satırı o tercihin bedelini verir.

#### Kararın Geri Dönüşü

Tablonun son sütunu — yirmi sürümlük koşum artı kaçan hatanın altı katlı düzeltmesi —
sıralamayı üçüncü bir nicelikle sınar: S1 8.434 dakikayla
en ucuzdur, S2 9.574, S3 9.704, rastgele 11.220, en çok bileşen 11.484. S1'in kaçan riski
S2'den yüksek olmasına karşın toplam bedeli düşüktür, çünkü risk puanı düzeltme maliyetini
ölçmez: kusur kümesinde düzeltme dakikası olasılıktan ve etkiden bağımsız çekilir. Bir sıra
üç niceliği birden eniyileyemez; hangisinin eniyileneceği plana yazılır.

Kırmızı sonucun anlamı da sıradan gelir. Tahmini riski en yüksek sınıfların testleri —
sözleşme testi ve tümleşim testi — kırmızıya döndüğünde sürüm adayı ilerlemez; kuyruktaki
kalemler bir kayıt açar. Sıranın kendisi de bir bakım kalemidir: kusur geçmişi değiştikçe
sınıf riski değişir, yeniden türetilmeyen bir risk sırası birkaç sürüm sonra bir alışkanlığa
dönüşür ve kazancı ±%100 satırındaki değerlere iner.

#### Özet

- Risk puanı olasılık ile etkinin çarpımıdır (TS8); sınıf riskini plan bilmez, tahmin eder
  (TS9).
- Kaçan hata sayısı beş sırayı 17 ile 21,36 arasında ayırır, kaçan risk 155 ile 207,1
  arasında; ağırlıksız sayma sıralamanın kazancını gizler.
- Toplam 570 riskin 93 puanı hiçbir testin görmediği iki sınıfa aittir; sıranın oynadığı
  aralık 477'dir ve bu aralıkta S2 ile S3 arasındaki fark 1,48 kattır.
- Risk sırası pahalı bir kalemi öne alarak geri bildirimi 48,9 dakikaya çıkarır; aynı tahmin
  dakikaya bölündüğünde kaçan risk 155'te kalır, geri bildirim 18,2 dakikaya iner.
- Tahmin hatası ±%100'e çıktığında yoğunluk sırasının ortalaması 167,3 olur ve iki yüz
  çizimin 20'si en ucuz önce sırasına yenilir; kazanç yavaş erir.
- Risk puanı düzeltme maliyetini ölçmez: en düşük kaçan riske sahip sıra en ucuz sıra
  olmayabilir.

#### Sonraki Adım

Beş sıra da aynı on iki testin içinden seçim yaptı ve hiçbiri 93 puanlık tabana dokunamadı.
O tabanın 33 puanı `senaryo-disi` sınıfına ait: yazılmış hiçbir senaryonun geçmediği yollarda
ortaya çıkan dört kusur. Bu sınıfı görmenin yolu sırayı değiştirmek değil, sınama biçimini
değiştirmektir — betiği önceden yazılmamış, yönü sınama sırasında belirlenen bir oturum.
Sonraki ders o oturumu bir bütçe kalemi hâline getirir: kapsamın darlığı oturumun bulduğunu
nasıl değiştiriyor ve bulunan kusurun ne kadarı betikli kümeye dönüşüyor.

### Keşifsel Test

Source: https://academia.sh/tr/kurslar/test-sureci/kesifsel-test

Betiği önceden yazılmamış sınamanın bir bütçe kalemine dönüştürülmesi: oturum kapsamının genişlik ile derinlik arasında bölünmesi, betikli kümenin göremediği kusur sınıfının yalnız derinlikle görünmesi, bulunan kusurun betiğe dönüşme oranı ve yirmi sürümlük ufukta kaçan hata ile net bedelin ölçülmesi.

Önceki dersin beş sırası da aynı on iki testin içinden seçim yaptı ve hiçbiri 93 puanlık
tabana dokunamadı. O tabanı üreten iki sınıftan biri `senaryo-disi`: yazılmış hiçbir
senaryonun geçmediği yollarda ortaya çıkan kusurlar. Bir sınıf, onu görecek bir test
yazılmadığı sürece sıralamayla bulunamaz.

**Keşifsel test** (exploratory testing) betiği önceden yazılmamış, yönü sınama sırasında
görülene göre belirlenen bir sınama biçimidir. Serbest olması yapılandırılmamış olduğu
anlamına gelmez: sınayan bir **oturum kapsamı** yazar — hangi bölgeye, ne kadar süreyle, ne
arayarak bakılacağı. Bu ders o kapsamı bir bütçe dağıtımı olarak kurar; dağıtılan kaynak
insan dikkatidir ve iki yöne birden harcanamaz.

#### Genişlik ile Derinlik Aynı Bütçedendir

Sistemin sınanabilir yüzeyi iki boyutludur: kaç **yol** gezildiği (genişlik) ve her yolda ne
kadar ileri gidildiği (derinlik). Bir oturumun bedeli bu ikisinin çarpımıdır, dolayısıyla
sabit bütçede biri ötekinin karşılığıdır.

**TS11 (varsayım) — sürüm başına keşifsel oturum bütçesi 120 dakikadır**; bu, birinci dersin
otomatik test bütçesiyle (TS3) aynı büyüklüktür ve seçimi duyarlılık taramasıyla sınanır.
**TS12 (varsayım) — bir yolu bir derinlik seviyesi kadar sınamak 6 dakika sürer.** Bütçe
böylece 20 yol-derinlik birimidir.
**TS13 (varsayım) — her kusur bir yolda ve bir derinlikte durur; senaryo dışı kalan iki sınıf
tanımı gereği derindir (3–5), ötekiler 1–5 arasına dağılır.** Bu, sınıfın tanımından gelen bir
kısıttır: yazılı senaryoların geçtiği sığ yollarda duran bir kusur zaten betikli kümede
görünürdü.
**TS14 (varsayım) — bulunan bir sınıf bir sonraki sürümden başlayarak betikli kümeye
çevrilir**; testsiz bir sınıf için 45 dakika yazım artı sürüm başına 5 dakika koşum, envanterde
testi olan bir sınıf için yazım yoktur, bedel o testin sürüm başına dakikasıdır.

```js
// kutuphane/envanter.mjs — birinci dersin envanterinden bu derste gereken alanlar: her testin
// kusur sinifi ve bedel nicelikleri (surec, cagri, elle adim; M21/K03 ve K04 kapanis
// tablolarindan). Dakikaya cevrim birinci dersteki TS1 ve TS2 ile, aynen.
const HAM = [["T01", "kural-siniri", 0, 266, 0], ["T02", "sema-uyusmazligi", 1, 772, 0],
  ["T03", "goc-veri-kaybi", 1, 21, 0], ["T04", "sozlesme-alani", 2, 100, 0],
  ["T05", "arayuz-durumu", 2, 16477, 0], ["T06", "gorsel-sapma", 1, 784, 1],
  ["T07", "veri-buyumesi", 9, 55000, 0], ["T08", "sql-birlestirme", 0, 75, 7],
  ["T09", "bilinen-acik", 0, 10, 5], ["T10", "erisilebilirlik", 0, 14, 6],
  ["T11", "bozulmus-yanit", 1, 13, 5], ["T12", "kurtarma-eksigi", 2, 7, 10]];
export const TESTLER = HAM.map(([kod, sinif, surec, cagri, elle]) =>
  ({ kod, sinif, dk: 0.5 * surec + 0.0002 * cagri + 6 * elle }));
const AGIRLIK = [["kural-siniri", 9], ["sema-uyusmazligi", 6], ["goc-veri-kaybi", 4],
  ["sozlesme-alani", 5], ["arayuz-durumu", 8], ["gorsel-sapma", 3], ["veri-buyumesi", 4],
  ["sql-birlestirme", 3], ["bilinen-acik", 3], ["erisilebilirlik", 4], ["bozulmus-yanit", 3],
  ["kurtarma-eksigi", 2], ["senaryo-disi", 5], ["anlam-sapmasi", 4]];
export function kusurlar(tohum, n) {
  let x = tohum;
  const rast = () => { x = (1103515245 * x + 12345) % 2147483648; return x / 2147483648; };
  const toplam = AGIRLIK.reduce((a, [, w]) => a + w, 0);
  const liste = [];
  for (let i = 1; i <= n; i += 1) {
    let p = rast() * toplam, sinif = AGIRLIK[AGIRLIK.length - 1][0];
    for (const [s, w] of AGIRLIK) { p -= w; if (p < 0) { sinif = s; break; } }
    liste.push({ no: i, sinif, olasilik: 1 + Math.floor(rast() * 5),
      etki: 1 + Math.floor(rast() * 5), duzeltme: 15 + 15 * Math.floor(rast() * 8) });
  }
  return liste;
}
```

#### Dört Kapsam, Yirmi Sürüm

Dört dağıtım aynı 120 dakikayı böler: yirmi yolu bir derinliğe kadar (E1), on yolu iki (E2),
beş yolu dört (E3), dört yolu beş (E4). Kapsamın hangi yolları kapsayacağı bir bilgi
sorunudur — plan kusurun nerede olduğunu bilmez, bu yüzden yol seçimi tohumlu ve rastgeledir
ve iki yüz çizimin ortalaması alınır. Ölçüm yirmi sürümlük ufuk boyunca yürür: her sürümde üç
kusur ortaya çıkar, bir oturum koşar, bulunan sınıf sonraki sürümden başlayarak betikli kümeye
girer.

```js
// kutuphane/kesif.mjs — kesifsel oturumu bir butce dagitimi olarak olcer.
// Girdi: kutuphane/envanter.mjs (birinci dersten devralinan test kumesi ve kusur kumesi).
import { TESTLER, kusurlar } from "./envanter.mjs";

const TOHUM = 20260731, N = 60, YOL = 20, BIRIM = 6, OTURUM = 120, CIZIM = 200;
const KAT = 6, ADAY = 20, PAY = N / ADAY;                        // TS5, TS6: surum basina 3 kusur
// Ikinci dersin yogunluk sirasinin butceye sigan dokuz kalemi; kalan siniflar betiksizdir.
const BETIKLI = new Set(["T01", "T02", "T03", "T04", "T05", "T06", "T07", "T08", "T09"]
  .map((k) => TESTLER.find((t) => t.kod === k).sinif));
// Betiksiz siniflar ikiye ayrilir: envanterde testi olan ama butceye girmeyenler (VARTEST)
// ve hicbir testin gormedigi ikisi (DERIN).
const VARTEST = new Map(TESTLER.filter((t) => BETIKLI.has(t.sinif) === false).map((t) => [t.sinif, t]));
const DERIN = new Set(["senaryo-disi", "anlam-sapmasi"]);
const v = (x, n = 1) => x.toFixed(n).replace(".", ",");
const g = (x, n) => String(x).padStart(n);
const uret = (t) => { let x = t;
  return () => { x = (1103515245 * x + 12345) % 2147483648; return x / 2147483648; }; };

// TS13: her kusur bir yolda ve bir derinlikte durur. Senaryo disi kalan iki sinif taniminca
// derindir (3-5); otekiler 1-5. Yol ve derinlik tohum 31417'den gelir.
const r0 = uret(31417);
const KUSUR = kusurlar(TOHUM, N).map((k) => ({ ...k, yol: 1 + Math.floor(r0() * YOL),
  derinlik: DERIN.has(k.sinif) ? 3 + Math.floor(r0() * 3) : 1 + Math.floor(r0() * 5) }));
const BETIKSIZ = KUSUR.filter((k) => BETIKLI.has(k.sinif) === false);

// Bir oturum p yolu d derinligine kadar gezer; bedeli p*d*BIRIM dakikadir. Kusur, yolu
// gezildiyse ve derinligi d'yi asmiyorsa bulunur; bulma ani o yolun sirasindan gelir.
const gez = (r, p) => { const y = Array.from({ length: YOL }, (_, j) => j + 1);
  for (let j = YOL - 1; j > 0; j -= 1) { const q = Math.floor(r() * (j + 1));
    [y[j], y[q]] = [y[q], y[j]]; }
  return y.slice(0, p); };

// Ufuk: ADAY surum, surum basina PAY kusur ve bir oturum. Bulunan sinif bir sonraki surumden
// baslayarak betige cevrilir (TS14) ve o siniftan sonra kusur kacmaz.
function ufuk(p, d, tohum, butce = OTURUM) {
  const r = uret(tohum), yolSayisi = Math.min(p, Math.floor(butce / (BIRIM * d))), o = [];
  for (let c = 0; c < CIZIM; c += 1) {
    const cevrik = new Map(); let bulunan = 0, an = 0, kacan = [];
    for (let s = 1; s <= ADAY; s += 1) {
      const yer = new Map(gez(r, yolSayisi).map((y, i) => [y, (i + 1) * d * BIRIM]));
      const yeni = [];
      for (const k of KUSUR.slice((s - 1) * PAY, s * PAY)) {
        if (BETIKLI.has(k.sinif) || cevrik.has(k.sinif)) continue;
        if (yer.has(k.yol) && k.derinlik <= d) { bulunan += 1; an += yer.get(k.yol); yeni.push(k.sinif); }
        else kacan.push(k);
      }
      for (const x of yeni) if (cevrik.has(x) === false) cevrik.set(x, s);
    }
    const bedel = [...cevrik].reduce((a, [x, s]) => a + (ADAY - s) *
      (VARTEST.has(x) ? VARTEST.get(x).dk : 5) + (VARTEST.has(x) ? 0 : 45), 0);
    o.push({ bulunan, an: an / (bulunan || 1), cevrik, bedel,
      testsiz: [...cevrik.keys()].filter((x) => DERIN.has(x)).length,
      disi: [...cevrik.keys()].filter((x) => VARTEST.has(x)).length,
      kacan: kacan.length, risk: kacan.reduce((a, k) => a + k.olasilik * k.etki, 0),
      duz: kacan.reduce((a, k) => a + k.duzeltme, 0) });
  }
  const ort = (f) => o.reduce((a, x) => a + f(x), 0) / CIZIM;
  return { p: yolSayisi, d, butce, bulunan: ort((x) => x.bulunan), an: ort((x) => x.an),
    testsiz: ort((x) => x.testsiz), disi: ort((x) => x.disi), kacan: ort((x) => x.kacan),
    risk: ort((x) => x.risk), bedel: ort((x) => x.bedel), duz: ort((x) => x.duz) };
}
const TABANDUZ = BETIKSIZ.reduce((a, k) => a + k.duzeltme, 0);
const net = (o) => KAT * (TABANDUZ - o.duz) - ADAY * o.butce - o.bedel;

console.log(`kusur kumesi tohum ${TOHUM}, ${N} kusur; yol ve derinlik tohum 31417, ${YOL} yol`);
console.log(`betikli kume ikinci dersin dokuz kalemi -> ${BETIKLI.size} sinif kapali; ` +
  `ufukta ${BETIKSIZ.length} betiksiz kusur, ${BETIKSIZ.filter((k) => DERIN.has(k.sinif)).length}'i testsiz iki sinifta`);
console.log("derinlik dagilimi (kusur sayisi): " + [1, 2, 3, 4, 5]
  .map((d) => d + ":" + KUSUR.filter((k) => k.derinlik === d).length).join("  "));

console.log(`\noturum butcesi TS11 = ${OTURUM} dk = ${OTURUM / BIRIM} yol-derinlik birimi; ` +
  `${ADAY} surum, ${CIZIM} cizim ortalamasi`);
console.log("kapsam                  yol  derinlik   bulunan   butce disi   testsiz   donusum   bulma ani");
const D = [[20, 1], [10, 2], [5, 4], [4, 5]].map(([p, d], i) => ufuk(p, d, 5501 + i));
D.forEach((o, i) => console.log(`E${i + 1} ${o.p} yol x derinlik ${o.d}`.padEnd(24) +
  g(o.p, 3) + g(o.d, 10) + g(v(o.bulunan, 2), 10) + g(v(o.disi, 2), 13) + g(v(o.testsiz, 2), 10) +
  g(v((o.disi + o.testsiz) / o.bulunan, 2), 10) + g(v(o.an), 12)));

console.log("\nufkun sonu — ikinci dersin yogunluk sirasi tek basina 18 kusur kaciriyordu");
console.log("kapsam   kacan hata   kacan risk   oturum dk   donusum dk   kacan hatadan kazanc   net dk");
D.forEach((o, i) => console.log(`E${i + 1}`.padEnd(9) + g(v(o.kacan, 2), 10) + g(v(o.risk), 13) +
  g(ADAY * o.butce, 12) + g(v(o.bedel, 0), 13) + g(v(KAT * (TABANDUZ - o.duz), 0), 23) + g(v(net(o), 0), 9)));
console.log(`oturumsuz taban: 18 kacan, 155 risk, duzeltme ${TABANDUZ} dk (TS5 ile ${KAT * TABANDUZ} dk)`);

console.log("\nTS11'in duyarliligi — E4'un derinligi (5) uc butcede; 120 dk satiri E4'un ta kendisi");
console.log("butce dk   gezilen yol   bulunan   testsiz   kacan hata   net dk");
for (const b of [60, 120, 240]) { const o = ufuk(YOL, 5, 5504, b);
  console.log(g(b, 8) + g(o.p, 14) + g(v(o.bulunan, 2), 10) + g(v(o.testsiz, 2), 10) +
    g(v(o.kacan, 2), 13) + g(v(net(o), 0), 9)); }
```

```
kusur kumesi tohum 20260731, 60 kusur; yol ve derinlik tohum 31417, 20 yol
betikli kume ikinci dersin dokuz kalemi -> 9 sinif kapali; ufukta 18 betiksiz kusur, 10'i testsiz iki sinifta
derinlik dagilimi (kusur sayisi): 1:18  2:10  3:13  4:8  5:11

oturum butcesi TS11 = 120 dk = 20 yol-derinlik birimi; 20 surum, 200 cizim ortalamasi
kapsam                  yol  derinlik   bulunan   butce disi   testsiz   donusum   bulma ani
E1 20 yol x derinlik 1   20         1      3,00         3,00      0,00      1,00        64,4
E2 10 yol x derinlik 2   10         2      2,04         2,04      0,00      1,00        63,0
E3 5 yol x derinlik 4     5         4      2,53         1,35      1,12      0,98        73,3
E4 4 yol x derinlik 5     4         5      2,75         1,29      1,42      0,98        74,8

ufkun sonu — ikinci dersin yogunluk sirasi tek basina 18 kusur kaciriyordu
kapsam   kacan hata   kacan risk   oturum dk   donusum dk   kacan hatadan kazanc   net dk
E1            12,00        114,0        2400         1281                   2610    -1071
E2            13,85        126,5        2400          867                   1881    -1386
E3            11,32        104,1        2400          729                   2831     -298
E4            10,39         95,2        2400          735                   3280      145
oturumsuz taban: 18 kacan, 155 risk, duzeltme 1260 dk (TS5 ile 7560 dk)

TS11'in duyarliligi — E4'un derinligi (5) uc butcede; 120 dk satiri E4'un ta kendisi
butce dk   gezilen yol   bulunan   testsiz   kacan hata   net dk
      60             2      1,57      0,90        13,81      202
     120             4      2,75      1,42        10,39      145
     240             8      4,09      1,79         6,27     -923
```

#### Genişliğin Göremediği Sınıf

E1 yirmi yolun hepsini gezer ve bulabileceği her şeyi bulur: derinlik 1'de duran üç betiksiz
kusur. Bu üçü tam sayıdır, çizimden çizime değişmez — tam genişlikte rastlantı kalmaz. Buna
karşılık E1'in `testsiz` sütunu **0,00**'dır ve ne kadar çizim yapılırsa yapılsın 0,00 kalır.
Nedeni bir olasılık değil bir kısıttır: senaryo dışı kalan iki sınıfın kusurları 3'ten sığ bir
derinlikte durmaz, çünkü sığ olsalardı yazılı senaryolar zaten oradan geçerdi.

E4 aynı bütçeyle yalnız dört yola bakar ve daha az kusur bulur (2,75'e karşı 3,00) — ama
bulduklarının 1,42'si testsiz sınıftandır. **Genişlik daha çok kusur bulur, derinlik başka
türden kusur bulur.** İki dağıtım arasındaki fark bir verimlilik farkı değil, bir erişim
farkıdır; E1'in eksiği daha az çalışması değil, bakmadığı bir yerin olmasıdır.

Bulma anı bu erişimin bedelini gösterir: 63,0 ile 74,8 dakika arasında. Önceki dersin yoğunluk
sırası kusuru ortalama 18,2 dakikada haber veriyordu; keşifsel oturum dört katına yakın bir
gecikmeyle haber verir ve gecikme derinlikle birlikte artar, çünkü derin bir yolun sonuna
varmak zaman alır.

#### Dönüşüm Oranı ve İki Tür Bulgu

Bulunan kusurun betikli kümeye dönüşme oranı dört dağıtımda da 0,98 ile 1,00 arasındadır:
oturumun bulduğu hemen her kusur yeni bir betikli teste yol açar. Bu, göründüğü kadar iyi bir
haber değildir, çünkü dönüşenler iki türdür.

**Bütçe dışı sınıf**, envanterde testi olan ama 120 dakikaya sığmadığı için plandan düşmüş
sınıftır — E1'in bulduğu üç sınıfın üçü de budur. Böyle bir bulgu yeni bilgi getirmez; birinci
dersin bütçe kararını tekrar sorar ve dönüşümü de o kararın bedelini geri getirir: E1'in
dönüşüm bedeli 1.281 dakikadır, çünkü çevrilen testler kalan her sürümde koşar. **Testsiz
sınıf** ise envanterde karşılığı olmayan sınıftır; dönüşümü 45 dakika yazım artı sürüm başına
5 dakikadır ve E4'ün 735 dakikalık dönüşüm bedelinin küçük parçasıdır.

Ayrım kararı değiştirir. Bütçe dışı bir sınıfın bulunması, keşifsel oturumun değil planın
sonucudur ve cevabı bir oturum değil bir bütçe tartışmasıdır. Testsiz bir sınıfın bulunması
ise oturumun kendi ürünüdür ve karşılığında kalıcı bir test bırakır.

#### Ufkun Sonundaki Sayı

Yirmi sürümün sonunda önceki dersin sırası tek başına 18 kusur kaçırıyordu, 155 risk puanıyla.
Keşifsel oturum bu sayıyı her dağıtımda düşürür: E2 13,85'e, E1 12,00'a, E3 11,32'ye, E4
10,39'a. Kaçan risk 155'ten 95,2'ye iner — yani oturum yalnız daha az kusur kaçırmakla kalmaz,
kaçırdıkları da daha hafiftir, çünkü testsiz iki sınıf kaçanların ağır ucundaydı.

Net sütunu bunu bir karara bağlar. Yirmi sürümlük oturum bedeli 2.400 dakikadır ve yalnız E4
artıya geçer: 145 dakika. E1 kaçan hatayı 12'ye indirir ama 1.071 dakika zarar eder, çünkü
bulduğu her şey pahalı bir testin yeniden bütçeye alınmasını gerektirir. **Bir dağıtımın
kaçan hatayı azaltması onu haklı çıkarmaz; azalttığı kadarının bedeli de sayılır.**

TS11 taraması bunun sınırını verir. Bütçe 60 dakikaya indiğinde kaçan hata 13,81'e çıkar ama
net 202'ye yükselir; 240 dakikaya çıkarıldığında kaçan hata 6,27'ye iner ve net −923 olur.
Kaçan hata bütçeyle birlikte düşmeye devam eder, net etmez. Sıfır kaçan hata isteyen bir plan
bütçeyi tüketir; bu kursun her dersinde tekrarlanan takasın buradaki biçimi budur.

Kırmızı sonucun karşılığı da farklıdır. Bir keşifsel oturum yeşil ya da kırmızı vermez, bir
bulgu listesi verir; listedeki her satır ya bir kayıt açar ya bir test doğurur. Sürümü
durdurma yetkisi oturumda değildir — oturumun ürünü, sonraki sürümlerde sürümü durdurabilecek
bir betikli testtir.

#### Özet

- Oturum kapsamı bir bütçedir: genişlik ile derinliğin çarpımı sabittir (TS11–TS12), biri
  ötekinin karşılığıdır.
- Tam genişlik (E1) derinlik 1'deki üç kusuru kesin bulur ama testsiz iki sınıfı hiç göremez;
  bu bir olasılık değil, sınıfın tanımından gelen bir kısıttır (TS13).
- Derin kapsam (E4) daha az kusur bulur (2,75'e karşı 3,00) ama bulduklarının 1,42'si testsiz
  sınıftandır; fark verimlilik değil erişim farkıdır.
- Dönüşüm oranı 0,98–1,00'dır, ama dönüşenlerin çoğu bütçe dışı sınıftır: yeni bilgi değil,
  bütçe kararının yeniden sorulmasıdır.
- Yirmi sürümde kaçan hata 18'den 10,39'a, kaçan risk 155'ten 95,2'ye iner; 2.400 dakikalık
  oturum bedeliyle yalnız derin kapsam artıya geçer (145 dakika).
- Bütçe 240 dakikaya çıkarıldığında kaçan hata 6,27'ye iner ve net −923 olur: kaçan hata
  bütçeyle düşmeye devam eder, net etmez.

#### Sonraki Adım

Buraya kadar üç ders sınamanın kendisini dağıttı: hangi test, hangi sırayla, hangi kapsamda.
Üçünün de ortak bir sessiz varsayımı var — bir kusurun kusur olduğu, bir işin bittiğinin
belli olduğu. Oysa keşifsel oturumun `anlam-sapmasi` sınıfındaki bulguları tam bu noktada
tartışmaya açılır: sınayan bunu bir kusur sayar, geliştiren beklenen davranış sayar, ürün
tarafı ikisine de katılmaz. Kaçan hata sayısı bu durumda bir ölçüm değil bir anlaşmazlıktır.
Sonraki ders tamamlanma tanımını ve kabul ölçütünü ele alır: ölçütü kimin yazdığı, ne zaman
kapandığı ve belirsiz bırakılan bir ölçütün kaç birim yeniden açılan iş ürettiği.

### Kabul Ölçütleri

Source: https://academia.sh/tr/kurslar/test-sureci/kabul-olcutleri

Tamamlanma tanımının bir veri yapısı olarak yazılması: kabul ölçütünün özne, ölçü ve sınır yuvaları, yuvayı kimin ne zaman doldurduğuna göre değişen geri dönüş sayısı ve keşif aşaması, ürün tarafının dikkatinin sınırlı bir bütçe olması ve kapanışta yazılan ölçütün gözlenen davranışı onaylaması.

Önceki dersin keşifsel oturumu `anlam-sapmasi` sınıfında bulgular üretti, ama bu bulguların
kusur olup olmadığı bir ölçüm sorusu değildi: sınayan kusur sayar, geliştiren beklenen davranış
sayar, ürün tarafı ikisine de katılmaz. Böyle bir durumda kaçan hata sayısı bir ölçü değil bir
anlaşmazlıktır.

Anlaşmazlığı ölçüye çeviren şey **kabul ölçütüdür** (acceptance criteria): bir iş kaleminin ne
zaman bittiğini söyleyen, önceden yazılmış koşul. Kabul ölçütünün otomatik teste çevrilmesi
Birim Testi ve Test Odaklı Geliştirme kursunda kurulmuştu; buradaki soru başkadır — ölçütü
**kim** yazar ve **ne zaman** kapanır. Bu ders o iki soruyu bir bütçe dağıtımı olarak kurar;
dağıtılan kaynak ürün tarafının dikkatidir ve ölçüsü **geri dönüş**, yani yeniden açılan iştir.
(Dayanıklılık ve Güvenilirlik kursundaki `geri dönüş` bir devralmadan sonra asıl düğüme
dönmektir; burada
kastedilen, kapanmış sayılan bir işin yeniden açılmasıdır.)

#### Ölçütün Üç Yuvası

Bir kabul ölçütü üç yuva doldurur: **özne** neyin gözleneceğini, **ölçü** nasıl gözleneceğini,
**sınır** hangi değerin kabul edildiğini söyler. Bir **tamamlanma tanımı** (definition of done)
bu ölçütlerin tümünün karşılandığı durumdur. Üç yuvanın hepsi her ölçüt için gerekmez ve
gerekliliği ölçütün türünden gelir: varlık ölçütü özne ile ölçü ister ("gecikme bildirimi
gönderilir"), sayısal ölçüt sınırı da ister ("iade tarihinden beş gün sonra"), anlamsal ölçüt
sınırı ister **ve** o sınırın gözlemden türetilmiş olmamasını ister ("bildirim ödüncü alan
üyeye gider").

Son madde bu dersin en keskin noktasıdır. Bir ölçüt kapanışta, sistemin gözlenen davranışına
bakılarak yazılırsa sınır o davranışa eşitlenir; ölçüt hiçbir zaman kırmızı vermez, çünkü
sınadığı şey kendi kaynağıdır.

**TS15 (varsayım) — 20 iş kalemi, kalem başına 4 kabul ölçütü, toplam 80 ölçüt; tür dağılımı
tohum 6060'tan gelir.** Kusur kümesinin 60 kusuru kalemlere üçer üçer dağılır.
**TS16 (varsayım) — ürün tarafının ölçüt yazımına ayırabildiği süre ufuk boyunca 300
dakikadır; bir ölçüte bakması 12 dakika sürer.** Bu, dağıtılan kaynaktır ve duyarlılığı
taranır.
**TS17 (varsayım) — bir geri dönüşün bedeli açığa çıktığı aşamaya bağlıdır:** yazım anında 5,
geliştirme sırasında 20, kabul incelemesinde 60, sürümden sonra 180 dakika.

```js
// kutuphane/kusurlar.mjs — birinci dersin kusur kumesi, aynen (tohum 20260731, 60 kusur,
// 14 sinif). Bu ders envanterin bedel sutunlarini kullanmaz, yalniz kusur kumesini kullanir.
const AGIRLIK = [["kural-siniri", 9], ["sema-uyusmazligi", 6], ["goc-veri-kaybi", 4],
  ["sozlesme-alani", 5], ["arayuz-durumu", 8], ["gorsel-sapma", 3], ["veri-buyumesi", 4],
  ["sql-birlestirme", 3], ["bilinen-acik", 3], ["erisilebilirlik", 4], ["bozulmus-yanit", 3],
  ["kurtarma-eksigi", 2], ["senaryo-disi", 5], ["anlam-sapmasi", 4]];
export function kusurlar(tohum, n) {
  let x = tohum;
  const rast = () => { x = (1103515245 * x + 12345) % 2147483648; return x / 2147483648; };
  const toplam = AGIRLIK.reduce((a, [, w]) => a + w, 0);
  const liste = [];
  for (let i = 1; i <= n; i += 1) {
    let p = rast() * toplam, sinif = AGIRLIK[AGIRLIK.length - 1][0];
    for (const [s, w] of AGIRLIK) { p -= w; if (p < 0) { sinif = s; break; } }
    liste.push({ no: i, sinif, olasilik: 1 + Math.floor(rast() * 5),
      etki: 1 + Math.floor(rast() * 5), duzeltme: 15 + 15 * Math.floor(rast() * 8) });
  }
  return liste;
}
```

#### Beş Politika

Beş politika ölçütü farklı ellere ve farklı anlara dağıtır. Eksik kalan bir yuva, onu
dolduracak tarafın ölçüte ilk baktığı aşamada açığa çıkar: eksik sınırı ürün tarafı,
eksik ölçüyü sınayan doldurur.

```js
// kutuphane/olcut.mjs — kabul olcutunu uc yuvali bir veri yapisi olarak kurar ve olcutu
// kimin ne zaman yazdiginin geri donusunu olcer. Girdi: kutuphane/kusurlar.mjs
import { kusurlar } from "./kusurlar.mjs";

const IS = 20, BASINA = 4, TOHUM = 20260731, N = 60, URUNDK = 12, URUNBUTCE = 300;
const BEDEL = [5, 20, 60, 180];        // TS17: geri donusun asama basina dakikasi
const ASAMA = ["yazim ani", "gelistirme", "kabul incelemesi", "surumden sonra"];
let KAT = 6;                           // TS5: kacan hatanin duzeltme kati
const g = (x, n) => String(x).padStart(n);

// TS15: 20 is kalemi x 4 kabul olcutu = 80 olcut. Olcut turu gereken yuvalari belirler:
// varlik (ozne+olcu), sayisal (ozne+olcu+sinir), anlamsal (ucu de; sinir gozlemden turetilemez).
let x = 6060;
const r = () => { x = (1103515245 * x + 12345) % 2147483648; return x / 2147483648; };
const TUR = ["varlik", "sayisal", "anlamsal"];
const OLCUTLER = Array.from({ length: IS * BASINA }, (_, i) => { const p = r();
  return { no: i, kalem: Math.floor(i / BASINA), tur: TUR[p < 0.45 ? 0 : p < 0.8 ? 1 : 2] }; });
const GEREK = { varlik: ["ozne", "olcu"], sayisal: ["ozne", "olcu", "sinir"],
  anlamsal: ["ozne", "olcu", "sinir"] };

// Kusurlar kalemlere ucer ucer dagilir. Anlam sapmasi kusuru taniminca anlamsal bir olcutun
// altinda durur — onu kusur yapan sey koddaki bir kural degil, olcutun ne dedigidir; kalemde
// anlamsal olcut yoksa kalemin bir olcutu anlamsal olarak isaretlenir.
const KUSUR = kusurlar(TOHUM, N).map((k, i) => ({ ...k, kalem: Math.floor(i / (N / IS)) }));
const ANLAM = KUSUR.filter((k) => k.sinif === "anlam-sapmasi");
for (const k of ANLAM) {
  const kalemin = OLCUTLER.filter((o) => o.kalem === k.kalem);
  k.olcut = kalemin.find((o) => o.tur === "anlamsal")
    || Object.assign(kalemin[Math.floor(r() * kalemin.length)], { tur: "anlamsal" });
}

// Politika: her olcutte hangi yuvalarin doldugu, sinir yuvasini dolduran urun mu, sinirin
// gozlemden turetilip turetilmedigi ve eksigi kapatacak tarafin olcutu ilk okudugu asama.
function olc(ad, yuva, urunMu, gozlemden, urunOkuma, sinayanOkuma, yazim) {
  const durum = OLCUTLER.map((o) => {
    const dolu = yuva(o);
    const eksik = GEREK[o.tur].filter((y) => dolu.includes(y) === false);
    const yargilar = eksik.length === 0 && (o.tur !== "anlamsal" || gozlemden === false);
    return { ...o, yargilar, sinirli: dolu.includes("sinir"),
      asama: eksik.length ? urunOkuma : yargilar ? -1 : sinayanOkuma };
  });
  const acik = durum.filter((o) => o.yargilar === false);
  const kacan = ANLAM.filter((k) => durum[k.olcut.no].yargilar === false);
  const urunSaat = urunMu ? URUNDK * durum.filter((o) => o.sinirli).length : 0;
  return { ad, urunSaat, yazimDk: yazim * OLCUTLER.length + urunSaat,
    geri: acik.length, kalem: new Set(acik.map((o) => o.kalem)).size,
    donusDk: acik.reduce((a, o) => a + BEDEL[o.asama], 0),
    dagilim: [0, 1, 2, 3].map((s) => acik.filter((o) => o.asama === s).length),
    kacan: kacan.length, risk: kacan.reduce((a, k) => a + k.olasilik * k.etki, 0),
    duz: KAT * kacan.reduce((a, k) => a + k.duzeltme, 0) };
}
const IKI = ["ozne", "olcu"], UC = ["ozne", "olcu", "sinir"];
const P = () => [olc("Y1 gelistiren yazar, urun bakmaz", () => IKI, false, false, 2, 2, 5),
  olc("Y2 urun her olcute bakar, basta", () => UC, true, false, 0, 1, 5),
  olc("Y3 sinayan kapanista yazar", () => UC, false, true, 3, 2, 8),
  olc("Y4 olcut yok, tek satirlik tanim", () => [], false, false, 3, 3, 1),
  olc("Y5 urun yalniz anlamsal olcute bakar", (o) => (o.tur === "anlamsal" ? UC : IKI),
    true, false, 2, 2, 5)];
const toplam = (o) => o.yazimDk + o.donusDk + o.duz;
const sigan = (t) => { const u = P().map((o, i) => [t[i], o.urunSaat <= URUNBUTCE, i])
  .filter(([, s]) => s); return "Y" + (u.sort((a, b) => a[0] - b[0])[0][2] + 1); };

console.log(`${IS} is kalemi x ${BASINA} kabul olcutu = ${OLCUTLER.length} olcut (tohum 6060); tur: ` +
  TUR.map((t) => `${t} ${OLCUTLER.filter((o) => o.tur === t).length}`).join(", "));
console.log(`kusur kumesi tohum ${TOHUM}, ${N} kusur; ${ANLAM.length} anlam sapmasi, ` +
  `${ANLAM.reduce((a, k) => a + k.olasilik * k.etki, 0)} risk puani; ` +
  `${new Set(ANLAM.map((k) => k.kalem)).size} ayri is kaleminde`);
console.log(`TS16 urun butcesi ${URUNBUTCE} dk (olcut basina ${URUNDK} dk); ` +
  `TS17 geri donus: ` + ASAMA.map((a, i) => `${a} ${BEDEL[i]} dk`).join(", "));

console.log("\npolitika                               urun dk  butce  geri donus  kalem  kesif asamasi");
for (const o of P()) console.log(o.ad.padEnd(38) + g(o.urunSaat, 7) +
  g(o.urunSaat <= URUNBUTCE ? "sigar" : "asiyor", 7) + g(o.geri, 12) + g(o.kalem, 7) +
  "  " + (o.geri ? ASAMA[o.dagilim.indexOf(Math.max(...o.dagilim))] : "-"));

console.log("\nayni kusur kumesinde bedel");
console.log("politika                                yazim dk   donus dk   kacan  kacan risk   duzeltme dk   toplam dk");
for (const o of P()) console.log(o.ad.padEnd(38) + g(o.yazimDk, 9) + g(o.donusDk, 11) +
  g(o.kacan, 8) + g(o.risk, 12) + g(o.duz, 14) + g(toplam(o), 12));

console.log("\nurun butcesi TS16'nin duyarliligi — urun once anlamsal, sonra sayisal olcute bakar");
console.log("urun dk   bakilan olcut   geri donus   kacan   toplam dk");
const sira = [...OLCUTLER].sort((a, b) => TUR.indexOf(b.tur) - TUR.indexOf(a.tur));
for (const b of [0, 204, 300, 960]) {
  const kume = new Set(sira.slice(0, Math.floor(b / URUNDK)).map((o) => o.no));
  const o = olc("", (t) => (kume.has(t.no) ? UC : IKI), true, false, 2, 2, 5);
  console.log(g(b, 7) + g(kume.size, 16) + g(o.geri, 13) + g(o.kacan, 8) + g(toplam(o), 12));
}

console.log("\nTS5'in duyarliligi: kacan hatanin kati degisirse hangi politika en ucuz");
console.log("kat   " + ["Y1", "Y2", "Y3", "Y4", "Y5"].map((a) => g(a, 8)).join("") +
  "   en ucuz   butceye sigan en ucuz");
for (const kat of [1, 6, 20]) {
  KAT = kat;
  const t = P().map(toplam);
  console.log(g(kat, 3) + t.map((n) => g(n, 8)).join("") +
    g("Y" + (t.indexOf(Math.min(...t)) + 1), 10) + g(sigan(t), 22));
}
```

```
20 is kalemi x 4 kabul olcutu = 80 olcut (tohum 6060); tur: varlik 34, sayisal 29, anlamsal 17
kusur kumesi tohum 20260731, 60 kusur; 6 anlam sapmasi, 60 risk puani; 5 ayri is kaleminde
TS16 urun butcesi 300 dk (olcut basina 12 dk); TS17 geri donus: yazim ani 5 dk, gelistirme 20 dk, kabul incelemesi 60 dk, surumden sonra 180 dk

politika                               urun dk  butce  geri donus  kalem  kesif asamasi
Y1 gelistiren yazar, urun bakmaz            0  sigar          46     20  kabul incelemesi
Y2 urun her olcute bakar, basta           960 asiyor           0      0  -
Y3 sinayan kapanista yazar                  0  sigar          17     14  kabul incelemesi
Y4 olcut yok, tek satirlik tanim            0  sigar          80     20  surumden sonra
Y5 urun yalniz anlamsal olcute bakar      204  sigar          29     19  kabul incelemesi

ayni kusur kumesinde bedel
politika                                yazim dk   donus dk   kacan  kacan risk   duzeltme dk   toplam dk
Y1 gelistiren yazar, urun bakmaz            400       2760       6          60          2970        6130
Y2 urun her olcute bakar, basta            1360          0       0           0             0        1360
Y3 sinayan kapanista yazar                  640       1020       6          60          2970        4630
Y4 olcut yok, tek satirlik tanim             80      14400       6          60          2970       17450
Y5 urun yalniz anlamsal olcute bakar        604       1740       0           0             0        2344

urun butcesi TS16'nin duyarliligi — urun once anlamsal, sonra sayisal olcute bakar
urun dk   bakilan olcut   geri donus   kacan   toplam dk
      0               0           46       6        6130
    204              17           29       0        2344
    300              25           21       0        1960
    960              80            0       0        1360

TS5'in duyarliligi: kacan hatanin kati degisirse hangi politika en ucuz
kat         Y1      Y2      Y3      Y4      Y5   en ucuz   butceye sigan en ucuz
  1    3655    1360    2155   14975    2344        Y2                    Y3
  6    6130    1360    4630   17450    2344        Y2                    Y5
 20   13060    1360   11560   24380    2344        Y2                    Y5
```

#### Kâğıt Üzerindeki En Ucuz Politika Bütçede Yok

Y2 her ölçütü ürün tarafıyla birlikte yazar ve tabloda hiçbir geri dönüş üretmez: 1.360
dakikayla beş politikanın en ucuzudur. Ama ürün sütunu 960 dakika der, TS16'nın bütçesi 300 —
politika **uygulanamaz.** Beş satırın en iyisi bir seçenek değildir; bir üst sınırdır.

Bütçeye sığanlar arasında Y5 öne çıkar: ürün tarafı yalnız on yedi anlamsal ölçüte bakar, 204
dakika harcar ve kaçan hatayı sıfıra indirir. Y1 aynı bütçeyi hiç kullanmaz ve altı kusuru
sürüme geçirir; toplam bedeli 6.130 dakika, Y5'in 2,6 katı. Aradaki fark 204 dakikalık bir
dikkatin nereye konduğudur.

Ürün bütçesi taraması bu dağıtımın biçimini verir. İlk 204 dakika kaçan hatanın **tamamını**
kaldırır (6 → 0), çünkü o dakikalar anlamsal ölçütlere gider. Kalan 756 dakika bir kusur daha
kurtarmaz; yalnız geri dönüşü 29'dan 0'a indirir ve toplamı 2.344'ten 1.360'a çeker. Aynı
kaynağın ilk parçası ile son parçası farklı şeyler satın alır: ilk parça kaçan hatayı, son
parça geri dönüşü.

#### Kapanışta Yazılan Ölçüt

Y3 tabloda makul görünür: ürün bütçesi harcamaz, yalnız on yedi geri dönüş üretir, 4.630
dakikayla Y1'den ucuzdur. Yine de altı kusuru sürüme geçirir. Nedeni ölçütün eksik olması
değil — üç yuvası da doludur. Nedeni sınırın **gözlemden türetilmiş** olmasıdır: sınayan
kapanışta, çalışan sistemi izleyerek ölçüt yazar ve sistemin yaptığını kabul edilebilir olarak
kaydeder. Ölçüt kırmızı vermez, çünkü sınadığı davranışın kendisinden türemiştir. **Yazılmış
ölçüt sayısı bir kalite göstergesi değildir; ölçütün kaynağı göstergedir.**

Y4 ölçütü hiç yazmaz ve seksen ölçütün seksenini sürümden sonra tartışmaya bırakır: 14.400
dakikalık geri dönüş, yirmi kalemin yirmisi yeniden açılır. Keşif aşaması sütunu bu politikayı
ötekilerden ayıran tek şeydir — sayı değil, an. Aynı anlaşmazlık kabul incelemesinde 60,
sürümden sonra 180 dakikaya mal olur.

#### Kararın Geri Dönüşü

Bu derste kararın geri dönüşü doğrudan ölçülen niceliktir. Y1 yirmi kalemin yirmisini yeniden
açar, Y5 on dokuzunu, Y3 on dördünü, Y2 hiçbirini. Yeniden açılan kalem sayısı politikalar
arasında az değişir; değişen, açılmanın **hangi aşamada** olduğu ve kaç kusurun sürüme
geçtiğidir.

TS5 taraması kararı bir bağlama bağlar. Kaçan hatanın düzeltmesi yakalanmışla eşitse (kat 1)
bütçeye sığan en ucuz politika Y3'tür — kapanışta yazılan ölçüt, kusur ucuzken savunulabilir.
Kat 6 ve 20'de Y5 kazanır ve arayı açar. Yani ölçütü kimin yazacağı sorusunun cevabı, kaçan
bir kusurun ekibe neye mal olduğuna bağlıdır; o sayı bilinmeden politika seçilemez.

#### Özet

- Kabul ölçütü üç yuvadır: özne, ölçü, sınır. Ölçütün türü hangi yuvaların gerekli olduğunu
  belirler; anlamsal ölçüt ayrıca sınırın gözlemden türetilmemiş olmasını ister.
- Ürün tarafının dikkati sınırlı bir kaynaktır (TS16, 300 dakika); her ölçüte bakan politika
  960 dakika ister ve bütçeye sığmaz — tablodaki en ucuz satır bir seçenek değil, üst sınırdır.
- Aynı bütçenin ilk 204 dakikası kaçan hatanın tamamını kaldırır, kalan 756 dakika yalnız geri
  dönüşü azaltır; bir kaynağın ilk ve son parçası farklı şeyler satın alır.
- Kapanışta yazılan ölçüt üç yuvasını doldurur ama sınırı gözlenen davranıştan aldığı için
  kırmızı vermez ve altı kusuru sürüme geçirir.
- Ölçüt hiç yazılmadığında anlaşmazlık kaybolmaz, yalnız aşaması kayar: aynı geri dönüş kabul
  incelemesinde 60, sürümden sonra 180 dakikadır.
- Politika seçimi kaçan hatanın katına bağlıdır: kat 1'de kapanışta yazım, kat 6 ve 20'de
  hedefli ürün dikkati bütçeye sığan en ucuz seçenektir.

#### Sonraki Adım

Buradaki bütün ölçütleri ekibin içindeki taraflar yazdı: geliştiren, sınayan, ürün. Üçü de
sistemi tasarlayan tarafta duruyor ve üçü de aynı sözlüğü kullanıyor. Oysa ölçütün sonunda
karşılanması gereken beklenti ekibin dışındadır ve o beklenti hiçbir yuvaya yazılmadı —
kütüphane görevlisinin bir ödünç işlemini kaç adımda bitirmeyi beklediği, bir üyenin gecikme
bildirimini ne zaman anlamlı bulduğu. Sonraki ders bu doğrulamayı yönetir: kabul kapsamının
kullanıcı görevlerine nasıl dağıtılacağı, dönen geri bildirimin kusur ile istek ile yanlış
anlama arasında nasıl ayrılacağı ve her sınıfın ekibe neye mal olduğu.

### Kullanıcı Kabul Testi

Source: https://academia.sh/tr/kurslar/test-sureci/kullanici-kabul-testi

Son kullanıcı doğrulamasının bir dağıtım kararı olarak yönetilmesi: kabul kapsamının kullanıcı görevlerine dört ayrı bölünmesi, dönen geri bildirimin kusur ile istek ile yanlış anlama arasında ayrılması, her sınıfın dakikayla ölçülen bedeli ve geri bildirim hacminin neden bir kalite ölçüsü olmadığı.

Önceki dersin bütün ölçütlerini ekibin içindeki taraflar yazdı. Üçü de sistemi tasarlayan
tarafta duruyor ve aynı sözlüğü kullanıyor; oysa ölçütün sonunda karşılanması gereken beklenti
ekibin dışındadır. Kütüphane görevlisinin bir ödünç işlemini kaç adımda bitirmeyi beklediği ya
da bir üyenin gecikme bildirimini ne zaman anlamlı bulduğu hiçbir yuvaya yazılmadı.

**Kullanıcı kabul testi** (user acceptance testing) bu boşluğu son kullanıcıya sordurur. Bu
ders onu bir dağıtım kararı olarak kurar: dağıtılan kaynak kullanıcının zamanıdır, dağıtım
birimi **kullanıcı görevidir** ve dönen şey tek türden değildir. Geri bildirimin bir kısmı
kusurdur, bir kısmı yeni bir **istektir**, bir kısmı da sistemin doğru davranışını yanlış
okuyan bir **yanlış anlamadır** — ve üçünün bedeli farklıdır.

#### Kabul Aşamasına Ne Ulaşır

Kabul testi son kapıdır: ondan önceki kapıların gördüğü kusur buraya gelmez. İkinci dersin
bütçeye sığan dokuz kalemi dokuz sınıfı kapatıyordu; kalan beş sınıfın 18 kusuru bu aşamaya
ulaşır. Dördüncü dersin Y5 politikası da bir durum bırakmıştı: yirmi dokuz sayısal ölçütün
sınırı boştur.

Bu iki miras geri bildirimin sınıfını belirler. Bir kullanıcı bir ölçütü gözlediğinde sonuç
üçe ayrılır: **sınırı boş bir ölçütte** kullanıcı kendi sınırını söyler ve bu bir istektir;
**tam ve altında kusur olan bir ölçütte** bir kusur bildirir; **tam, kusursuz ama ölçüsü
kullanıcıya görünmeyen bir varlık ölçütünde** gördüğünü kendi beklentisiyle karşılaştırır ve
bir yanlış anlama üretir.

**TS18 (varsayım) — 20 iş kalemi 12 kullanıcı görevine dağılır, her göreve en az bir kalem
düşer; kullanım payı tohum 4712'den gelir.**
**TS19 (varsayım) — kabul bütçesi 600 kullanıcı dakikası, bir ölçütü gözlemek 8 dakika**;
bütçe böylece 75 ölçüt gözlemidir ve seksen ölçütün tamamına yetmez.
**TS20 (varsayım) — isteklerin %30'u kabul edilip işe dönüşür**, gerisi kayıtta kalır.
**TS21 (varsayım) — bir isteği karara bağlamak 30, kabul edilen isteği yapmak 120, bir yanlış
anlamayı gidermek 20 dakikadır; kusurun bedeli kusur kümesindeki düzeltme dakikasıdır.**

```js
// kutuphane/kusurlar.mjs — birinci dersin kusur kumesi, aynen (tohum 20260731, 60 kusur,
// 14 sinif). Bu ders envanterin bedel sutunlarini kullanmaz, yalniz kusur kumesini kullanir.
const AGIRLIK = [["kural-siniri", 9], ["sema-uyusmazligi", 6], ["goc-veri-kaybi", 4],
  ["sozlesme-alani", 5], ["arayuz-durumu", 8], ["gorsel-sapma", 3], ["veri-buyumesi", 4],
  ["sql-birlestirme", 3], ["bilinen-acik", 3], ["erisilebilirlik", 4], ["bozulmus-yanit", 3],
  ["kurtarma-eksigi", 2], ["senaryo-disi", 5], ["anlam-sapmasi", 4]];
export function kusurlar(tohum, n) {
  let x = tohum;
  const rast = () => { x = (1103515245 * x + 12345) % 2147483648; return x / 2147483648; };
  const toplam = AGIRLIK.reduce((a, [, w]) => a + w, 0);
  const liste = [];
  for (let i = 1; i <= n; i += 1) {
    let p = rast() * toplam, sinif = AGIRLIK[AGIRLIK.length - 1][0];
    for (const [s, w] of AGIRLIK) { p -= w; if (p < 0) { sinif = s; break; } }
    liste.push({ no: i, sinif, olasilik: 1 + Math.floor(rast() * 5),
      etki: 1 + Math.floor(rast() * 5), duzeltme: 15 + 15 * Math.floor(rast() * 8) });
  }
  return liste;
}
```

#### Dört Kapsam Dağıtımı

Aynı 600 dakika dört kurala göre bölünür: eşit (K1), kullanım payına göre (K2), ölçütü en çok
eksik olan göreve (K3) ve yalnız son dört yeni göreve (K4).

```js
// kutuphane/kabul.mjs — kabul kapsamini kullanici gorevlerine dagitir ve donen geri bildirimi
// kusur / istek / yanlis anlama diye ayirir. Girdi: kutuphane/kusurlar.mjs
import { kusurlar } from "./kusurlar.mjs";

const IS = 20, BASINA = 4, GOREV = 12, TOHUM = 20260731, N = 60, KAT = 6;
const BUTCE = 600, OLCUTDK = 8;          // TS19: kabul butcesi, olcut basina kullanici dakikasi
let KABUL = 0.3;                         // TS20: istegin ise donusme orani
const ISTEKDK = 30, YANLISDK = 20, YENIISDK = 120;   // TS21: sinif basina bedel
const v = (x, n = 1) => x.toFixed(n).replace(".", ",");
const g = (x, n) => String(x).padStart(n);
const uret = (t) => { let x = t;
  return () => { x = (1103515245 * x + 12345) % 2147483648; return x / 2147483648; }; };

// Dorduncu dersin olcut kumesi aynen: 80 olcut, tohum 6060, uc tur.
const r = uret(6060);
const TUR = ["varlik", "sayisal", "anlamsal"];
const OLCUTLER = Array.from({ length: IS * BASINA }, (_, i) => { const p = r();
  return { no: i, kalem: Math.floor(i / BASINA), tur: TUR[p < 0.45 ? 0 : p < 0.8 ? 1 : 2] }; });
// Kabul asamasina ulasan kusurlar: ikinci dersin butceye sigan dokuz kaleminin gormedigi
// bes sinif, yani o dersin 18 kacan kusuru. Betikli kumenin gordukleri buraya gelmez.
const ULASAN = new Set(["erisilebilirlik", "bozulmus-yanit", "kurtarma-eksigi",
  "senaryo-disi", "anlam-sapmasi"]);
const KUSUR = kusurlar(TOHUM, N).map((k, i) => ({ ...k, kalem: Math.floor(i / (N / IS)) }))
  .filter((k) => ULASAN.has(k.sinif));
for (const k of KUSUR.filter((k) => k.sinif === "anlam-sapmasi")) {
  const kalemin = OLCUTLER.filter((o) => o.kalem === k.kalem);
  if (kalemin.some((o) => o.tur === "anlamsal") === false)
    Object.assign(kalemin[Math.floor(r() * kalemin.length)], { tur: "anlamsal" });
}
// Dorduncu dersin Y5 politikasinin biraktigi durum: sayisal olcutlerin siniri bostur.
for (const o of OLCUTLER) o.tam = o.tur !== "sayisal";

// TS18: 20 is kalemi 12 kullanici gorevine dagilir (her goreve en az bir kalem);
// gorevin kullanim payi ayni tohumdan gelir.
const r2 = uret(4712);
const PAY = Array.from({ length: GOREV }, () => 1 + Math.floor(r2() * 9));
const PAYTOP = PAY.reduce((a, x) => a + x, 0);
const KALEMGOREV = Array.from({ length: IS }, (_, i) => i % GOREV);
for (let j = IS - 1; j > 0; j -= 1) { const q = Math.floor(r2() * (j + 1));
  [KALEMGOREV[j], KALEMGOREV[q]] = [KALEMGOREV[q], KALEMGOREV[j]]; }
const gorevi = (o) => KALEMGOREV[o.kalem];
// Her kusur kaleminden bir olcutun altinda durur; anlam sapmasi anlamsal olcutte.
const r3 = uret(9313);
for (const k of KUSUR) {
  const aday = OLCUTLER.filter((o) => o.kalem === k.kalem &&
    (k.sinif === "anlam-sapmasi" ? o.tur === "anlamsal" : true));
  k.olcut = aday[Math.floor(r3() * aday.length)];
}
const KUSURLU = new Set(KUSUR.map((k) => k.olcut.no));

// Bir gorevde m dakika: m/OLCUTDK olcut gozlenir. Geri bildirim olcutun durumundan cikar:
// siniri bos olcut -> istek (kullanici kendi sinirini soyler), kusurlu tam olcut -> kusur,
// kusursuz varlik olcutu -> yanlis anlama (olcusu kullaniciya gorunmez, kendi beklentisiyle
// karsilastirir), kusursuz anlamsal olcut -> geri bildirim yok.
function sina(dagitim) {
  const bulgu = { kusur: [], istek: 0, "yanlis anlama": 0 };
  let gozlenen = 0, an = 0, adet = 0;
  for (let go = 0; go < GOREV; go += 1) {
    const liste = OLCUTLER.filter((o) => gorevi(o) === go);
    const kac = Math.min(liste.length, Math.floor(dagitim[go] / OLCUTDK));
    gozlenen += kac;
    liste.slice(0, kac).forEach((o, i) => {
      const t = (i + 1) * OLCUTDK;
      if (o.tam === false) { bulgu.istek += 1; an += t; adet += 1; }
      else if (KUSURLU.has(o.no)) { const l = KUSUR.filter((k) => k.olcut.no === o.no);
        bulgu.kusur.push(...l); an += t * l.length; adet += l.length; }
      else if (o.tur === "varlik") { bulgu["yanlis anlama"] += 1; an += t; adet += 1; }
    });
  }
  const kacan = KUSUR.filter((k) => bulgu.kusur.includes(k) === false);
  // Bulunan kusurun duzeltmesi bir kez, kacanin duzeltmesi TS5 katiyla odenir.
  const o = { gozlenen, an: an / (adet || 1), kusur: bulgu.kusur.length, istek: bulgu.istek,
    yanlis: bulgu["yanlis anlama"], kacan: kacan.length,
    kacanRisk: kacan.reduce((a, k) => a + k.olasilik * k.etki, 0),
    kusurDk: bulgu.kusur.reduce((a, k) => a + k.duzeltme, 0),
    kacanDk: KAT * kacan.reduce((a, k) => a + k.duzeltme, 0),
    istekDk: ISTEKDK * bulgu.istek + KABUL * bulgu.istek * YENIISDK,
    yanlisDk: YANLISDK * bulgu["yanlis anlama"] };
  return { ...o, toplam: o.kusurDk + o.kacanDk + o.istekDk + o.yanlisDk };
}
const esit = () => Array.from({ length: GOREV }, () => BUTCE / GOREV);
const paya = () => PAY.map((p) => (BUTCE * p) / PAYTOP);
const eksige = () => { const e = Array.from({ length: GOREV }, (_, go) =>
  OLCUTLER.filter((o) => gorevi(o) === go && o.tam === false).length);
  const t = e.reduce((a, x) => a + x, 0); return e.map((x) => (BUTCE * x) / t); };
const yeni = () => Array.from({ length: GOREV }, (_, go) => (go >= GOREV - 4 ? BUTCE / 4 : 0));
const D = [["K1 esit dagitim", esit()], ["K2 kullanim payina gore", paya()],
  ["K3 en cok eksik olcute", eksige()], ["K4 yalniz dort yeni goreve", yeni()]];

console.log(`${OLCUTLER.length} olcut (tohum 6060) ${GOREV} kullanici gorevine dagildi (tohum 4712)`);
console.log("gorev basina olcut: " + Array.from({ length: GOREV }, (_, go) =>
  OLCUTLER.filter((o) => gorevi(o) === go).length).join(" ") +
  "; kullanim payi: " + PAY.map((p) => v((100 * p) / PAYTOP, 0) + "%").join(" "));
console.log(`dorduncu dersin Y5 durumu: ${OLCUTLER.filter((o) => o.tam === false).length} olcutun siniri bos; ` +
  `${KUSURLU.size} olcutun altinda kusur var`);

console.log(`\nkabul butcesi TS19 = ${BUTCE} kullanici dk, olcut basina ${OLCUTDK} dk = ${BUTCE / OLCUTDK} olcut`);
console.log("dagitim                   gozlenen  kusur  istek  yanlis anlama  kusur payi  bulgu ani");
for (const [ad, d] of D) { const o = sina(d);
  const t = o.kusur + o.istek + o.yanlis;
  console.log(ad.padEnd(26) + g(o.gozlenen, 8) + g(o.kusur, 7) + g(o.istek, 7) + g(o.yanlis, 15) +
    g(t ? v(o.kusur / t, 2) : "-", 12) + g(v(o.an), 11)); }

console.log("\nsiniflarin bedeli — istek 30 dk karar + %30'u 120 dk is, yanlis anlama 20 dk (TS21)");
console.log("dagitim                   kusur dk   istek dk   yanlis dk   kacan  kacan risk   kacan dk   toplam dk");
for (const [ad, d] of D) { const o = sina(d);
  console.log(ad.padEnd(26) + g(o.kusurDk, 8) + g(v(o.istekDk, 0), 11) + g(o.yanlisDk, 12) +
    g(o.kacan, 7) + g(o.kacanRisk, 12) + g(o.kacanDk, 11) + g(v(o.toplam, 0), 12)); }

console.log("\nTS20'nin duyarliligi: istegin kac katinin ise donustugu");
console.log("kabul orani   " + D.map(([ad]) => g(ad.slice(0, 2), 9)).join("") + "   en ucuz");
for (const p of [0, 0.3, 0.6]) {
  KABUL = p;
  const t = D.map(([, d]) => sina(d).toplam);
  console.log(g("%" + 100 * p, 11) + t.map((n) => g(v(n, 0), 9)).join("") +
    g(D[t.indexOf(Math.min(...t))][0].slice(0, 2), 10));
}
```

```
80 olcut (tohum 6060) 12 kullanici gorevine dagildi (tohum 4712)
gorev basina olcut: 8 8 8 8 8 8 8 8 4 4 4 4; kullanim payi: 4% 8% 10% 12% 3% 10% 10% 3% 12% 12% 12% 6%
dorduncu dersin Y5 durumu: 29 olcutun siniri bos; 15 olcutun altinda kusur var

kabul butcesi TS19 = 600 kullanici dk, olcut basina 8 dk = 75 olcut
dagitim                   gozlenen  kusur  istek  yanlis anlama  kusur payi  bulgu ani
K1 esit dagitim                 64      7     21             30        0,12       25,2
K2 kullanim payina gore         54      8     21             22        0,16       26,0
K3 en cok eksik olcute          60      6     23             24        0,11       28,7
K4 yalniz dort yeni goreve      16      6      4              8        0,33       19,6

siniflarin bedeli — istek 30 dk karar + %30'u 120 dk is, yanlis anlama 20 dk (TS21)
dagitim                   kusur dk   istek dk   yanlis dk   kacan  kacan risk   kacan dk   toplam dk
K1 esit dagitim                675       1386         600     11          88       3510        6171
K2 kullanim payina gore        720       1386         440     10          82       3240        5786
K3 en cok eksik olcute         555       1518         480     12         102       4230        6783
K4 yalniz dort yeni goreve     570        264         160     12          89       4140        5134

TS20'nin duyarliligi: istegin kac katinin ise donustugu
kabul orani          K1       K2       K3       K4   en ucuz
         %0     5415     5030     5955     4990        K4
        %30     6171     5786     6783     5134        K4
        %60     6927     6542     7611     5278        K4
```

#### Geri Bildirim Hacmi Bir Kalite Ölçüsü Değildir

Dört dağıtımın ürettiği geri bildirimin **kusur payı** 0,11 ile 0,33 arasındadır. Yani en iyi
durumda üç bildirimden biri, en kötü durumda dokuz bildirimden biri gerçek bir kusurdur;
gerisi istek ve yanlış anlamadır. K1 elli sekiz bildirim üretir ve bunun yalnız yedisi
kusurdur. Bir kabul turunu "elli sekiz bulgu" diye özetlemek, ekibe yedi kusurdan söz ettiğini
söylemez.

Sınıflandırma yapılmadan bu ayrım görünmez: üç sınıf aynı listeye düşer ve aynı biçimde
yazılır. Ayrımı ölçüte bağlamak onu bir yargı olmaktan çıkarır: sınırı boş bir ölçütte gelen
bildirim istektir, çünkü ortada karşılanmayan bir söz yoktur — kullanıcının sınırı ilk kez
orada söylenmektedir.

#### Hangi Dağıtım Ne Satın Alır

K3 en çok isteği getiren dağıtımdır (23). Şaşırtıcı değildir: eksik ölçütü hedeflemek, tanımı
gereği sınırı yazılmamış ölçütleri gözletir. Kaçan hatası da en
yüksektir (12) ve toplamı en pahalıdır (6.783 dakika). **Belirsizliği hedeflemek kusuru değil
isteği bulur.**

K2 kullanım payına göre dağıtır, en çok kusuru bulur (8) ve en az kusuru kaçırır (10, kaçan
risk 82). Kullanıcının çok kullandığı görevde duran kusur, sınandığında bulunur. Buna karşılık
K2 gözleyebildiği ölçüt sayısında en düşüktür (54), çünkü bütçenin büyük parçası az sayıda
göreve gider.

K4 en ucuz görünür (5.134 dakika) ve en yüksek kusur payına sahiptir (0,33). Nedeni bir
üstünlük değildir: yalnız on altı ölçüt gözlediği için gürültü de üretmez. İstek ve yanlış
anlama bedeli 424 dakikada kalır, ama on iki kusuru kaçırır. **Toplam bedel sütunu tek başına
yanıltıcıdır, çünkü geri bildirim üretmemek ucuzdur.** İki sütunu birlikte okumak gerekir:
K2 kaçan hatada, K4 işlenen bildirimde ucuzdur ve ikisi aynı şeyi ölçmez.

TS20 taraması bunu doğrular. İsteklerin hiçbiri işe dönüşmese bile (kabul oranı %0) sıralama
değişmez; %60'a çıkarıldığında K4'ün önü açılır, çünkü işlenecek isteği zaten azdır. Bir kabul
turunun bedelini asıl belirleyen, bulunan kusur sayısı değil **işlenen bildirim sayısıdır.**

#### Kararın Geri Dönüşü

Kabul testinin kırmızısı tek bir karar değildir, üç ayrı kuyruktur. Kusur sürümü durdurur ve
düzeltmeye gider. İstek ürün tarafının kararına gider ve %30'u işe dönüşür; kalanı bir kayıt
olarak durur. Yanlış anlama koda değil arayüz metnine ya da belgeye gider ve dördüncü dersin
ölçüt yuvalarına geri besleme yapar — bir yanlış anlama, ölçüsü kullanıcıya görünmeyen bir
ölçütün işaretidir.

Bulgunun ortalama anı 19,6 ile 28,7 dakika arasındadır, ama bu sayı yanıltıcı biçimde küçüktür:
kabul penceresi geliştirme bittikten **sonra** açılır. İkinci dersin betikli kümesi kusuru
ortalama 18,2 dakikada, üçüncü dersin keşifsel oturumu 74,8 dakikada haber veriyordu; kabul
testinin bulgusu ise ancak 600 dakikalık pencere açıldığında ortaya çıkar. Aynı kusur zinciri
boyunca haber verme anı büyür ve bedeli onunla büyür.

#### Özet

- Kabul aşamasına yalnız önceki kapıların görmediği kusurlar ulaşır: ikinci dersin beş kapalı
  olmayan sınıfından 18 kusur.
- Geri bildirim üç sınıftır ve sınıfı ölçütün durumu belirler: sınırı boş ölçüt istek, kusurlu
  tam ölçüt kusur, ölçüsü kullanıcıya görünmeyen kusursuz ölçüt yanlış anlama üretir.
- Kusur payı 0,11 ile 0,33 arasındadır; bir kabul turunu bulgu sayısıyla özetlemek ekibe kaç
  kusur bulunduğunu söylemez.
- Belirsizliği hedefleyen dağıtım en çok isteği ve en çok kaçan hatayı üretir (23 istek, 12
  kaçan, 6.783 dakika); kullanım payına göre dağıtım en çok kusuru bulur (8) ve en az kaçırır.
- En düşük toplam bedelli dağıtım en az bildirim üretendir (K4, 5.134 dakika, 12 kaçan); geri
  bildirim üretmemek ucuzdur, bu yüzden toplam bedel tek başına okunmaz.
- Kabul testinin kırmızısı üç ayrı kuyruğa gider: düzeltme, ürün kararı ve arayüz metni;
  yanlış anlama ölçüt yuvalarına geri besleme yapar.

#### Sonraki Adım

Bu konu dört karar bıraktı. Plan kapsamı, ölçütü ve sorumluluğu yazdı; risk sırası aynı
bütçeyi olasılık ile etkiye göre böldü; keşifsel oturum betikli kümenin göremediği sınıfa
genişlik yerine derinlikle ulaştı; kabul ölçütü ve kullanıcı kabul testi tamamlanmanın tanımını
ekibin dışına taşıdı. Dördünün ortak yanı şudur: hepsi **kâğıt üzerinde** bir dağıtımdır. Bir
plan yazılıdır ama koşmaz; bir risk sırası bir listedir ama kimse ona bakmadan da değişiklik
gönderebilir; bir oturum kapsamı ancak biri oturmaya karar verirse açılır; bir kabul ölçütü
ancak biri onu okursa kapanır. Bu dağıtımın her değişiklikte, kimsenin hatırlamasına
kalmadan, kendiliğinden işlemesi için bir düzenek gerekir — ve o düzenek hiç kurulmadı.
Otomasyon Altyapısı konusu tam buradan başlar.

## Otomasyon Altyapısı

Testlerin bir hatta yerleşmesi ve sonucun karara dönüşmesi: aşamalı test kapılarının geri bildirim süresi ile kaçan hata arasında kurduğu takas, ortam çeşitliliğinin maliyeti, test ortamında gizli değer ile veri güvenliği, sonuçların ekip için okunur kılınması, kaçan hata ile geri dönüş ve döngü süresi göstergelerinin yorumlanması ve yinelenen hataların kaynağına inilmesi.

### Sürekli Tümleştirmede Test

Source: https://academia.sh/tr/kurslar/test-sureci/surekli-tumlestirmede-test

Dört kursun bıraktığı sınamaların bir hatta yerleştirilmesi: aşama ve kapı düzeninin geri bildirim süresi ile kaçan hata arasında kurduğu takas, erken kesmenin koşum bütçesine etkisi, gecelik ve sürüm kapılarının bekleme bedeli ve karantinanın kapı kararına bağlanması.

Önceki konu bir dağıtım bıraktı: kapsam yazıldı, risk sırası kuruldu, keşifsel oturumun
kapsamı belirlendi, kabul ölçütü tamamlanma tanımına bağlandı. Hepsinin ortak yanı şudur —
bu dağıtım **kâğıt üzerindedir**. Bir plan, her değişiklikte kendiliğinden işleyen bir
düzeneğe bağlanmadığı sürece yalnızca bir niyettir; kimin ne zaman hangi testi koşacağı
insanın hatırlamasına kalır.

Bu ders o düzeneği kurar. Değişikliği alıp sırayla sınamalardan geçiren zincire **hat**
(pipeline) denir; hattın her parçası bir **aşama** (stage), aşamanın sonucunun bir kararı
zorladığı nokta ise bir **kapı**dır (gate). Kabuk Programlama kursundaki **boru hattı** bir
sürecin çıktısını diğerinin girdisine bağlar; buradaki hat ise bir değişikliğin geçmesi
gereken sıralı sınama kümesidir, veri değil karar taşır. Sorulan şey tek bir sayıyla
yazılabilir: hangi sınama hangi aşamada koşarsa geri bildirim süresi ile **kaçan hata**
(escaped defect, önceki kursların **kaçan gerçek hata** kavramıyla aynı şey) arasındaki takas
nereye düşer.

#### Sınama Envanteri

Dört kurs otuz ders boyunca sınama yazdı ve her dersin kapanış tablosu o sınamanın
koşumdan bağımsız bedelini bıraktı. O sayılar burada dokuz **sınama takımı**na toplanır.
Süreç sayısı, ağ isteği, veri erişimi ve elle inceleme adımı doğrudan kapanış
tablolarından okunmuştur: tümleşim takımının 816 erişimi Tümleşim Testinin Kapsamı (4
sorgu), Test Ortamı Yönetimi (6 adım), Test Verisi Yönetimi (772 sorgu), Veritabanı
Testleri (21 sorgu, 13 göç adımı) ve Dış Servis Taklidi (0) satırlarının toplamıdır;
güvenlik takımının 20 elle adımı dört güvenlik dersinin elle inceleme sayılarının
toplamıdır.

**TS6 — koşum bedeli model dakikasıdır: bir süreç ayağa kaldırma 1 dakika, bin ağ isteği 1
dakika, bin veri erişimi 1 dakika, bir elle inceleme adımı 15 dakika; taban 1 dakika.** Elle
adımın ağırlığı diğerlerinin on beş katıdır, çünkü elle iş bir kapıya konulamaz — sırasını
bekler.

**TS7 — yüz değişiklikte kusur sınıfı başına ortaya çıkma sayısı.** Alt katmanların gördüğü
sınıflar daha sık yazılır, çünkü her değişiklik koda dokunur; kapasite ve kurtarma sınıfları
seyrektir. İki sınıfın **yakalayan** listesi boştur: bunlar kaynak derslerin "yakalamadığı"
sütunundan gelir ve hiçbir takımın göremediği kusurlardır.

```js
// envanter.mjs — dort kursta yazilan sinamalarin takim listesi, bedeli ve kusur kumesi
const yukari = (n) => Math.ceil(n / 1000);

// TS6: surec 1 dk, bin istek 1 dk, bin erisim 1 dk, elle adim 15 dk; taban 1 dk.
export const bedel = (t) => Math.max(1,
  t.surec + yukari(t.istek) + yukari(t.erisim) + t.elle * 15);

// surec / istek / erisim / elle sayilari M21/K03 ve K04 kapanis tablolarindan okundu.
export const takimlar = [
  { ad: 'birim', surec: 0, istek: 0, erisim: 9, elle: 0 },
  { ad: 'sozlesme', surec: 6, istek: 126, erisim: 0, elle: 0 },
  { ad: 'tumlesim', surec: 1, istek: 0, erisim: 816, elle: 0 },
  { ad: 'uctan-uca', surec: 12, istek: 24, erisim: 25511, elle: 0 },
  { ad: 'basarim-kosum', surec: 44, istek: 78939, erisim: 0, elle: 0 },
  { ad: 'basarim-ornekleme', surec: 0, istek: 0, erisim: 536000, elle: 0 },
  { ad: 'guvenlik-otomatik', surec: 9, istek: 8, erisim: 183, elle: 0 },
  { ad: 'guvenlik-elle', surec: 0, istek: 0, erisim: 0, elle: 20 },
  { ad: 'dayaniklilik', surec: 0, istek: 639, erisim: 20, elle: 0 },
];

// TS7: yuz degisiklikte kusur sinifi basina ortaya cikma sayisi.
export const kusurlar = [
  { sinif: 'sinir karsilastirmasi', yakalayan: ['birim'], sayi: 13 },
  { sinif: 'sema uyusmazligi', yakalayan: ['tumlesim'], sayi: 8 },
  { sinif: 'kart durumunun guncellenmemesi', yakalayan: ['uctan-uca'], sayi: 7 },
  { sinif: 'durum kodu eslemesi', yakalayan: ['uctan-uca'], sayi: 6 },
  { sinif: 'kirici degisiklik', yakalayan: ['birim', 'sozlesme'], sayi: 6 },
  { sinif: 'istek bicimi kaymasi', yakalayan: ['sozlesme'], sayi: 5 },
  { sinif: 'erisilebilirlik olcutu', yakalayan: ['guvenlik-elle'], sayi: 5 },
  { sinif: 'sozlesmedeki alanin kaybolmasi', yakalayan: ['sozlesme'], sayi: 4 },
  { sinif: 'reddedilen istekte uyari yoklugu', yakalayan: ['uctan-uca'], sayi: 4, karantina: true },
  { sinif: 'gecikme gerilemesi', yakalayan: ['basarim-kosum'], sayi: 4 },
  { sinif: 'gocte veri kaybi', yakalayan: ['tumlesim'], sayi: 3 },
  { sinif: 'birlestirilmis sorgu', yakalayan: ['guvenlik-otomatik'], sayi: 3 },
  { sinif: 'anlamsal uyusmazlik', yakalayan: [], sayi: 3 },
  { sinif: 'kontrol-sonra-yaz yarisi', yakalayan: ['uctan-uca'], sayi: 2 },
  { sinif: 'dagilima bagli rapor kusuru', yakalayan: ['tumlesim'], sayi: 2 },
  { sinif: 'surec omrune bagli durum', yakalayan: [], sayi: 2 },
  { sinif: 'az ornekle okunan yuzdelik', yakalayan: ['basarim-ornekleme'], sayi: 1 },
  { sinif: 'kurtarma eksigi', yakalayan: ['dayaniklilik'], sayi: 1 },
  { sinif: 'kapasite siniri', yakalayan: ['dayaniklilik'], sayi: 1 },
];
```

```js
// bedel.mjs — takim basina kosum bedeli ve kusur kumesinin toplami
import { takimlar, kusurlar, bedel } from './envanter.mjs';

const s = (x, n) => String(x).padStart(n);
console.log(`${'takim'.padEnd(20)}${s('surec', 6)}${s('istek', 8)}${s('erisim', 8)}${s('elle', 6)}${s('dakika', 8)}`);
for (const t of takimlar) {
  console.log(`${t.ad.padEnd(20)}${s(t.surec, 6)}${s(t.istek, 8)}${s(t.erisim, 8)}${s(t.elle, 6)}${s(bedel(t), 8)}`);
}
const toplam = takimlar.reduce((a, t) => a + bedel(t), 0);
const kusurToplam = kusurlar.reduce((a, k) => a + k.sayi, 0);
const gorulmeyen = kusurlar.filter((k) => k.yakalayan.length === 0);
console.log(`\ntakim ${takimlar.length}, tam takim ${toplam} dakika`);
console.log(`kusur sinifi ${kusurlar.length}, yuz degisiklikte ${kusurToplam} kusur, `
  + `${100 - kusurToplam} temiz degisiklik`);
console.log(`hicbir takimin gormedigi sinif ${gorulmeyen.length}, `
  + `${gorulmeyen.reduce((a, k) => a + k.sayi, 0)} kusur`);
```

```
takim                surec   istek  erisim  elle  dakika
birim                    0       0       9     0       1
sozlesme                 6     126       0     0       7
tumlesim                 1       0     816     0       2
uctan-uca               12      24   25511     0      39
basarim-kosum           44   78939       0     0     123
basarim-ornekleme        0       0  536000     0     536
guvenlik-otomatik        9       8     183     0      11
guvenlik-elle            0       0       0    20     300
dayaniklilik             0     639      20     0       2

takim 9, tam takim 1021 dakika
kusur sinifi 19, yuz degisiklikte 80 kusur, 20 temiz degisiklik
hicbir takimin gormedigi sinif 2, 5 kusur
```

Tam takım 1021 dakikadır, yani on yedi saat. Bu sayı kararı zaten vermiştir: her
değişiklikte tam takımı koşturmak bir seçenek değildir. Piramidin aritmetiği Test Piramidi
dersinde yapıldı; burada piramit **zamana** çevrilir — birim takımı 1 dakika, uçtan uca
takımı 39, başarım takımının örnekleme çalışması tek başına 536 dakikadır.

#### Kapı Düzenleri

Bir düzen, aşamaların sırasını ve her aşamanın **sıklığını** belirler: her değişiklikte,
gecelik ya da sürüm başına. Sıklık bir bekleme üretir.

**TS8 — bekleme süreleri: gecelik kapı için 240 dakika, sürüm kapısı için 1200 dakika.**
Değişiklikler iş gününe düzgün yayılır; gecelik koşum gün sonunda bir kez çalışır, yani
ortalama bekleme yarım iş günüdür. Sürüm haftada birdir, ortalama bekleme iki buçuk iş
günüdür.

**TS9 — yüz değişiklik yirmi iş gününe yayılır: günde beş değişiklik, dört sürüm.** Bu
varsayım koşum sayısını belirler ve makine bedelini doğrudan etkiler.

**TS10 — bir koşucu günde 480 dakika iş yapar.** Gereken koşucu sayısı buradan çıkar ve
düzenin donanım bedelidir.

**Erken kesme** (fail fast), bir aşama kırmızıya döndüğünde sonraki aşamaların hiç
koşmamasıdır. Aşağıdaki ölçüm bunu bir seçenek olarak açıp kapatır.

```js
// kapi.mjs — ayni kusur kumesi uzerinde yedi kapi duzeninin karsilastirilmasi
import { takimlar, kusurlar, bedel } from './envanter.mjs';

const dk = Object.fromEntries(takimlar.map((t) => [t.ad, bedel(t)]));
const BEKLEME = { degisiklik: 0, gecelik: 240, surum: 1200 };   // TS8
const KOSUM = { degisiklik: 100, gecelik: 20, surum: 4 };       // TS9

const A = ['birim', 'sozlesme'];
const B = ['tumlesim', 'uctan-uca'];
const C = ['basarim-kosum', 'guvenlik-otomatik', 'dayaniklilik'];
const D = ['basarim-ornekleme', 'guvenlik-elle'];

const duzenler = [
  { ad: 'tek kapi', kes: true, asamalar: [['hepsi', 'degisiklik', [...A, ...B, ...C, ...D]]] },
  { ad: 'uc asama', kes: true, asamalar: [['hizli', 'degisiklik', A], ['orta', 'degisiklik', B], ['agir', 'degisiklik', [...C, ...D]]] },
  { ad: 'uc asama, kesmesiz', kes: false, asamalar: [['hizli', 'degisiklik', A], ['orta', 'degisiklik', B], ['agir', 'degisiklik', [...C, ...D]]] },
  { ad: 'agir asama gecelik', kes: true, asamalar: [['hizli', 'degisiklik', A], ['orta', 'degisiklik', B], ['agir', 'gecelik', [...C, ...D]]] },
  { ad: 'uc siklik', kes: true, asamalar: [['hizli', 'degisiklik', A], ['orta', 'degisiklik', B], ['agir', 'gecelik', C], ['surum', 'surum', D]] },
  { ad: 'elle is hat disi', kes: true, asamalar: [['hizli', 'degisiklik', A], ['orta', 'degisiklik', B], ['agir', 'gecelik', C], ['surum', 'surum', ['basarim-ornekleme']]] },
  { ad: 'uc siklik, karantina', kes: true, karantina: true, asamalar: [['hizli', 'degisiklik', A], ['orta', 'degisiklik', B], ['agir', 'gecelik', C], ['surum', 'surum', D]] },
];

const olc = (d) => {
  const asama = d.asamalar.map(([ad, siklik, tk]) => ({ ad, siklik, tk, sure: tk.reduce((a, x) => a + dk[x], 0) }));
  const oncekiSure = (i) => {
    const s = asama[i].siklik;
    const dahaSik = asama.filter((x) => KOSUM[x.siklik] > KOSUM[s]).reduce((a, x) => a + x.sure, 0);
    const ayni = asama.slice(0, i + 1).filter((x) => x.siklik === s).reduce((a, x) => a + x.sure, 0);
    return dahaSik + BEKLEME[s] + ayni;
  };
  let kacan = 0, agirlik = 0, toplamGeri = 0, enKotu = 0, kesilen = 0;
  for (const k of kusurlar) {
    const gorur = (t) => k.yakalayan.includes(t) && (d.karantina !== true || k.karantina !== true);
    const i = asama.findIndex((x) => x.tk.some(gorur));
    if (i === -1) { kacan += k.sayi; continue; }
    const geri = oncekiSure(i);
    agirlik += k.sayi;
    toplamGeri += geri * k.sayi;
    enKotu = Math.max(enKotu, geri);
    // erken kesme: kirmizi asamadan sonraki degisiklik asamalari kosmaz
    if (d.kes && asama[i].siklik === 'degisiklik') {
      kesilen += k.sayi * asama.slice(i + 1).filter((x) => x.siklik === 'degisiklik').reduce((a, x) => a + x.sure, 0);
    }
  }
  const makine = (asama.reduce((a, x) => a + x.sure * KOSUM[x.siklik], 0) - kesilen) / 100;
  return { makine, kosucu: Math.ceil((makine * 5) / 480), geri: toplamGeri / agirlik, enKotu, kacan };
};

const s = (x, n) => String(x).padStart(n);
console.log(`${'duzen'.padEnd(22)}${s('dk/degisiklik', 14)}${s('kosucu', 8)}${s('ort geri', 10)}${s('en kotu', 9)}${s('kacan/100', 11)}`);
for (const d of duzenler) {
  const r = olc(d);
  console.log(`${d.ad.padEnd(22)}${s(r.makine.toFixed(1), 14)}${s(r.kosucu, 8)}`
    + `${s(r.geri.toFixed(1), 10)}${s(r.enKotu, 9)}${s(r.kacan, 11)}`);
}
const sure = (g) => g.reduce((a, x) => a + dk[x], 0);
console.log(`\nasama sureleri: hizli ${sure(A)}, orta ${sure(B)}, gecelik ${sure(C)}, surum ${sure(D)} dakika`);
console.log(`gece penceresi 480 dakika; agir asamanin tamami ${sure([...C, ...D])} dakika, `
  + `${sure([...C, ...D]) <= 480 ? 'sigar' : 'sigmaz'}`);
```

```
duzen                  dk/degisiklik  kosucu  ort geri  en kotu  kacan/100
tek kapi                      1021.0      11    1021.0     1021          5
uc asama                       426.3       5     228.1     1021          5
uc asama, kesmesiz            1021.0      11     228.1     1021          5
agir asama gecelik             231.9       3     276.1     1261          5
uc siklik                       98.2       2     252.6     2221          5
elle is hat disi                86.2       1     107.7     1921         10
uc siklik, karantina            98.2       2     264.0     2221          9

asama sureleri: hizli 8, orta 41, gecelik 136, surum 836 dakika
gece penceresi 480 dakika; agir asamanin tamami 972 dakika, sigmaz
```

#### Tablonun Okunması

İlk iki satır aşamalara bölmenin ne kazandırdığını verir. Tek kapı düzeninde her kusur
1021 dakika sonra görünür, çünkü sonuç ancak takımın tamamı bitince çıkar. Aynı takım üç
aşamaya bölündüğünde ortalama geri bildirim 228,1 dakikaya iner — kaçan hata değişmez,
kaçırılan kusur kümesi aynıdır. Bölme tek başına bedava bir kazançtır ve kaybı yoktur.

Üçüncü satır **erken kesmenin** payını ayırır. Aynı düzen, kesme kapalıyken değişiklik
başına 1021 dakika ve on bir koşucu ister; kesme açıkken 426,3 dakika ve beş koşucu. Geri
bildirim süresi iki satırda aynıdır: erken kesme kimseye daha erken haber vermez, yalnız
kırmızı bir değişikliğin ardından koşacak 1013 dakikalık işi iptal eder. Kazanç yüzde
elli sekizdir ve doğrudan kusur sıklığından gelir — kusursuz bir depoda erken kesme hiçbir
şey kazandırmaz.

Dördüncü ve beşinci satırlar sıklığın bedelini gösterir. Ağır aşama gecelik kapıya
taşındığında değişiklik başına 231,9 dakika kalır, ama son satır bir sorun bildiriyor:
ağır aşamanın tamamı 972 dakikadır ve 480 dakikalık gece penceresine sığmaz. Gecelik bir
kapı, gece bitmeden bitmek zorundadır. Bu yüzden ağır aşama ikiye ayrılır — başarım koşumu,
otomatik güvenlik taraması ve dayanıklılık testleri gecelik (136 dakika, pencereye sığar),
başarım örneklemesi ve elle güvenlik incelemesi sürüm kapısına. Üç sıklık düzeni değişiklik
başına 98,2 dakikaya ve iki koşucuya iner; karşılığında en kötü geri bildirim 1021'den
2221 dakikaya çıkar.

Altıncı satır bu kursun kuralının en açık örneğidir. Elle güvenlik incelemesi hattan
tamamen çıkarıldığında değişiklik başına 86,2 dakika ve tek koşucu yeter, ortalama geri
bildirim 107,7 dakikaya düşer — **ve kaçan hata beşten ona çıkar.** Ortalamanın iyileşmesi
bir kazanç değil, bir ölçüm tuzağıdır: en geç yakalanan sınıf artık hiç yakalanmadığı için
ortalamanın paydasından da çıkmıştır. Geri bildirim süresi tek başına okunduğunda, testi
silmek her zaman iyi görünür.

Son satır karantinayı kapı kararına bağlar. Kırılganlık Yönetimi dersinde karantinaya
alınan uçtan uca test hattan çıkarıldığında koşum bedeli değişmez (98,2 dakika aynıdır,
çünkü takımın süresi başka testlerce belirlenir) ama koruduğu kusur sınıfı kaçar: beşten
dokuza. Karantina bir bakım kararı gibi görünür; tabloda bir kapı kararıdır.

İlk beş satırda kaçan hata beşte sabit kalıyor. Bu beş kusur iki sınıftandır ve hiçbir
düzen onları yakalamaz — anlamsal uyuşmazlık ile süreç ömrüne bağlı durum, kaynak derslerin
"yakalamadığı" sütunundan gelir. **Sıfır kaçan hata isteyen bir düzen yoktur;** en pahalı
düzen bile beşini kaçırır, çünkü envanterde o sınıfı gören bir sınama yazılmamıştır.

#### Kararın Geri Dönüşü

Bir kapının değeri, kırmızı sonucunun ne yaptığındadır. Hızlı ve orta aşamalar
**birleştirmeyi engeller**: sonuç kırmızıysa değişiklik ana dala girmez ve sahibi hâlâ o
işin başındadır. Gecelik kapı birleştirmeyi engelleyemez, çünkü değişiklik çoktan
girmiştir; kırmızı sonucu bir **kayıt açar** ve o kaydın sahibi gecelik koşuma giren
değişiklik kümesidir — kim olduğu tek tek belli değildir, bu da 240 dakikalık beklemenin
ikinci bedelidir. Sürüm kapısı **yayımı durdurur** ve kararın sahibi tek bir kişi değil,
sürümü onaylayan ekiptir.

Bu üç geri dönüş, aşama yerleşiminin asıl ölçütüdür: bir sınama, sonucunun karşılığı olan
kararın verilebildiği en erken aşamaya konur. Elle güvenlik incelemesinin sürüm kapısında
olması bir eksiklik değil, elle işin doğasıdır — sıra bekler ve otomatik bir kapıya konamaz.

#### Özet

- Dört kursun otuz dersi dokuz sınama takımına toplandı; tam takım 1021 model dakikasıdır
  ve bedellerin tamamı kaynak derslerin kapanış tablolarından okundu.
- Takımı üç aşamaya bölmek ortalama geri bildirimi 1021'den 228,1 dakikaya indirdi ve
  kaçan hatayı değiştirmedi.
- Erken kesme geri bildirimi hiç değiştirmeden değişiklik başına makine bedelini 1021'den
  426,3 dakikaya, koşucu sayısını on birden beşe indirdi.
- Ağır aşamanın tamamı 972 dakikadır ve 480 dakikalık gece penceresine sığmaz; ikiye
  ayrıldığında değişiklik başına bedel 98,2 dakikaya iner, en kötü geri bildirim 2221'e çıkar.
- Elle incelemeyi hattan çıkarmak ortalama geri bildirimi 107,7 dakikaya düşürdü ve kaçan
  hatayı beşten ona çıkardı; karantinadaki testin çıkarılması kaçan hatayı dokuza taşıdı.
- Hiçbir düzen kaçan hatayı sıfırlamadı: iki kusur sınıfını envanterdeki hiçbir sınama
  görmüyor.

#### Sonraki Adım

Bu tablonun her satırı bir varsayıma yaslanıyor: aşamanın süresi, sınamanın kendi koşum
bedelidir. Oysa hızlı aşama dışındaki her takım bir **test ortamı** ister — tümleşim takımı
bir bağımlı servis, uçtan uca takımı ayakta bir sistem, başarım takımı üretim benzeri bir
ortam. Bu ortamlar sonsuz değildir. İki değişiklik aynı anda geldiğinde biri bekler ve
beklediği süre aşamanın süresine eklenir; ortam çoğaltıldığında bekleme düşer ama maliyet
doğrusal büyür. Sonraki ders kaç ortamın hangi aşamada tutulacağını, paylaşılan ortamın
kuyruk süresini ve ayrı ortamın bedelini sayar.

### Test Ortamlarının Yönetimi

Source: https://academia.sh/tr/kurslar/test-sureci/test-ortamlarinin-yonetimi

Önceki dersin kapı düzenine ortam boyutunun eklenmesi: ortam sınıfının bir takıma verdiği görme sınırı, paylaşılan ortamın kuyruk süresi, ayrılmış ortamın boşta geçen bedeli ve sekiz ortam düzeninin aynı kusur kümesinde ürettiği kaçan hata.

Önceki dersin tablosundaki her satır sessiz bir varsayıma yaslanıyordu: bir aşamanın
süresi, orada koşan sınamaların koşum bedelidir. Bu, ortamı bedava saymak demektir. Oysa
hızlı aşama dışındaki her takım bir **test ortamı** ister ve ortam iki yerden bedel keser —
sınırlı sayıda olduğu için **kuyruk**, sürekli ayrılmış olduğu için boşta bekleme.

Bu dersin sorusu ortamın nasıl kurulduğu değildir; o düzenek Test Ortamı Yönetimi dersinde
adım adım kuruldu ve sayıldı (dört kurulum, iki yıkım adımı). Buradaki soru dağıtımdır:
kaç ortam, hangi aşamada, hangi sınıftan. Dağıtılan kaynak **ortam-dakikasıdır**; iki
karşılığı kuyruk süresi ile **kaçan hatadır**.

#### Ortam Sınıfı ve Sadakat

Bir ortamın ayırt edici özelliği fiyatı değil **sadakatidir**: üretimin hangi özelliğini
taşıdığı. Dört sınıf ayrılır. `yok` sınıfı ortam değildir, koşucunun kendisidir; yalnız
kendi başlattığı süreçleri kullanan takımlar buradadır. `gecici` sınıfı koşum başına
kurulup yıkılan, tek süreç ömürlü ve tohumlu veriyle dolan ortamdır. `paylasilan` sınıfı
sürekli ayakta duran tam sistemdir: birden çok servis, kalıcı durum, gerçek yeniden
başlatma. `uretim-benzeri` sınıfı buna üretim ölçeğinde veri ve gerçek kaynak sınırı ekler.

Sadakat bir takımın görme sınırını belirler. Uçtan uca takımı tek süreç ömürlü bir ortamda
kart durumunu sınayamaz, çünkü akış birden çok servise dağılmıştır. Bu yüzden kusur
kümesinin her satırına bir sütun daha eklenir: o sınıfın görünür olması için gereken **en
düşük ortam sınıfı**. Sütunun değerleri kaynak derslerin ölçümlerinden okundu — dağılıma
bağlı rapor kusuru tohumlu veriyle görülebildiği için `gecici`, kapasite sınırı yalnız
gerçek kaynak sınırında göründüğü için `uretim-benzeri`.

**TS11 — sadakat sıralıdır: `yok` < `gecici` < `paylasilan` < `uretim-benzeri`.** Üst sınıf
alt sınıfın gördüğü her şeyi görür. Bu, sadakati tek boyuta indiren bir basitleştirmedir.

**TS12 — ayrılmış ortam gün boyu bedel öder: paylaşılan ortam günde 1440 model dakikası,
üretim benzeri ortam dört katı.** Bedeli kullanımla değil varlığıyla ölçülür. Geçici ortam
bunun karşıtıdır: yalnız kullanıldığı dakikayı öder, koşum başına 3 dakikalık kurulum ve
yıkım ekler.

```js
// matris.mjs — ortam siniflari, asama listesi ve kusur kumesinin ortam sutunu
// Asama sureleri, kusur siniflari ve sayilari onceki dersin envanterinden okundu.
export const SEVIYE = { yok: 0, gecici: 1, paylasilan: 2, 'uretim-benzeri': 3 };   // TS11
export const GUNLUK = { paylasilan: 1440, 'uretim-benzeri': 5760 };                // TS12
export const KURULUM = 3;   // gecici ortamin kosum basina kurulum + yikim bedeli

// [ad, siklik, sure, taban, takimlar]; taban onceki dersin "uc siklik" satirindaki
// asama basina geri bildirim suresidir.
export const asamalar = [
  ['hizli', 'degisiklik', 8, 8, ['birim', 'sozlesme']],
  ['orta', 'degisiklik', 41, 49, ['tumlesim', 'uctan-uca']],
  ['gecelik', 'gecelik', 136, 425, ['basarim-kosum', 'guvenlik-otomatik', 'dayaniklilik']],
  ['surum', 'surum', 836, 2221, ['basarim-ornekleme', 'guvenlik-elle']],
].map(([ad, siklik, sure, taban, takimlar]) => ({ ad, siklik, sure, taban, takimlar }));

// [sinif, yakalayan takim, yuz degisiklikte sayi, gereken en dusuk ortam sinifi]
export const kusurlar = [
  ['sinir karsilastirmasi', 'birim', 13, 0],
  ['sema uyusmazligi', 'tumlesim', 8, 1],
  ['kart durumunun guncellenmemesi', 'uctan-uca', 7, 2],
  ['durum kodu eslemesi', 'uctan-uca', 6, 1],
  ['kirici degisiklik', 'sozlesme', 6, 0],
  ['istek bicimi kaymasi', 'sozlesme', 5, 0],
  ['erisilebilirlik olcutu', 'guvenlik-elle', 5, 2],
  ['sozlesmedeki alanin kaybolmasi', 'sozlesme', 4, 0],
  ['reddedilen istekte uyari yoklugu', 'uctan-uca', 4, 2],
  ['gecikme gerilemesi', 'basarim-kosum', 4, 3],
  ['gocte veri kaybi', 'tumlesim', 3, 1],
  ['birlestirilmis sorgu', 'guvenlik-otomatik', 3, 0],
  ['anlamsal uyusmazlik', null, 3, 0],
  ['kontrol-sonra-yaz yarisi', 'uctan-uca', 2, 2],
  ['dagilima bagli rapor kusuru', 'tumlesim', 2, 1],
  ['surec omrune bagli durum', null, 2, 0],
  ['az ornekle okunan yuzdelik', 'basarim-ornekleme', 1, 3],
  ['kurtarma eksigi', 'dayaniklilik', 1, 2],
  ['kapasite siniri', 'dayaniklilik', 1, 3],
].map(([sinif, takim, sayi, ortam]) => ({ sinif, takim, sayi, ortam }));

// Bir duzen, her havuza bir ortam sinifi, bir adet ve hizmet ettigi asamalari verir.
const G = (asamalar) => ({ sinif: 'gecici', adet: Infinity, asamalar });
const P = (adet, asamalar) => ({ sinif: 'paylasilan', adet, asamalar });
const U = (adet, asamalar) => ({ sinif: 'uretim-benzeri', adet, asamalar });

export const duzenler = [
  { ad: 'hepsi gecici', havuzlar: [G(['orta', 'gecelik', 'surum'])] },
  { ad: 'gecici orta', havuzlar: [G(['orta']), P(1, ['gecelik', 'surum'])] },
  { ad: 'tek paylasilan', havuzlar: [P(1, ['orta', 'gecelik', 'surum'])] },
  { ad: 'iki paylasilan', havuzlar: [P(2, ['orta', 'gecelik', 'surum'])] },
  { ad: 'dort paylasilan', havuzlar: [P(4, ['orta', 'gecelik', 'surum'])] },
  { ad: 'ayri agir ortam', havuzlar: [P(2, ['orta']), P(1, ['gecelik', 'surum'])] },
  { ad: 'uretim benzeri surum', havuzlar: [P(2, ['orta']), P(1, ['gecelik']), U(1, ['surum'])] },
  { ad: 'uretim benzeri agir', havuzlar: [P(2, ['orta']), U(1, ['gecelik', 'surum'])] },
];
```

#### Kuyruk

Ayrılmış bir ortam aynı anda tek koşum taşır. İki değişiklik aynı anda geldiğinde biri
bekler ve beklediği süre o değişikliğin **geri bildirim süresine** eklenir. Kuyruğun
büyüklüğü ortalama yüke değil gelişlerin düzensizliğine bağlıdır; bu yüzden gelişler düzgün
aralıklarla değil, tohumlu bir diziyle yerleştirilir.

**TS13 — günde beş değişiklik iş gününün 480 dakikasına tohumlu olarak dağılır; ortamlar
kesintisiz çalışır.** Önceki dersin TS9'u yüz değişikliği yirmi iş gününe yaymıştı; buradaki
ek, o beş değişikliğin gün içindeki yerleşimidir. Gecelik koşum her günün 480. dakikasında,
sürüm koşumu dört sürüm gününün aynı dakikasında kuyruğa girer — sürüm koşumu gecelik
koşumla aynı ortama aynı anda talip olur.

```js
// kuyruk.mjs — tohumlu gelis dizisi ve k ortamli ilk gelen ilk hizmet kuyrugu
export const GUN = 20, IS_GUNU = 480, TAM_GUN = 1440, GUNLUK_DEGISIKLIK = 5;

// TS13: dogrusal eslenik uretec (onceki kurstan); tohum gorunur, dizi yeniden uretilebilir.
export function uretecKur(tohum) {
  let durum = tohum;
  return () => { durum = (durum * 1103515245 + 12345) % 2147483648; return durum / 2147483648; };
}

export function gelisler(tohum = 20260731) {
  const rast = uretecKur(tohum);
  const liste = [];
  for (let gun = 0; gun < GUN; gun += 1) {
    const dakikalar = [];
    for (let i = 0; i < GUNLUK_DEGISIKLIK; i += 1) dakikalar.push(Math.floor(rast() * IS_GUNU));
    for (const d of dakikalar.sort((a, b) => a - b)) liste.push(gun * TAM_GUN + d);
  }
  return liste;
}

// isler: { t, sure, asama }. adet Infinity ise ortam her zaman hazirdir.
export function kuyrukOlc(isler, adet) {
  const sirali = [...isler].sort((a, b) => a.t - b.t);
  const bosalma = adet === Infinity ? null : new Array(adet).fill(0);
  const bekleme = new Map();
  let mesgul = 0;
  for (const is of sirali) {
    let baslangic = is.t;
    if (bosalma !== null) {
      let en = 0;
      for (let i = 1; i < bosalma.length; i += 1) if (bosalma[i] < bosalma[en]) en = i;
      baslangic = Math.max(is.t, bosalma[en]);
      bosalma[en] = baslangic + is.sure;
    }
    const b = bekleme.get(is.asama) ?? [];
    b.push(baslangic - is.t);
    bekleme.set(is.asama, b);
    mesgul += is.sure;
  }
  const ozet = new Map();
  for (const [asama, b] of bekleme) {
    ozet.set(asama, { ort: b.reduce((a, x) => a + x, 0) / b.length, enKotu: Math.max(...b) });
  }
  const doluluk = adet === Infinity ? 0 : mesgul / (adet * GUN * TAM_GUN);
  return { ozet, doluluk };
}
```

#### Sekiz Düzen

Ölçüm önceki dersin üç sıklık kapı düzenini sabit tutar, yalnız ortam dağıtımını
değiştirir. Aşamanın ortam sınıfı gereken sadakatin altındaysa kusur, yakalayan takım
yazılmış olsa bile kaçar.

```js
// olc.mjs — sekiz ortam duzeni ayni takim listesi uzerinde: kuyruk, bedel, kacan hata
import { SEVIYE, GUNLUK, KURULUM, asamalar, kusurlar, duzenler } from './matris.mjs';
import { gelisler, kuyrukOlc, GUN, IS_GUNU, TAM_GUN } from './kuyruk.mjs';

const GELIS = gelisler();
const SURUM_GUNLERI = [4, 9, 14, 19];               // TS9: yirmi is gununde dort surum

const isListesi = (asama, ek) => {
  const sure = asama.sure + ek;
  if (asama.siklik === 'degisiklik') return GELIS.map((t) => ({ t, sure, asama: asama.ad }));
  const gunler = asama.siklik === 'surum' ? SURUM_GUNLERI : [...Array(GUN).keys()];
  return gunler.map((g) => ({ t: g * TAM_GUN + IS_GUNU, sure, asama: asama.ad }));
};

export function olc(duzen) {
  const sinifi = new Map();                          // asama adi -> ortam sinifi
  for (const h of duzen.havuzlar) for (const a of h.asamalar) sinifi.set(a, h.sinif);

  let bedel = 0, doluluk = 0;
  const bekleme = new Map();
  for (const h of duzen.havuzlar) {
    const ek = h.sinif === 'gecici' ? KURULUM : 0;
    const isler = h.asamalar.flatMap((ad) => isListesi(asamalar.find((a) => a.ad === ad), ek));
    const r = kuyrukOlc(isler, h.adet);
    for (const [asama, o] of r.ozet) bekleme.set(asama, o);
    if (h.adet === Infinity) bedel += isler.reduce((a, x) => a + x.sure, 0);
    else { bedel += h.adet * GUNLUK[h.sinif] * GUN; doluluk = Math.max(doluluk, r.doluluk); }
  }

  let ek = 0, kuyruk = 0;                            // asama sirasinca birikir
  const geri = new Map();
  for (const a of asamalar) {
    if (sinifi.get(a.ad) === 'gecici') ek += KURULUM;
    kuyruk += bekleme.get(a.ad)?.ort ?? 0;
    geri.set(a.ad, a.taban + ek + kuyruk);
  }

  let kacan = 0, agirlik = 0, toplam = 0;
  for (const k of kusurlar) {
    const a = asamalar.find((x) => x.takimlar.includes(k.takim));
    const seviye = a === undefined ? 0 : SEVIYE[sinifi.get(a.ad) ?? 'yok'];
    if (a === undefined || seviye < k.ortam) { kacan += k.sayi; continue; }
    agirlik += k.sayi;
    toplam += geri.get(a.ad) * k.sayi;
  }
  return {
    ortam: duzen.havuzlar.reduce((a, h) => a + (h.adet === Infinity ? 0 : h.adet), 0),
    bedel: bedel / 100, doluluk, bekleme,
    kuyruk: bekleme.get('orta')?.ort ?? 0,
    enKotu: Math.max(0, ...[...bekleme.values()].map((o) => o.enKotu)),
    geri: toplam / agirlik, kacan,
  };
}

const s = (x, n) => String(x).padStart(n);
console.log(`${'duzen'.padEnd(22)}${s('ortam', 6)}${s('ortam-dk/deg', 14)}${s('doluluk', 9)}`
  + `${s('orta kuyruk', 13)}${s('en kotu', 9)}${s('ort geri', 10)}${s('kacan/100', 11)}`);
for (const d of duzenler) {
  const r = olc(d);
  console.log(`${d.ad.padEnd(22)}${s(r.ortam, 6)}${s(r.bedel.toFixed(0), 14)}`
    + `${s(r.ortam === 0 ? '-' : `%${(r.doluluk * 100).toFixed(0)}`, 9)}`
    + `${s(r.kuyruk.toFixed(1), 13)}${s(r.enKotu.toFixed(0), 9)}`
    + `${s(r.geri.toFixed(1), 10)}${s(r.kacan, 11)}`);
}

const tek = olc(duzenler.find((d) => d.ad === 'tek paylasilan'));
console.log(`\ntek paylasilan ortamda asama basina kuyruk (dakika):`);
for (const [asama, o] of tek.bekleme) {
  console.log(`  ${asama.padEnd(9)} ortalama ${s(o.ort.toFixed(1), 6)}, en kotu ${s(o.enKotu, 5)}`);
}
const yuk = 100 * 41 + 20 * 136 + 4 * 836;
console.log(`\ngelis dizisi tohumu 20260731; ${GELIS.length} degisiklik, ${GUN} is gunu, `
  + `ilk gelis ${GELIS[0] % TAM_GUN}. dakika, is gunu ${IS_GUNU} dakika`);
console.log(`bir paylasilan ortamin yuku ${yuk} dakika, ayrilmis suresi ${GUN * TAM_GUN} dakika`);
const gorulen = kusurlar.filter((k) => k.takim !== null);
const taban = gorulen.reduce((a, k) => a + asamalar.find((x) => x.takimlar.includes(k.takim)).taban * k.sayi, 0)
  / gorulen.reduce((a, k) => a + k.sayi, 0);
console.log(`ortam kuyrugu sifir sayildiginda ortalama geri bildirim ${taban.toFixed(1)} dakika`);
```

```
duzen                  ortam  ortam-dk/deg  doluluk  orta kuyruk  en kotu  ort geri  kacan/100
hepsi gecici               0           105        -          0.0        0      50.1         30
gecici orta                1           332      %21          0.0      136     262.9         24
tek paylasilan             1           288      %35          6.4      172     228.3         11
iki paylasilan             2           576      %18          0.0       38     212.5         11
dort paylasilan            4          1152       %9          0.0        0     211.6         11
ayri agir ortam            3           864      %21          0.0      136     221.4         11
uretim benzeri surum       4          2016      %12          0.0        0     240.3         10
uretim benzeri agir        3          1728      %21          0.0      136     263.5          5

tek paylasilan ortamda asama basina kuyruk (dakika):
  orta      ortalama    6.4, en kotu    50
  gecelik   ortalama   18.6, en kotu    79
  surum     ortalama  145.0, en kotu   172

gelis dizisi tohumu 20260731; 100 degisiklik, 20 is gunu, ilk gelis 74. dakika, is gunu 480 dakika
bir paylasilan ortamin yuku 10164 dakika, ayrilmis suresi 28800 dakika
ortam kuyrugu sifir sayildiginda ortalama geri bildirim 252.6 dakika
```

#### Tablonun Okunması

Son satır önceki dersle bağı kuruyor: ortam kuyruğu sıfır sayıldığında ortalama geri
bildirim 252,6 dakikadır — önceki dersin üç sıklık satırındaki sayının aynısı. O sayı bir
ortam varsayımını gizliyordu.

Tek paylaşılan ortam düzeninde doluluk yüzde 35'tir; ortam zamanın üçte ikisinde boştur.
Buna rağmen kuyruk sıfır değildir ve nerede biriktiği ortalamadan okunamaz: orta aşama
ortalama 6,4 dakika bekler, en kötü durumda 50; gecelik aşama 18,6 ve 79; sürüm aşaması
145,0 ve 172. Kuyruk en seyrek koşan aşamada en büyüktür: sürüm koşumu gecelik koşumla
aynı dakikada aynı ortama talip olur ve 136 dakikalık koşumun arkasında bekler. İkinci
ortam bunu doğrudan çözer — sürüm beklemesi 145'ten 7,5'e, orta aşamanınki
sıfıra iner. Karşılığında bedel 288'den 576 ortam-dakikasına çıkar ve **kaçan hata
değişmez** — on birde kalır. Kuyruk parayla çözülür, sadakat çözülmez.

Dört ortam bunu bir adım öteye götürür: kuyruk her aşamada sıfırlanır, doluluk yüzde 9'a
iner, bedel 1152 ortam-dakikasına çıkar ve ortalama geri bildirim 212,5'ten yalnız 211,6'ya
düşer. Üçüncü ve dördüncü ortam 576 ortam-dakikası karşılığında 0,9 dakika satın alır;
yüzde 18 dolulukta çoğaltma bir şey kazandırmaz.

İlk iki satır ters yöne bakıyor. Hepsi geçici düzeni hiç ayrılmış ortam tutmaz, değişiklik
başına 105 ortam-dakikasıyla en ucuz satırdır ve kaçan hatayı beşten otuza çıkarır. Geçici
ortam yalnız orta aşamada tutulduğunda bile kaçan hata yirmi dörttür, çünkü uçtan uca
takımı o aşamada koşar ve tek süreç ömürlü bir ortamda kart durumunu, reddedilen istekteki
uyarıyı ve kontrol-sonra-yaz yarışını göremez. Bedeli 332 ortam-dakikasıdır — tek
paylaşılan ortamdan **pahalı**, kaçan hatası iki katından fazla. İki eksende birden
yenilen bir düzen tabloda görünür, anlatıda görünmez.

Ortalama geri bildirim sütunu önceki dersteki tuzağı yineliyor: hepsi geçici düzeni 50,1
dakikayla tablonun en iyi geri bildirimini verir, çünkü geç yakalanan sınıflar artık hiç
yakalanmıyor ve ortalamanın paydasından düşüyor.

Son üç satır sadakatin nereye konacağını sorar. Üretim benzeri ortam sürüm kapısına
konduğunda kaçan hata on birden yalnız ona iner; geri kazanılan tek sınıf az örnekle okunan
yüzdeliktir. Aynı ortam gecelik aşamayı kapsadığında kaçan hata beşe düşer — gecikme
gerilemesi ve kapasite sınırı orada koşan takımlardır — üstelik dört yerine üç ortamla ve
2016 yerine 1728 ortam-dakikasıyla. Sadakat, kapının geç olmasıyla değil orada koşan
takımla eşleşir; en son kapıya en pahalı ortamı koymak parayı yanlış yere yatırmaktır.

Kaçan hata beşin altına inmiyor. Beş kusur, önceki dersin envanterinde hiçbir takımın
görmediği iki sınıftandır; süreç ömrüne bağlı durumu üretim benzeri bir ortam **da**
yakalamaz, çünkü onu yakalayacak bir sınama yazılmamıştır. Ortam sadakati bir sınamanın
görme sınırını genişletir, sınamanın kendisini yazmaz.

#### Kararın Geri Dönüşü

Ortam kararının geri dönüşü iki yerde belirir. Birincisi kuyruktur: değişiklik ortam
beklerken sahibi başka işe geçer, kırmızı sonuç geldiğinde bağlam kaybolmuştur — kuyruk
süresi bu yüzden koşum süresiyle aynı hanede sayılır. İkincisi ortamın kendi kırmızısıdır:
bir ortam bozulduğunda o aşamanın **bütün** testleri düşer ve düşen testler kusurdan değil
ortamdan haber verir. Bu sonucun sahibi değişikliği yapan kişi değil ortamı tutan ekiptir;
kararı testi karantinaya almak değil ortamı geri getirmektir. İki kırmızı türünün
ayrılamaması, paylaşılan ortamın sayılmayan üçüncü bedelidir.

#### Özet

- Ortam dört sınıfa ayrılır; sınıf, o aşamada koşan takımın görebileceği en üst kusur
  sınıfını belirler ve sınamanın kendisini yazmaz.
- Ortam kuyruğu sıfır sayıldığında ortalama geri bildirim 252,6 dakikadır: önceki dersin
  sayısı gizli bir ortam varsayımına dayanıyordu.
- Tek paylaşılan ortam yüzde 35 dolulukta bile kuyruk üretti; kuyruk en seyrek koşan
  aşamada en büyüktür (sürüm 145,0 dakika, orta 6,4 dakika).
- İkinci ortam kuyruğu neredeyse sıfırlar, bedeli 288'den 576 ortam-dakikasına çıkarır ve
  kaçan hatayı değiştirmez; dördüncü ortam 576 ortam-dakikasıyla 0,9 dakika alır.
- Geçici ortam en ucuz satırdır (105 ortam-dakikası) ve kaçan hatayı beşten otuza çıkarır;
  geçici ortamı orta aşamada tutan düzen hem daha pahalı hem daha çok hata kaçırır.
- Üretim benzeri ortamı sürüm kapısı yerine gecelik aşamaya taşımak kaçan hatayı ondan
  beşe indirdi ve bedeli 2016'dan 1728 ortam-dakikasına düşürdü.

#### Sonraki Adım

Üretim benzeri ortam bir soruyu açıkta bırakıyor: üretime benzemek için o ortama ne konur.
Veri tarafı Test Verisi Yönetimi dersinde ölçüldü. Sayılmayan şey erişim izni, kimlik,
imzalama anahtarı ve dış servis belirtecidir; hepsi **gizli değerdir** ve teste verilen her
gizli değer iki şey üretir — bir sızma yüzeyi ve yakalanabilir bir kusur sınıfı. Sonraki
ders bu dağıtımı sayar: ortama hangi gizli değer girer, yetki kapsamı daraltıldığında açık
yüzey ne kadar küçülür, süreli yetki kaç adımı elle bırakır.

### Gizli Değer ve Veri Yönetimi

Source: https://academia.sh/tr/kurslar/test-sureci/gizli-deger-ve-veri-yonetimi

Test ortamına giren gizli değerlerin dağıtımı: koşum artefaktlarında ölçülen sızma noktaları, en az yetkinin açık yüzeye etkisi, süreli yetkinin elle bıraktığı adım sayısı ve yedi yetki düzeninin aynı kusur kümesinde ürettiği kaçan hata.

Önceki ders üretim benzeri ortamı bir bedele bağladı ama o ortama **ne konduğunu** sormadı.
Veri tarafı Test Verisi Yönetimi dersinde ölçülmüştü: maskeleme kişisel alanların içeriğine
bakar ve hangi özelliği koruduğu orada sayıldı. Erişim izni, kimlik, imzalama anahtarı ve
dış servis belirteci bu ölçümün dışında kaldı. Bunlar **gizli değerdir** ve maskelenemezler
— bir gizli değerin maskelenmiş hâli çalışmaz, dolayısıyla ya girer ya girmez.

Bu dersin dağıttığı kaynak **yetkidir**: kaç gizli değer, ne kadar kapsamla, ne kadar
süreyle test ortamına girer. Dağıtımın iki karşılığı vardır — girmeyen her gizli değer bir
kusur sınıfını kaçırır, giren her gizli değer bir **sızma yüzeyi** açar.

#### Sızma Yüzeyi

Sızma yüzeyi bir tahmin değil, sayılabilir bir niceliktir: bir koşumun bıraktığı
artefaktlardan kaçı gizli değerin kendisini taşıyor. Aşağıdaki koşum, bildirim sınırını
gerçekten çağırır ve önceki kursta ölçülen kusuru — bilinmeyen muhatap — tetikler. İki
belirteç biçimi denenir: süresiz kalıcı bir belirteç ile yalnız o koşum için verilmiş bir
belirteç.

```js
// sizma.mjs — bir belirtecin kosum artefaktlarinda kac yerde gorundugu ve kosumdan sonraki degeri
import { createServer } from 'node:http';

const gecerli = new Set(['B-kalici-9f21', 'B-kosum-4a7c']);
const uyeler = new Set(['U-17']);
const sunucu = createServer((istek, yanit) => {
  const b = istek.headers['x-belirtec'] ?? '';
  const muhatap = new URL(istek.url, 'http://y').searchParams.get('muhatap');
  const [kod, govde] = !gecerli.has(b)
    ? [403, `{"hata":"gecersiz belirtec"}`]
    : uyeler.has(muhatap) ? [202, '{"durum":"kuyruga alindi"}']
      // tani govdesi istegin baglamini geri yaziyor: ucuncu sizma noktasi burasi
      : [404, `{"hata":"bilinmeyen muhatap: ${muhatap}","belirtec":"${b}"}`];
  yanit.writeHead(kod, { 'content-type': 'application/json' });
  yanit.end(govde);
});
sunucu.listen(8941, '127.0.0.1');

const TABAN = 'http://127.0.0.1:8941/bildirim';
const cagir = (b, muhatap) => fetch(`${TABAN}?muhatap=${muhatap}`, { headers: { 'x-belirtec': b } });

// Kosum, Test Ortami Yonetimi dersinde olculen kusuru tetikler: bilinmeyen muhatap.
async function kosum(b) {
  const artefakt = [`ADIM odunc-akisi x-belirtec=${b}`];      // 1: kosum kaydi
  const yanit = await cagir(b, 'U-99');
  artefakt.push(`HATA ${await yanit.text()}`);                // 2: karsi tarafin tani govdesi
  artefakt.push(`YENIDEN 1/1 x-belirtec=${b} sonuc=${yanit.status}`);   // 3: yeniden deneme ozeti
  return { artefakt, sizan: artefakt.filter((s) => s.includes(b)).length };
}

const kalici = await kosum('B-kalici-9f21');
const kosumluk = await kosum('B-kosum-4a7c');
gecerli.delete('B-kosum-4a7c');                               // yetki penceresi kapandi
const sonra = async (b) => (await cagir(b, 'U-17')).status;
const kaliciSonra = await sonra('B-kalici-9f21');
const kosumSonra = await sonra('B-kosum-4a7c');
sunucu.close();

console.log(`kalici belirtec : ${kalici.artefakt.length} artefakt, ${kalici.sizan} tanesinde deger var`);
console.log(`kosum belirteci : ${kosumluk.artefakt.length} artefakt, ${kosumluk.sizan} tanesinde deger var`);
console.log(`sizma noktasi iki bicimde de ayni : ${kalici.sizan === kosumluk.sizan}`);
console.log(`kosumdan sonraki istek, kalici belirtecle : ${kaliciSonra}`);
console.log(`kosumdan sonraki istek, kosum belirteciyle: ${kosumSonra}`);
console.log(`ekibin kodunda olmayan artefakt: ${kosumluk.artefakt[1]}`);
```

```
kalici belirtec : 3 artefakt, 3 tanesinde deger var
kosum belirteci : 3 artefakt, 3 tanesinde deger var
sizma noktasi iki bicimde de ayni : true
kosumdan sonraki istek, kalici belirtecle : 202
kosumdan sonraki istek, kosum belirteciyle: 403
ekibin kodunda olmayan artefakt: HATA {"hata":"bilinmeyen muhatap: U-99","belirtec":"B-kosum-4a7c"}
```

Üç sonuç okunmalı. Birincisi, tek bir başarısız çağrı üç artefakt bırakıyor ve üçü de
belirtecin kendisini taşıyor. İkincisi, bu üç noktadan yalnız ikisi ekibin kendi kodundadır;
üçüncüsü karşı tarafın tanı gövdesidir ve ekibin yazdığı hiçbir örtme kuralı o gövdeyi
öngöremez. Üçüncüsü, iki belirteç biçimi **tam olarak aynı sayıda** sızıyor — sızma noktası
belirtecin niteliğine değil hata yolunun biçimine bağlıdır.

Değişen tek şey koşumdan sonraki değerdir: kalıcı belirteç hâlâ 202 alıyor, koşum belirteci
403. Kaldıraç gizlemek değil, **kapsam** ve **süredir**.

#### Gizli Değer Kataloğu

Test ortamının istediği beş gizli değer vardır ve her biri belirli kusur sınıflarını açar.
Sayılar önceki dersin kusur kümesinden gelir: orta aşamada yakalanan otuz iki kusurun
tamamı bir gizli değere bağlıdır, gecelik aşamada dördü, sürüm aşamasında biri.

**TS14 — üretim kapsamındaki erişim sayıları varsayımdır; dar kapsamdakiler ölçümdür.** Dar
kapsam, gizli değerin gerçekten dokunduğu alt kümedir ve Test Verisi Yönetimi dersinin
ölçtüğü test kümesinden okundu: 772 satır, 200 üye. Üretim tarafındaki sayılar aynı
sistemin üretim ölçeğine ilişkin varsayımlardır ve bir büyüklük sırası taşırlar.

**TS15 — süreli düzende yetki penceresi 44 dakikadır ve kurulumdaki elle adımlar gizli
değer başına sayılır.** Pencere, orta aşamanın 41 dakikalık koşumu ile geçici ortamın 3
dakikalık kurulumunun toplamıdır; koşumdan kısa olamaz, çünkü koşum biterken yetkinin de
bitmesi gerekir. Elle adımlar üç kalemdir: gizli değer başına bir hesap açma, dar kapsamda
bir kapsam listesi yazma, otomatik vermede bir düzenek kurma. Bunlar dönem başına bir kez,
koşum başına elle adım ise her koşumda yeniden ödenir.

```js
// gizli.mjs — gizli deger katalogu ve yetki duzenleri
// asama basina geri bildirim suresi ve gizli degere bagli olmayan yakalanan kusur sayisi;
// ikisi de onceki dersin "uretim benzeri agir" satirindan okundu.
export const asamalar = {
  hizli: { geri: 8, serbest: 28 },
  orta: { geri: 49, serbest: 0 },
  gecelik: { geri: 425, serbest: 5 },
  surum: { geri: 2357, serbest: 5 },
};
export const KACAN_TABAN = 5;      // hicbir sinamanin gormedigi iki sinif
export const ELLE = 15;            // TS6: bir elle adim 15 dakika
export const KOSUM = { orta: 100, gecelik: 20, surum: 4 };

// [ad, asama, actigi kusur sayisi, yazma gerekir mi, uretim erisimi, dar kapsam erisimi]
export const gizliler = [
  ['katalog kimligi', 'orta', 13, true, 240000, 772],
  ['bildirim belirteci', 'orta', 10, false, 18000, 200],
  ['odeme anahtari', 'orta', 9, true, 9000, 41],
  ['basarim ortami kimligi', 'gecelik', 4, true, 240000, 772],
  ['olcum deposu kimligi', 'surum', 1, true, 4800, 24],
].map(([ad, asama, sayi, yazma, uretim, dar]) => ({ ad, asama, sayi, yazma, uretim, dar }));

// Bir duzen her gizli deger icin: ortama girer mi, kac kayda erisir, yazabilir mi;
// yetki penceresi (dakika), kurulumda elle adim, kosum basina elle adim.
export const duzenler = [
  { ad: 'uretim kimligi', girer: 1, erisim: (g) => g.uretim, yazar: 1, pencere: Infinity, kurulum: 0, kosumElle: 0 },
  { ad: 'ayri hesap', girer: 1, erisim: () => 772, yazar: 1, pencere: Infinity, kurulum: 5, kosumElle: 0 },
  { ad: 'salt okuma', girer: 1, erisim: () => 772, yazar: 0, pencere: Infinity, kurulum: 5, kosumElle: 0 },
  { ad: 'dar kapsam', girer: 1, erisim: (g) => g.dar, yazar: 1, pencere: Infinity, kurulum: 10, kosumElle: 0 },
  { ad: 'dar kapsam, sureli', girer: 1, erisim: (g) => g.dar, yazar: 1, pencere: 44, kurulum: 10, kosumElle: 1 },
  { ad: 'sureli, otomatik verme', girer: 1, erisim: (g) => g.dar, yazar: 1, pencere: 44, kurulum: 11, kosumElle: 0 },
  { ad: 'taklit', girer: 0, erisim: () => 0, yazar: 0, pencere: 0, kurulum: 0, kosumElle: 0 },
];
```

#### Yedi Yetki Düzeni

**En az yetki** ilkesi burada bir sayıya bağlanır: **açık yüzey**, sızan gizli değerlerin
toplamda kaç kayda erişebildiğidir. Bir gizli değer gerektirdiği yazma iznini alamazsa
açtığı kusur sınıfları kaçar; ortama hiç girmezse de aynı şey olur.

```js
// kapsam.mjs — yedi yetki duzeni: sizma yuzeyi, acik yuzey, elle adim, kacan hata
import { asamalar, gizliler, duzenler, KACAN_TABAN, ELLE, KOSUM } from './gizli.mjs';

const SIZMA_NOKTASI = 3;           // olculen deger: hata yolu uc artefakt uretiyor

function olc(d) {
  const giren = d.girer === 1 ? gizliler : [];
  const acik = giren.reduce((a, g) => a + d.erisim(g), 0);
  const elleKosum = d.kosumElle * Object.values(KOSUM).reduce((a, x) => a + x, 0);

  let ek = 0;
  const geri = {};
  for (const [ad, a] of Object.entries(asamalar)) {
    if (ad !== 'hizli') ek += d.kosumElle * ELLE;
    geri[ad] = a.geri + ek;
  }

  let kacan = KACAN_TABAN, agirlik = 0, toplam = 0;
  for (const [ad, a] of Object.entries(asamalar)) {
    agirlik += a.serbest;
    toplam += geri[ad] * a.serbest;
  }
  for (const g of gizliler) {
    if (d.girer === 0 || (g.yazma && d.yazar === 0)) { kacan += g.sayi; continue; }
    agirlik += g.sayi;
    toplam += geri[g.asama] * g.sayi;
  }
  return {
    giren: giren.length, sizma: giren.length * SIZMA_NOKTASI, acik,
    pencere: d.pencere, elle: d.kurulum + elleKosum, geri: toplam / agirlik, kacan,
  };
}

const s = (x, n) => String(x).padStart(n);
console.log(`${'yetki duzeni'.padEnd(24)}${s('giren', 6)}${s('sizma', 7)}${s('acik yuzey', 12)}`
  + `${s('pencere', 9)}${s('elle adim', 11)}${s('ort geri', 10)}${s('kacan/100', 11)}`);
for (const d of duzenler) {
  const r = olc(d);
  console.log(`${d.ad.padEnd(24)}${s(r.giren, 6)}${s(r.sizma, 7)}${s(r.acik, 12)}`
    + `${s(r.pencere === Infinity ? 'sinirsiz' : r.pencere, 9)}${s(r.elle, 11)}`
    + `${s(r.geri.toFixed(1), 10)}${s(r.kacan, 11)}`);
}

const uretim = gizliler.reduce((a, g) => a + g.uretim, 0);
const dar = gizliler.reduce((a, g) => a + g.dar, 0);
const yazan = gizliler.filter((g) => g.yazma);
console.log(`\ngizli deger ${gizliler.length}, yazma isteyen ${yazan.length}, `
  + `bunlarin actigi kusur ${yazan.reduce((a, g) => a + g.sayi, 0)}`);
console.log(`uretim kapsaminda ${uretim} kayit, dar kapsamda ${dar} kayit, `
  + `daralma ${(uretim / dar).toFixed(0)} kat`);
console.log(`kosum sayisi ${Object.values(KOSUM).reduce((a, x) => a + x, 0)}; `
  + `kosum basina bir elle adim ${Object.values(KOSUM).reduce((a, x) => a + x, 0) * ELLE} dakika eder`);
```

```
yetki duzeni             giren  sizma  acik yuzey  pencere  elle adim  ort geri  kacan/100
uretim kimligi               5     15      511800 sinirsiz          0     263.5          5
ayri hesap                   5     15        3860 sinirsiz          5     263.5          5
salt okuma                   5     15        3860 sinirsiz          5     304.7         32
dar kapsam                   5     15        1809 sinirsiz         10     263.5          5
dar kapsam, sureli           5     15        1809       44        134     277.1          5
sureli, otomatik verme       5     15        1809       44         11     263.5          5
taklit                       0      0           0        0          0     371.9         42

gizli deger 5, yazma isteyen 4, bunlarin actigi kusur 27
uretim kapsaminda 511800 kayit, dar kapsamda 1809 kayit, daralma 283 kat
kosum sayisi 124; kosum basina bir elle adim 1860 dakika eder
```

#### Tablonun Okunması

İlk ve dördüncü satırlar bu dersin ana ölçümüdür. Üretim kimliğiyle dar kapsam arasında
kaçan hata da (beş), ortalama geri bildirim de (263,5 dakika) birebir aynıdır; ayrıldıkları
tek yer açık yüzeydir — 511.800 kayda karşılık 1.809, iki yüz seksen üç kat. **En az
yetkinin kaçan hatadaki bedeli sıfırdır.** Ödenen şey kurulumdaki on elle adımdır ve bu
adımlar dönem başına bir kez ödenir. Kapsamı daraltmayı zorlaştıran şey ölçülebilir bir
takas değil, bir kez yapılacak işin ertelenmesidir.

Bu sayının bir koşulu vardır ve dersin başlığındaki ikinci yarıyı bağlar. Dar kapsamın
1.809 kaydı, ortamdaki verinin **maskelenmiş test kümesi** olduğunu varsayar. Ortama
üretimin bir kopyası alınmışsa gizli değerin kapsamı ne kadar dar olursa olsun açık yüzey
ortamın kendi büyüklüğüne eşitlenir: 240.000 kayıt orada durur ve o ortama erişebilen her
belirteç onlara erişir. İki karar bu yüzden ayrı ayrı verilemez — maskelenmemiş veri en az
yetkiyi geçersiz kılar, dar yetki de maskelemenin yerini tutmaz.

Üçüncü satır daraltmanın yanlış eksende yapılması hâlidir. Salt okuma yetkisi kulağa
güvenli gelir, ama beş gizli değerin dördü yazma ister: katalog kimliği göç sınamasında
şema değiştirir, ödeme anahtarı kart durumunu günceller, ölçüm kimlikleri sonuç yazar.
Bu dördünün açtığı yirmi yedi kusur kaçar ve kaçan hata beşten otuz ikiye çıkar. Açık yüzey
ise hiç küçülmez — 3.860'ta kalır, çünkü değer hâlâ ortama giriyor ve hâlâ üç noktadan
sızıyor. Bu satır iki eksende birden kaybeder.

Ortalama geri bildirim sütunu burada önceki iki dersteki tuzağın **tersini** gösteriyor.
Salt okumada ortalama 263,5'ten 304,7'ye, taklitte 371,9'a **çıkıyor**. Sebep kaçırılan
kusurların ucuz yakalananlar olmasıdır: orta aşamanın kırk dokuz dakikalık kusurları
paydadan düştüğünde geriye pahalı yakalananlar kalır ve ortalama yükselir. Aynı sütun bir
derste iyileşerek, bir derste kötüleşerek yalan söyleyebiliyor; ikisinde de yalanı açığa
çıkaran şey kaçan hata sütunudur.

Son satır sızma yüzeyini gerçekten sıfırlayan tek düzendir. Hiçbir gizli değer girmez,
sızma noktası sıfırdır, açık yüzey sıfırdır — ve kaçan hata kırk ikidir. Dış servis
taklidi bir sınama biçimi olarak Dış Servis Taklidi dersinde kuruldu; burada bir **yetki kararı** olarak
görünüyor ve bedeli otuz yedi kusurdur. Sızma yüzeyini sıfırlamak isteyen bir düzen,
sınamanın kendisini kapatmış olur.

Beşinci ve altıncı satırlar süreli yetkinin bedelini ayırır. Pencerenin sınırsızdan 44
dakikaya inmesi ne sızma noktasını (on beş) ne açık yüzeyi (1.809) değiştirir; değişen tek
şey sızan değerin koşumdan sonraki işe yararlığıdır — yukarıdaki koşumun 403'ü budur.
Belirteci elle vermek yüz yirmi dört koşumun her birine bir adım ekler: elle adım sayısı
ondan yüz otuz dörde, ortalama geri bildirim 263,5'ten 277,1 dakikaya çıkar. Vermeyi
otomatikleştirmek aynı pencereyi on bir elle adımla ve geri bildirimi hiç değiştirmeden
verir. Aradaki fark tek bir kurulum adımıdır ve 1.860 dakikalık elle işi ortadan kaldırır.

#### Kararın Geri Dönüşü

Bir gizli değerin sızdığı görüldüğünde verilen karar, tablodaki pencere sütununa bağlıdır.
Pencere sınırsızsa karar bir iptal ve yeniden verme işlemidir; o değeri kullanan her
sistemin bulunması gerekir ve iş üretim tarafını tutan ekibe düşer. Pencere 44 dakikaysa
karar bir kayıt açmakla sınırlıdır, çünkü değer kaydın açıldığı anda çoktan geçersizdir.
Aynı olay, aynı sızma noktası, iki ayrı karar.

İkinci geri dönüş hattın kendisindedir. Yetkinin reddi (403) ile kusurun kendisi (404) bir
aşamanın gözünde aynı kırmızıdır. Ayrılmadıkları sürece süresi dolmuş bir belirteç
**kırılgan test** gibi görünür ve karantinaya alınır; karantinaya alınan test ise önceki
dersin ölçtüğü gibi bir kusur sınıfını hattan düşürür. Yetki hatasını kusur hatasından
ayıran bir sınıflandırma, bu yüzden bir güvenlik önlemi değil bir kapı kararıdır.

#### Özet

- Gizli değer maskelenemez: ya test ortamına girer ya girmez; giren her değer bir sızma
  yüzeyi, girmeyen her değer bir kusur sınıfı kaçağı üretir.
- Tek bir başarısız çağrı üç artefakt bıraktı ve üçü de belirteci taşıdı; bu üçünden biri
  ekibin kodunda değil, karşı tarafın tanı gövdesindeydi.
- Sızma noktası sayısı belirtecin biçimine bağlı değildir; kalıcı ve süreli belirteç aynı
  sayıda sızdı, koşumdan sonra biri 202 aldı, öteki 403.
- Kapsamı daraltmak açık yüzeyi 511.800 kayıttan 1.809'a indirdi (283 kat) ve kaçan hatayı
  hiç değiştirmedi; bedeli kurulumdaki on elle adımdır.
- Salt okumaya çekmek beş gizli değerin dördünü işlevsiz bıraktı: kaçan hata beşten otuz
  ikiye çıktı, açık yüzey ise hiç küçülmedi.
- Süreli yetkiyi elle vermek yüz yirmi dört koşuma birer adım ekledi (134 elle adım, geri
  bildirim 277,1 dakika); vermeyi otomatikleştirmek aynı pencereyi 11 adımla ve 263,5
  dakikayla verdi.

#### Sonraki Adım

Üç ders boyunca üç dağıtım sayıldı: koşum dakikası, ortam-dakikası ve yetki. Üçünün de
ürettiği sayılar aynı yerde duruyor — bir koşumun içinde. Yukarıdaki üç artefaktı okuyan
kimse yoktur; kapı düzeninin ortalama geri bildirimini, ortam kuyruğunu ve açık yüzeyi
kimse ekranında görmez. Bir ölçünün karar değiştirebilmesi için önce okunabilir olması
gerekir ve okunabilirliğin de bir bütçesi vardır: kaç satır, kime, ne sıklıkta. Sonraki
ders bu dağıtımı ele alır — koşumun bıraktığı yüzlerce satırdan hangilerinin ekibe
ulaştığını ve ulaşmayanların hangi kararı sessizce iptal ettiğini sayar.

### Raporlama

Source: https://academia.sh/tr/kurslar/test-sureci/raporlama

Bir koşum sonucunun ekip için okunur kılınması: aynı gecelik koşumun dört raporlama düzeninde okunan satır, açılan adım ve gürültü kalemi cinsinden ölçülmesi, karara dönüşme süresinin dikkat bütçesiyle karşılaştırılması, karara bağlanamayan kökün kaçan hataya dönüşmesi ve kırılgan bölümünün ayıklama eşiğinin kaynağıyla birlikte taranması.

Önceki dersler hattı, ortamı ve gizli değeri düzene soktu. Hat artık her değişiklikte ve her
gece bir sonuç üretiyor. Bu dersin sorusu o sonucun kendisi değil, **kime ne söylediğidir**:
bir koşum sonucu bir karara dönüşmediği sürece koşulmamış sayılır, çünkü kimse ona dayanarak
bir şey yapmaz.

Bu bir yazım işi değil, bir dağıtım işidir. Dağıtılan kaynak **dikkattir**: sonuç karara
dönüşene kadar kaç satır okunuyor, neden kaç adımda bulunuyor, kaç kalem karşılığında hiçbir
şey yapılmıyor. Aynı koşum sonucu dört düzende sunulur ve bu üç sayı ölçülür.

#### Bir Koşumun Ham Sonucu

Ölçülecek sonuç gecelik bir koşumdan gelir: günün beş değişikliği birlikte alınır ve sürüm
kapısındaki iki takım dışında yedi takım koşar. Sonucun içindeki kırmızı kalemler dört
kaynaktan gelir ve raporun asıl işi bunları ayırmaktır — **kök** (bir kusurun ilk düşürdüğü
test), **türev** (aynı kökün düşürdüğü öteki testler), **ortam** (sınanan koda değil
düzeneğe ait arıza) ve **kırılgan** (kod değişmeden düşen test).

```js
// kosum.mjs — bir gecelik kosumun ham sonucu: test durumlari, kirmizi kalemler, kokler
let s = 20260731;                                  // tohum
const rast = () => ((s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32);

// TS16: yedi takimin test sayisi; kirilgan testler `adim` araligiyla dagilir ve `oran` ile
// duser, `gecmis` GERCEK bir kusur yuzunden dusme olasiligidir. Surum kapisi bu kosumda yok.
export const takimlar = [
  { ad: 'birim', test: 240, adim: 0, oran: 0.00, gecmis: 0.010 },
  { ad: 'sozlesme', test: 42, adim: 21, oran: 0.03, gecmis: 0.008 },
  { ad: 'tumlesim', test: 96, adim: 12, oran: 0.06, gecmis: 0.015 },
  { ad: 'uctan-uca', test: 34, adim: 3, oran: 0.15, gecmis: 0.012 },
  { ad: 'basarim-kosum', test: 12, adim: 6, oran: 0.10, gecmis: 0.010 },
  { ad: 'guvenlik-otomatik', test: 58, adim: 19, oran: 0.04, gecmis: 0.006 },
  { ad: 'dayaniklilik', test: 14, adim: 7, oran: 0.05, gecmis: 0.010 },
];

// M21/K03: karar 200 kosumda OLCULEN orana bakar; olculen oran iki kaynagin toplamidir.
export const testler = [];
for (const t of takimlar) {
  for (let i = 0; i < t.test; i += 1) {
    const kirilgan = t.adim > 0 && i % t.adim === 0 ? t.oran : 0;
    let dK = 0, dG = 0;
    for (let k = 0; k < 200; k += 1) {
      if (rast() < kirilgan) dK += 1;
      if (rast() < t.gecmis) dG += 1;
    }
    testler.push({ id: `${t.ad}/${i + 1}`, takim: t.ad, kirilgan, dK, dG,
      olculen: (dK + dG) / 200, kirmizi: false, kok: null });
  }
}

// TS17: bu kosumun kirmizi kokleri. Sinif adi ve yakalayan takim otomasyon-altyapisi/01'in
// kusur kumesinden; etkilenen test sayisi kusurun dokundugu ortak yuzeyden gelir.
export const kokler = [
  { ad: 'sema uyusmazligi', takim: 'tumlesim', etkilenen: 23, gercek: true },
  { ad: 'sinir karsilastirmasi', takim: 'birim', etkilenen: 3, gercek: true },
  { ad: 'durum kodu eslemesi', takim: 'uctan-uca', etkilenen: 5, gercek: true },
  { ad: 'bagimli servis ayaga kalkmadi', takim: 'dayaniklilik', etkilenen: 14, gercek: false },
];
for (const t of testler) if (t.kirilgan > 0 && rast() < t.kirilgan) { t.kirmizi = true; t.kok = 'kirilgan'; }
for (const k of kokler) {
  for (const t of testler.filter((x) => x.takim === k.takim).slice(0, k.etkilenen)) {
    t.kirmizi = true; t.kok = k.ad;
  }
}
export const kirmizilar = testler.filter((t) => t.kirmizi);
export const gercekKok = kokler.filter((k) => k.gercek);
```

Bu koşumda üç gerçek kusur ve bir ortam arızası var; düşen kalem sayısı çok daha büyüktür,
çünkü paylaşılan bir şemaya dokunan tek kusur o şemayı kullanan yirmi üç testi birden
düşürür. Rapor bunu bilmez; gördüğü şey yalnızca kırmızı kalemlerdir.

#### Dört Raporlama Düzeni

Aşağıdaki düzenek okumayı maliyet olarak modeller (TS18). Bir satır üç saniye, bir **adım**
üç dakikadır; adım, satırı okumakla bitmeyen her iştir — ayrıntıyı açmak, yeniden koşturmak,
sahibini bulmak. Öbeklenmemiş raporda okuyucu aynı imzayı ancak üçüncü kalemde birleştirir.

**TS19 — bir kişinin bir koşum sonucuna ayırdığı dikkat 30 dakikadır**, yani bir iş gününün
on altıda biri; duyarlılığı tablonun ikinci bölümünde taranır. Karara bağlanamayan kök bir
sonraki gecelik koşuma kalır ve TS8 gereği 240 dakika ekler; hiç bağlanamayan kök **kaçar**.

```js
// rapor.mjs — ayni kosum sonucunun dort raporlama duzeninde karara donusme suresi
import { takimlar, testler, kirmizilar, kokler, gercekKok } from './kosum.mjs';

// TS18: satir 3 sn, adim 3 dk, ham gunlukte kirmizi basina 12 ayrinti satiri, ayni imza
// ucuncu kalemde birlesir. TS19: bir kosum sonucuna ayrilan dikkat 30 dk.
const SN = 3, ADIM = 3, AYRINTI = 12, TANIMA = 3, BUTCE = 30;
const BEKLEME = 240;                        // otomasyon-altyapisi/01 TS8: gecelik bekleme

// Bir duzenin okuma akisi: sirayla {satir, adim, biten kok} olaylari.
function akis(duzen, kalan) {
  const canli = (t) => t.kirmizi && (t.kok === 'kirilgan' || kalan.includes(t.kok));
  const o = [], say = {};
  const ham = (hepsi) => {
    for (const tk of takimlar) {
      if (hepsi) o.push({ satir: 2, adim: 0 });
      for (const t of testler.filter((x) => x.takim === tk.ad)) {
        if (canli(t) === false) { if (hepsi) o.push({ satir: 1, adim: 0 }); continue; }
        const n = (say[t.kok] = (say[t.kok] ?? 0) + 1);
        const grup = testler.filter((x) => canli(x) && x.kok === t.kok).length;
        o.push({ satir: hepsi ? 1 + AYRINTI : 1, adim: n <= TANIMA ? 1 : 0,
          biten: n === Math.min(TANIMA, grup) ? t.kok : null });
      }
    }
  };
  if (duzen === 'ham gunluk') { o.push({ satir: 1, adim: 1 }); ham(true); }
  if (duzen === 'duz liste') { o.push({ satir: 1, adim: 0 }); ham(false); o.push({ satir: 1, adim: 0 }); }
  if (duzen === 'obeklenmis') {
    for (const k of kokler.filter((x) => x.gercek && kalan.includes(x.ad)))
      o.push({ satir: 3, adim: 1, biten: k.ad });          // baslik + kok test + "n kalem daha"
    o.push({ satir: 1, adim: 1 }, { satir: 1, adim: 1 }, { satir: 2, adim: 0 }); // ortam, kirilgan, ozet
  }
  if (duzen === 'yalniz ozet') { o.push({ satir: 3, adim: 2 }); ham(true); }
  return o;
}

const oku = (duzen, kalan, butce) => {
  let dk = 0, satir = 0, adim = 0; const biten = [];
  for (const e of akis(duzen, kalan)) {
    const y = dk + (e.satir * SN) / 60 + e.adim * ADIM;
    if (y > butce) break;
    dk = y; satir += e.satir; adim += e.adim;
    if (e.biten && kalan.includes(e.biten)) biten.push(e.biten);
  }
  return { dk, satir, adim, biten };
};

// Karara baglanmayan kok bir sonraki kosuma kalir (+240 dk); hic baglanamayan kok kacar.
function olc(duzen, butce = BUTCE) {
  let kalan = gercekKok.map((k) => k.ad), dongu = 0, ilk = null; const geri = [];
  while (kalan.length > 0 && dongu < 20) {
    dongu += 1;
    const r = oku(duzen, kalan, butce);
    ilk ??= r;
    if (r.biten.length === 0) break;
    for (const _ of r.biten) geri.push(BEKLEME * dongu + r.dk);
    kalan = kalan.filter((a) => r.biten.includes(a) === false);
  }
  const ort = geri.length ? geri.reduce((a, c) => a + c, 0) / geri.length : NaN;
  return { ...ilk, ilkDongu: ilk.biten.length, dongu, kacan: kalan.length, geri: ort };
}

const D = ['ham gunluk', 'duz liste', 'obeklenmis', 'yalniz ozet'];
const gurultu = { 'ham gunluk': testler.length - 3, 'duz liste': kirmizilar.length - 3,
  obeklenmis: 2, 'yalniz ozet': testler.length - 3 };
const p = (x, n) => String(x).padStart(n);
const tam = akis('ham gunluk', gercekKok.map((k) => k.ad)).reduce((a, e) => a + e.satir, 0);
console.log(`${testler.length} test, ${kirmizilar.length} kirmizi kalem, ${kokler.length} kok `
  + `(${gercekKok.length} gercek kusur + 1 ortam), ${kirmizilar.filter((t) => t.kok === 'kirilgan').length} bagimsiz kirilgan dusus`);
console.log(`ham gunluk ${tam} satir = ${((tam * SN) / 60).toFixed(1)} dk, butcenin `
  + `${((tam * SN) / 60 / BUTCE).toFixed(1)} kati\n`);
console.log(`${'duzen'.padEnd(14)}${p('satir', 7)}${p('adim', 6)}${p('dakika', 8)}${p('gurultu', 9)}`
  + `${p('ilk dongu', 11)}${p('dongu', 7)}${p('kacan kok', 11)}${p('ort geri', 10)}`);
for (const d of D) {
  const r = olc(d);
  console.log(`${d.padEnd(14)}${p(r.satir, 7)}${p(r.adim, 6)}${p(r.dk.toFixed(1), 8)}`
    + `${p(gurultu[d], 9)}${p(`${r.ilkDongu}/3`, 11)}${p(r.dongu, 7)}${p(r.kacan, 11)}`
    + `${p(Number.isNaN(r.geri) ? '-' : r.geri.toFixed(1), 10)}`);
}
console.log(`\nTS19 duyarliligi (ilk donguda karara baglanan kok / kacan kok)`);
console.log(`${'butce'.padEnd(8)}${D.map((d) => p(d, 14)).join('')}`);
for (const b of [15, 30, 60])
  console.log(`${`${b} dk`.padEnd(8)}${D.map((d) => { const r = olc(d, b); return p(`${r.ilkDongu} / ${r.kacan}`, 14); }).join('')}`);
```

```
496 test, 46 kirmizi kalem, 4 kok (3 gercek kusur + 1 ortam), 1 bagimsiz kirilgan dusus
ham gunluk 895 satir = 44.8 dk, butcenin 1.5 kati

duzen           satir  adim  dakika  gurultu  ilk dongu  dongu  kacan kok  ort geri
ham gunluk        325     4    28.3      493        1/3      3          1     389.0
duz liste          32     9    28.6       43        3/3      1          0     268.6
obeklenmis         13     5    15.7        2        3/3      1          0     255.7
yalniz ozet       299     5    30.0      493        1/3      2          2     270.0

TS19 duyarliligi (ilk donguda karara baglanan kok / kacan kok)
butce       ham gunluk     duz liste    obeklenmis   yalniz ozet
15 dk            1 / 2         1 / 0         3 / 0         0 / 3
30 dk            1 / 1         3 / 0         3 / 0         1 / 2
60 dk            2 / 0         3 / 0         3 / 0         2 / 0
```

Ham günlük 895 satırdır; tamamını okumak bütçenin bir buçuk katıdır. Okuyucu 325 satırda
bütçeyi bitiriyor ve o gece yalnız bir kökü karara bağlıyor. Üç gece üst üste okunduğunda
bile üçüncü kök görünmüyor: uçtan uca takımı günlüğün sonunda, kırk altı kırmızının ayrıntı
satırlarının ardındadır. **Bir kusurun raporda bulunması, raporda yer alması demek
değildir.**

Düz kırmızı listesi ile öbeklenmiş rapor **aynı kararı** veriyor: üç kökün üçü de ilk
gecede kapanıyor, kaçan yok. Fark bedeldedir. Düz liste 32 satır okutuyor, öbeklenmiş 13;
ama düz liste 9 adım açtırıyor, öbeklenmiş 5. Sayılar burada ayrışır: 32 satır 1,6 dakika,
9 adım 27 dakikadır. **Raporun bedeli okuma bedeli değil, açma bedelidir** — ve açma bedeli
yalnızca öbeklemeyle düşer, kısaltmayla değil. Düz liste bütçenin yüzde doksan beşini
tüketiyor; öbeklenmiş rapor yarısını.

Bu payın önemi duyarlılık tablosunda görünür. On beş dakikalık bir bütçede öbeklenmiş rapor
yine üç kökü de çıkarıyor, düz liste yalnız birini. Ters yönde de simetri yoktur: bütçe
altmış dakikaya çıkarıldığında ham günlük ilk döngüde iki kök veriyor, öbeklenmiş rapor
bunu on beş dakikada üç yapıyor. **Dikkati artırmak öbeklemenin yaptığını yapmıyor**, çünkü
harcanan dikkatin çoğu kararın kendisine değil, aynı kökü tekrar tekrar tanımaya gidiyor.

Gürültü sütunu farkın kaynağını veriyor: ham günlükte 493 kalem hiçbir karara bağlanmaz,
düz listede 43, öbeklenmiş raporda 2 — ortam ve kırılgan bölümleri. Yalnız özet düzeninde
rapor üç satırdır ve hiçbir şey söylemez; okuyucu ham günlüğe döner, iki kök kaçar. Kısa bir
rapor okunur bir rapor değildir.

#### Kırılgan Bölümünün Eşiği

Öbeklenmiş raporun kazancının bir bölümü kırılgan kalemleri ayrı bölüme almasından gelir.
Bu bir eşik kararıdır: **ölçülen düşme oranı eşiği aşan test kırılgan sayılır ve ana
bölümden çıkarılır.** Eşiğin kaynağı Kırılganlık Yönetimi dersidir; orada aynı sayı
karantina eşiği olarak tarandı ve testi hattan çıkardı. Buradaki karar daha hafiftir — test
koşmayı sürdürür — ama bedeli aynı biçimde ölçülür.

```js
// esik.mjs — kirilgan bolumunun ayiklama esigi: kesilen gurultu ile gizlenen gercek kirmizi
import { testler, kirmizilar, gercekKok } from './kosum.mjs';

const p = (x, n) => String(x).padStart(n);
const toplam = (f) => testler.reduce((a, t) => a + f(t), 0);
console.log(`${'esik'.padStart(6)}${p('ayrilan test', 14)}${p('bolume dusen', 14)}`
  + `${p('gizlenen kalem', 16)}${p('gizlenen kok', 14)}`
  + `${p('kesilen gurultu', 17)}${p('gizlenen gercek', 17)}`);
for (const e of [0.005, 0.01, 0.015, 0.025, 0.05, 0.1]) {
  const ayri = new Set(testler.filter((t) => t.olculen >= e).map((t) => t.id));
  const dusen = kirmizilar.filter((t) => ayri.has(t.id));
  const kalem = dusen.filter((t) => gercekKok.some((k) => k.ad === t.kok)).length;
  const kok = gercekKok.filter((k) =>
    kirmizilar.filter((t) => t.kok === k.ad).every((t) => ayri.has(t.id))).length;
  console.log(`${`%${(e * 100).toFixed(1)}`.padStart(6)}${p(ayri.size, 14)}${p(dusen.length, 14)}`
    + `${p(kalem, 16)}${p(kok, 14)}`
    + `${p(toplam((t) => (ayri.has(t.id) ? t.dK : 0)), 17)}`
    + `${p(toplam((t) => (ayri.has(t.id) ? t.dG : 0)), 17)}`);
}
console.log(`\nsutunlar: ilk dordu BU kosumun ${kirmizilar.length} kirmizi kalemi uzerinde, `
  + `son ikisi 200 kosumluk gecmis uzerinde`);
console.log(`gecmisteki ${toplam((t) => t.dK + t.dG)} dususun `
  + `${toplam((t) => t.dK)}'i kirilganliktan, ${toplam((t) => t.dG)}'i gercek kusurdandir; `
  + `rapor ikisini ayirt edemez, yalnizca toplam orani gorur`);
```

```
  esik  ayrilan test  bolume dusen  gizlenen kalem  gizlenen kok  kesilen gurultu  gizlenen gercek
  %0.5           431            45              31             3              541             1013
  %1.0           296            35              23             0              541              878
  %1.5           187            26              18             0              541              660
  %2.5            61            14               9             0              541              241
  %5.0            25             7               4             0              508               59
 %10.0            13             3               2             0              364               26

sutunlar: ilk dordu BU kosumun 46 kirmizi kalemi uzerinde, son ikisi 200 kosumluk gecmis uzerinde
gecmisteki 1554 dususun 541'i kirilganliktan, 1013'i gercek kusurdandir; rapor ikisini ayirt edemez, yalnizca toplam orani gorur
```

İlk satır eşiğin nereye varabileceğini gösteriyor. Yüzde 0,5'te 496 testin 431'i kırılgan
sayılıyor, 46 kırmızının 45'i ikinci bölüme düşüyor ve **üç gerçek kökün üçü birden
gizleniyor.** Rapor tertemiz görünür ve hiçbir şey söylemez: gürültüyü sıfırlayan bir rapor
sinyali de sıfırlamış olabilir.

Nedeni son iki sütunda yazılı. Geçmişteki 1554 düşüşün 541'i kırılganlıktan, 1013'ü gerçek
kusurdan gelir; rapor ikisini ayırt edemez, yalnız toplam oranı görür. Bu yüzden düşük bir
eşik, en çok kusur çıkarmış testleri gizler.

Yüzde 2,5 eşiği ayrımı yapabildiği en düşük noktadır: 61 test ayrılıyor, 14 kalem ikinci
bölüme iniyor, dokuzu gerçek kusurdan geliyor, ama **hiçbir kök tamamen gizlenmiyor** —
her kökün en az bir kalemi ana bölümde kalıyor. Karşılığında geçmişteki 541 kırılgan
düşüşün tamamı kesiliyor. Eşiği yükseltmek buradan sonra yalnız kaybettirir: yüzde 5'te
kesilen gürültü 508'e, yüzde 10'da 364'e iner, yani gürültü ana bölüme geri döner.

#### Kararın Geri Dönüşü

Bir raporun her bölümü bir eylemin karşılığıdır; karşılığı olmayan bölüm çıkarılır. Kök
bölümü **bir kaydı açar** ve sahibi o kusura dokunan değişikliğin sahibidir; gecelik koşumun
sahibi tek bir kişi olmadığı için bu bölüm kökü göstermek zorundadır, kalem saymak yetmez.
Ortam bölümü hattı işletenin işine girer ve sınanan kod hakkında hiçbir şey söylemez.
Kırılgan bölümü kimseyi durdurmaz; bir bakım listesidir ve karantina kararının girdisidir.

Türev kalemlerin gösterilmemesi de bir karardır, saklama değil: yirmi üç test tek kusurdan
düştüğü için tek kayıt açılır. Ayrı ayrı gösterilselerdi kayıt sayısı değişmez, harcanan
dikkat yirmi üç katına çıkardı.

#### Özet

- Aynı koşum sonucu dört düzende sunuldu: 496 test, 46 kırmızı kalem, 3 gerçek kök, 1 ortam
  arızası, 1 bağımsız kırılgan düşüş.
- Öbeklenmiş rapor üç kökü 13 satır, 5 adım ve 15,7 dakikada karara bağladı; düz liste aynı
  kararı 32 satır, 9 adım ve 28,6 dakikada verdi — fark açılan adımlardan gelir (9 adım 27
  dakika, 32 satır 1,6 dakikadır).
- Ham günlükte bir kök hiçbir gece karara bağlanmadı ve kaçtı; yalnız özet düzeninde iki kök
  kaçtı. Ortalama geri bildirim süresi öbeklenmiş raporda 255,7, ham günlükte 389,0 dakikadır.
- Dikkat bütçesini artırmak öbeklemenin yerini tutmadı: 60 dakikada ham günlük iki kök,
  öbeklenmiş rapor 15 dakikada üç kök çıkardı.
- Kırılgan bölümünün eşiği yüzde 0,5'e indirildiğinde üç kökün üçü de gizlendi; yüzde 2,5'te
  kırılgan düşüşlerin tamamı kesilirken hiçbir kök gizlenmedi.

#### Sonraki Adım

Buradaki bütün ölçüler tek bir koşuma aittir ve tek bir gecenin kararını verir. Bir rapor,
kendi koşumundaki kökü ne kadar iyi gösterirse göstersin, şu soruya yanıt veremez: bu ekip
zaman içinde daha mı iyi sınıyor, yoksa aynı hatalar başka adlarla mı geliyor. Şema
uyuşmazlığının bu gece çıkması bir olaydır; her hafta çıkması bir süreç kusurudur ve tek
koşumun raporunda ikisi aynı görünür. Sonraki ders ölçüyü dönem düzeyine taşır: kaçan hata,
geri dönüş ve döngü süresi aynı veri üzerinden hesaplanır ve her birinin hangi kararı
değiştirdiği sorulur.

### Kalite Göstergeleri

Source: https://academia.sh/tr/kurslar/test-sureci/kalite-gostergeleri

Kaçan hata, geri dönüş ve döngü süresinin aynı olay kümesinden hesaplanması: hattı ucuzlatan tek bir kararın iki dönemde ölçülmesi, altı göstergenin eşikleri ve eşiklerin kaynaklarıyla birlikte taranması, hangisinin bir kararı değiştirdiğinin sayılması ve kararı değiştirmeyen göstergenin dikkat cinsinden bedeli.

Önceki ders tek bir koşumun sonucunu okunur kıldı ve o koşumun kökünü bir karara bağladı.
Bir raporun yanıtlayamadığı bir soru kaldı: bu ekip zaman içinde daha mı iyi sınıyor. Şema
uyuşmazlığının bir gece çıkması bir olaydır, her hafta çıkması bir süreç kusurudur ve tek
koşumun raporunda ikisi aynı görünür. Bu ders ölçüyü dönem düzeyine taşır.

Ölçülecek şey burada sistem değil, ekiptir. Çalışan sistemin yanıt süresi, hata oranı ve
doyumu Başarım Karşıt Kalıpları ve İzleme kursunun konusudur; buradaki göstergeler
sınamanın kendisini ölçer. Dağıtılan kaynak yine **dikkattir** ve miktarı gösterge sayısıdır:
her gösterge dönem başına toplanmayı ve okunmayı ister, karşılığında bir karar üretmesi
beklenir. Üretmiyorsa gürültüdür.

#### Üç Gösterge, Tek Veri

**Kaçak oranı** (defect leakage rate), hattın hiçbir aşamasında yakalanmayıp üretime giden
kusurun toplam kusura oranıdır; payındaki **kaçan hata** M21/K03'ün kaçan gerçek hatasıyla
aynı kavramdır. **Geri dönüş (iş)** (rework), bir kusur yüzünden yeniden yapılan iştir ve
saat cinsinden ölçülür; Dayanıklılık ve Güvenilirlik kursundaki **geri dönüş** devralmadan
sonra asıl kopyaya dönüşü anlatır — aynı sözcük, ayrı iki kavram. **Döngü süresi** (cycle
time) bir değişikliğin gönderilmesinden ana dala girmesine kadar geçen süredir; yayıma kadar
olan süre **uçtan uca süredir** (lead time) ve bu derste ölçülmez.

Üçünün ortak yanı şudur: hepsi tek bir olay kümesinden çıkar — hangi kusur hangi aşamada
yakalandı. Aşama sırası, geri bildirim dakikaları ve kusur kümesi Sürekli Tümleştirmede Test
dersinin üç sıklık düzeninden alınır.

```js
// donem.mjs — iki donemin ayni kusur kumesi uzerinden bes gostergesi
// TS9 (otomasyon-altyapisi/01): 100 degisiklik, 20 is gunu. Sinif adlari ve yuz degisiklikteki
// sayilari ayni dersin TS7 kusur kumesinden; asama, o sinifi yakalayan takimin uc siklik
// duzenindeki yeridir. Toplam 80 kusur, 20 temiz degisiklik.
export const SINIF = [
  ['sinir karsilastirmasi', 13, 'hizli'], ['kirici degisiklik', 6, 'hizli'],
  ['istek bicimi kaymasi', 5, 'hizli'], ['sozlesmedeki alanin kaybolmasi', 4, 'hizli'],
  ['sema uyusmazligi', 8, 'orta'], ['kart durumunun guncellenmemesi', 7, 'orta'],
  ['durum kodu eslemesi', 6, 'orta'], ['reddedilen istekte uyari yoklugu', 4, 'orta'],
  ['gocte veri kaybi', 3, 'orta'], ['kontrol-sonra-yaz yarisi', 2, 'orta'],
  ['dagilima bagli rapor kusuru', 2, 'orta'], ['gecikme gerilemesi', 4, 'gecelik'],
  ['birlestirilmis sorgu', 3, 'gecelik'], ['kurtarma eksigi', 1, 'gecelik'],
  ['kapasite siniri', 1, 'gecelik'], ['erisilebilirlik olcutu', 5, 'surum'],
  ['az ornekle okunan yuzdelik', 1, 'surum'], ['anlamsal uyusmazlik', 3, 'kacar'],
  ['surec omrune bagli durum', 2, 'kacar'],
];

// Geri bildirim dakikasi otomasyon-altyapisi/01'in uc siklik duzeninden okundu.
// TS20: asamaya gore geri donus (is) saati — gec yakalanan kusur baglam kaybi, yeniden
// sinama ve uretimde olay yonetimi yuzunden buyur. TS21: kacan kusur uretimde bir surum
// dongusu (2400 dk) sonra bildirilir; bir degisikligin taban dongu suresi 480 dk.
export const GERI = { hizli: 8, orta: 49, gecelik: 425, surum: 2221, kacar: 2400 };
export const IS = { hizli: 0.25, orta: 1, gecelik: 3, surum: 6, kacar: 16 };
export const TABAN = 480, GUN = 480, DEGISIKLIK = 100;
export const KAPASITE = 800;   // TS22: 5 kisi x 20 is gunu x 8 saat

// Donem 2'nin tek farki, otomasyon-altyapisi/01'in "elle is hat disi" duzenidir: elle
// guvenlik incelemesi surum kapisindan cikarilir ve erisilebilirlik olcutu artik yakalanmaz.
export const donem = (ikinci) => SINIF.map(([ad, n, a]) =>
  [ad, n, ikinci && ad === 'erisilebilirlik olcutu' ? 'kacar' : a]);

export function olc(k) {
  const kusur = k.reduce((a, [, n]) => a + n, 0);
  const kacan = k.filter(([, , a]) => a === 'kacar').reduce((a, [, n]) => a + n, 0);
  const is = k.reduce((a, [, n, x]) => a + n * IS[x], 0);
  const geri = k.reduce((a, [, n, x]) => a + n * GERI[x], 0) / kusur;
  // Dongu suresi: temiz ve kacan degisiklik tabanda birlesir, yakalanan kusur asamasinin
  // geri bildirimini ve geri donus isini bekler.
  const d = [];
  for (let i = 0; i < DEGISIKLIK - kusur; i += 1) d.push(TABAN);
  for (const [, n, x] of k)
    for (let i = 0; i < n; i += 1)
      d.push(x === 'kacar' ? TABAN : TABAN + GERI[x] + IS[x] * 60);
  d.sort((a, b) => a - b);
  const y = (q) => d[Math.ceil((q / 100) * d.length) - 1] / GUN;
  return { kusur, kacan, kacak: kacan / kusur, is, isOran: is / KAPASITE, geri,
    medyan: y(50), p90: y(90), yakalanan: kusur - kacan };
}
```

Döngü süresi hesabındaki bir ayrıntı sonucun tamamını belirler: **kaçan kusur döngü süresini
uzatmaz.** Kimse onu görmediği için değişiklik zamanında birleşir; bedeli üretimde, başka bir
kalemde ödenir. Bu satır, ilerideki tabloda döngü süresinin neden yanlış yöne gittiğini
açıklar.

#### İki Dönem, Tek Karar

Karşılaştırılan iki dönem arasında tek bir fark vardır. Birinci dönem üç sıklık düzenini
kullanır. İkinci dönemde ekip hattı ucuzlatmaya karar verir ve elle güvenlik incelemesini
sürüm kapısından çıkarır — Sürekli Tümleştirmede Test dersinde bu düzenin değişiklik başına
98,2 dakikadan 86,2 dakikaya indiği ölçülmüştü. Aynı derste bu kararın kaçan hatayı beşten
ona çıkardığı da ölçülmüştü; buradaki soru, göstergelerin bunu görüp göremediğidir.

```js
// karar.mjs — bes gostergenin iki donemdeki degeri ve hangisinin bir karari degistirdigi
import { SINIF, IS, KAPASITE, DEGISIKLIK, donem, olc } from './donem.mjs';

const D = [olc(donem(false)), olc(donem(true))];
const p = (x, n) => String(x).padStart(n);
const b = (x, n) => x.toFixed(n);

// Esik ve esigin kaynagi. Esigi olmayan gostergeye bagli bir karar da yoktur.
const G = [
  { ad: 'kacak orani', al: (x) => x.kacak * 100, y: (v) => `%${b(v, 2)}`, esik: 8,
    karar: 'envantere sinama ekle' },
  { ad: 'geri donus orani', al: (x) => x.isOran * 100, y: (v) => `%${b(v, 2)}`, esik: 25,
    karar: 'sinifi erken asamaya tasi' },
  { ad: 'dongu suresi p90', al: (x) => x.p90, y: (v) => `${b(v, 2)} gun`, esik: 3.5,
    karar: 'asama yerlesimini degistir' },
  { ad: 'dongu suresi medyan', al: (x) => x.medyan, y: (v) => `${b(v, 2)} gun`, esik: null,
    karar: 'yok' },
  { ad: 'ort geri bildirim', al: (x) => x.geri, y: (v) => `${b(v, 1)} dk`, esik: 480,
    karar: 'kapi sikligini degistir' },
  { ad: 'yakalanan kusur', al: (x) => x.yakalanan, y: (v) => String(v), esik: null,
    karar: 'yok' },
];

console.log(`${DEGISIKLIK} degisiklik, ${SINIF.length} kusur sinifi, ${D[0].kusur} kusur, `
  + `${DEGISIKLIK - D[0].kusur} temiz degisiklik; ekip kapasitesi ${KAPASITE} saat`);
console.log(`\n${'gosterge'.padEnd(19)}${p('donem 1', 10)}${p('donem 2', 10)}${p('esik', 9)}`
  + `${p('D1 asti', 9)}${p('D2 asti', 9)}${p('karar degisti', 15)}${'  bagli karar'}`);
let degistiren = 0;
for (const g of G) {
  const [v1, v2] = [g.al(D[0]), g.al(D[1])];
  const [a1, a2] = [g.esik !== null && v1 >= g.esik, g.esik !== null && v2 >= g.esik];
  const degisti = g.esik !== null && a1 !== a2;
  if (degisti) degistiren += 1;
  console.log(`${g.ad.padEnd(19)}${p(g.y(v1), 10)}${p(g.y(v2), 10)}`
    + `${p(g.esik === null ? '-' : g.y(g.esik), 9)}${p(g.esik === null ? '-' : (a1 ? 'evet' : 'hayir'), 9)}`
    + `${p(g.esik === null ? '-' : (a2 ? 'evet' : 'hayir'), 9)}${p(degisti ? 'EVET' : 'hayir', 15)}`
    + `  ${g.karar}`);
}
// TS23: bir gostergenin donem basina toplama ve inceleme bedeli 30 dakika.
console.log(`\n${G.length} gosterge x 30 dk = ${G.length * 30} dk/donem inceleme; `
  + `karari degistiren ${degistiren}, gurultu ${(G.length - degistiren) * 30} dk`);

// Karar "hangi sinif" sorusunu da sorar: geri donus saatinin sinif kirilimi.
console.log(`\ngeri donus saatini en cok yiyen uc sinif `
  + `(toplam ${b(D[0].is, 0)} -> ${b(D[1].is, 0)} saat)`);
console.log(`${' '.repeat(3)}${p('donem 1', 32)}${p('donem 2', 32)}`);
const sirala = (ikinci) => donem(ikinci).map(([ad, n, a]) => [ad, n * IS[a]])
  .sort((x, y2) => y2[1] - x[1]).slice(0, 3);
const [s1, s2] = [sirala(false), sirala(true)];
for (let i = 0; i < 3; i += 1)
  console.log(`${`${i + 1}.`.padEnd(3)}${p(`${s1[i][0]} ${b(s1[i][1], 0)} sa`, 32)}`
    + `${p(`${s2[i][0]} ${b(s2[i][1], 0)} sa`, 32)}`);
```

```
100 degisiklik, 19 kusur sinifi, 80 kusur, 20 temiz degisiklik; ekip kapasitesi 800 saat

gosterge              donem 1   donem 2     esik  D1 asti  D2 asti  karar degisti  bagli karar
kacak orani             %6.25    %12.50    %8.00    hayir     evet           EVET  envantere sinama ekle
geri donus orani       %22.75    %29.00   %25.00    hayir     evet           EVET  sinifi erken asamaya tasi
dongu suresi p90     2.26 gun  1.23 gun 3.50 gun    hayir    hayir          hayir  asama yerlesimini degistir
dongu suresi medyan  1.05 gun  1.05 gun        -        -        -          hayir  yok
ort geri bildirim    386.8 dk  398.0 dk 480.0 dk    hayir    hayir          hayir  kapi sikligini degistir
yakalanan kusur            75        70        -        -        -          hayir  yok

6 gosterge x 30 dk = 180 dk/donem inceleme; karari degistiren 2, gurultu 120 dk

geri donus saatini en cok yiyen uc sinif (toplam 182 -> 232 saat)
                            donem 1                         donem 2
1.        anlamsal uyusmazlik 48 sa    erisilebilirlik olcutu 80 sa
2.   surec omrune bagli durum 32 sa       anlamsal uyusmazlik 48 sa
3.     erisilebilirlik olcutu 30 sa  surec omrune bagli durum 32 sa
```

#### Eşikler ve Kaynakları

Bir göstergenin değeri tek başına karar taşımaz; onu karara çeviren şey bir eşiktir ve eşiğin
bir kaynağı olmak zorundadır.

- **Kaçak oranı, yüzde 8.** Kaynağı envanterin kör noktasıdır: Sürekli Tümleştirmede Test
  dersinde hiçbir sınama takımının görmediği iki kusur sınıfı sayıldı, seksen kusurun beşi.
  Bu, kaçak oranının **tabanıdır** — yüzde 6,25. Eşik bu tabanın altına konamaz, çünkü
  ulaşılamaz bir eşik hiçbir dönemde karar üretmez, yalnız kalıcı bir alarm bırakır.
- **Geri dönüş oranı, yüzde 25.** Kaynağı ekip kapasitesidir: 800 saatin dörtte biri 200
  saattir ve bunun üstü, bir dönemin çeyreğinin yeni iş yerine düzeltmeye gitmesi demektir.
- **Döngü süresi p90, 3,5 iş günü.** Kaynağı sürüm kapısının kendisidir: o kapının ortalama
  beklemesi 1200 dakika, yani 2,5 iş günüdür. Eşik bunun altına konursa sürüm kapısına takılan
  her değişiklik eşiği aşar ve gösterge kapı düzeninin bir sonucunu kusur gibi bildirir.
- **Ortalama geri bildirim, 480 dakika.** Bir iş günü: bir kusurun sahibine aynı gün içinde
  ulaşması ölçütü.
- **Döngü süresi medyanı ve yakalanan kusur sayısının eşiği yoktur**, çünkü bağlı bir kararları
  yoktur.

#### Kararı Değiştiren Gösterge

Altı göstergeden ikisi bir kararı değiştirdi. Kaçak oranı yüzde 6,25'ten yüzde 12,50'ye
çıkarak eşiği geçti; geri dönüş oranı yüzde 22,75'ten yüzde 29,00'a çıkarak geçti. Kalan
dördü ya eşiği hiçbir dönemde aşmadı ya da eşiği yoktu.

İki gösterge aynı yönde hareket etti ama **aynı şeyi göstermiyor.** Kaçak oranı kusur sayar,
geri dönüş oranı saatle tartar ve sıraları farklıdır. Birinci dönemde geri dönüş saatini en
çok yiyen sınıf anlamsal uyuşmazlıktır: 48 saat, ama yalnız üç kusur. Seksen kusurun içinde
üç kusur hiçbir sayım tabanlı listede üste çıkmaz; saatle tartıldığında birinci sıradadır.
İkinci dönemde erişilebilirlik ölçütü 30 saatten 80 saate fırlayarak birinci sıraya geçiyor
— beş kusurun tamamı artık üretimde bulunuyor ve düzeltmeleri altı saat yerine on altı saat
sürüyor. Karar bu yüzden "bir şey yap" değil, "şunu yap" biçiminde çıkar.

Kalan dört gösterge dersin asıl bulgusudur. **Döngü süresi p90'ı 2,26 günden 1,23 güne
düştü** — yani kalite düşerken bu gösterge iyileşti. Nedeni koddaki tek satırda yazılı:
kaçan kusur döngü süresini uzatmaz. Sürüm kapısında bekleyen beş değişiklik artık orada
beklemiyor, çünkü orada onları bekleten bir sınama kalmadı. Medyan ise hiç kıpırdamadı,
1,05 gün. Bir sınamayı silmek, döngü süresini her zaman iyileştirir.

Ortalama geri bildirim süresi 386,8 dakikadan 398,0 dakikaya, yani yüzde üç kadar bozuldu —
kaçak iki katına çıkarken. Bunun nedeni sayıların yakınlığıdır: sürüm kapısı bir kusuru 2221
dakikada bildiriyordu, üretimdeki bir kaçan hata 2400 dakikada bildiriliyor. **Ortalama,
yakalamayı kaçırmadan ayırt edemez.** Yakalanan kusur sayısı da 75'ten 70'e düştü; tek başına
okunduğunda "daha az hata" gibi görünen bu satırın anlattığı şey daha az yakalamadır.

Dikkat hesabı bunun bedelini verir: altı gösterge dönem başına 180 dakika toplama ve inceleme
ister, 120 dakikası hiçbir kararı değiştirmez. **Kararı değiştirmeyen gösterge gürültüdür** —
ve bedeli yalnız o 120 dakika değildir. Aynı dönemde döngü süresi ile yakalanan kusur sayısı
birlikte iyileşmiş görünüyor; ikisine bakan bir ekip, kaçağı ikiye katlayan kararı bir başarı
olarak okuyabilir.

#### Kararın Geri Dönüşü

Kaçak oranının kararı envanteri değiştirir: kör noktadaki sınıf için yeni bir sınama yazılır
ve o sınamanın koşum bedeli bir sonraki dönemin hat süresine eklenir. Bu, döngü süresini
kötüleştirir. Göstergeler birbirinden bağımsız değildir; biri iyileştirilirken öteki ödeme
yapar ve bu yüzden tek bir gösterge hedef alınmaz.

Geri dönüş oranının kararı bir kusur sınıfını erken bir aşamaya taşır ve sahibi kapı düzenini
kuran kişidir. Döngü süresinin kararı aşama yerleşimini değiştirir. Bağlı kararı olmayan bir
göstergenin sahibi yoktur; dönem toplantısında bir sayı olarak okunur, kimse ona dayanarak bir
şey yapmaz ve bedeli her dönem yeniden ödenir.

#### Özet

- Kaçak oranı, geri dönüş (iş) oranı ve döngü süresi aynı kusur kümesinden hesaplandı; geri
  dönüş (iş), Dayanıklılık ve Güvenilirlik kursundaki geri dönüşten (devralma sonrası asıl
  kopyaya dönüş) ayrı bir kavramdır.
- Hattı ucuzlatan tek bir karar kaçak oranını yüzde 6,25'ten yüzde 12,50'ye, geri dönüş
  oranını yüzde 22,75'ten yüzde 29,00'a çıkardı; ikisi de eşiğini geçti ve bir karar üretti.
- Aynı karar döngü süresi p90'ını 2,26 günden 1,23 güne **düşürdü**, çünkü kaçan kusur döngü
  süresini uzatmaz; medyan hiç değişmedi ve yakalanan kusur 75'ten 70'e indi.
- Ortalama geri bildirim süresi yalnız yüzde üç bozuldu: üretimde fark etme süresi (2400 dk)
  sürüm kapısının beklemesine (2221 dk) yakın olduğu için ortalama kaçırmayı gizler.
- Altı göstergenin ikisi bir kararı değiştirdi; kalan dördü dönem başına 120 dakika dikkat
  yedi ve ikisi yanlış yöne işaret etti. Kararı değiştirmeyen gösterge gürültüdür.

#### Sonraki Adım

Göstergeler bir şeyi söylüyor, bir şeyi söylemiyor. Söyledikleri: bu dönemde şema uyuşmazlığı
sekiz kez geldi, sınır karşılaştırması on üç kez ve geri dönüş saatinin çoğu birkaç sınıfta
toplanıyor. Söylemedikleri: neden. Sekiz şema uyuşmazlığının sekizi de ayrı ayrı düzeltildi
ve her biri kendi geri dönüş saatini yeniden ödetti; sekizin altında tek bir ortak neden
varsa hiçbir gösterge bunu göstermez, çünkü göstergeler sayar, açıklamaz. Aynı sınıfın
yinelenmesi bir kusur değil bir belirtidir ve belirtiden nedene inen bir yöntem gerekir.
Sonraki ders o yöntemi kurar.

### Kök Neden Çözümlemesi

Source: https://academia.sh/tr/kurslar/test-sureci/kok-neden-cozumlemesi

Yinelenen kusur sınıfının kaynağına inilmesi: neden zincirinin bir veri yapısı olarak kurulması, belirtiyi düzelten müdahale ile nedeni düzelten müdahalenin aynı kusur kümesinde karşılaştırılması, zincirde derinleştikçe kapsamın büyüyüp uygulanabilirliğin düşmesi ve inceleme eşiğinin kaynağı.

Göstergeler bir sınıfın tekrar tekrar geldiğini sayar ama nedenini söylemez. Sekiz şema
uyuşmazlığı ayrı ayrı düzeltildiyse, sekizin altında tek bir ortak neden olup olmadığı hiçbir
göstergede görünmez. Aynı sınıfın yinelenmesi bir kusur değil bir belirtidir.

**Kök neden çözümlemesi** (root cause analysis) belirtiden nedene inen yöntemdir. Aracı
**neden zinciridir** (causal chain): bir kusurdan başlayıp her adımda "bu neden oldu" diye
sorarak ilerleyen halkalar dizisi — yaygın adıyla **beş neden** (five whys). Zincirin bir
yerinde durulur ve orada bir eylem yazılır. Belirtiyi kapatan eyleme **düzeltici eylem**
(corrective action), sınıfın yinelemesini kesen eyleme **önleyici eylem** (preventive action)
denir. Bu dersin sorusu, zincirde **nerede durulacağıdır**; dağıtılan kaynak inceleme saatidir.

#### Zincir Bir Veri Yapısıdır

Zincirin ilk iki halkası kusur sınıfına özgüdür: bu kusur neden oluştu, o neden neden vardı.
Üçüncüden sonrası paylaşılır — birkaç sınıf aynı süreç eksiğinden beslenir. Derinleştikçe
düğüm sayısı azalır, düğüm başına sınıf artar. Buna karşılık derin bir halka ekibin
denetiminden çıkar: kodda değil, örgütte değişecek bir şey olur.

**TS24 (varsayım) — bir halkayı incelemek 25 dakika sürer, ufuk boyunca inceleme bütçesi 400
dakikadır ve inceleme derinlik başına bir sürüm gecikmeyle yürürlüğe girer.** Bütçenin
kaynağı Kalite Göstergeleri dersindeki dönemlik 120 dakikalık dikkattir.
**TS25 (varsayım) — halka k'deki önleyici eylemin bedeli 60, 240, 720 ve 2.400 dakikadır**;
derin halka daha çok tarafı ilgilendirir.
**TS26 (varsayım) — eylemin uygulanabilirlik payı derinlikle düşer: 0,90 / 0,70 / 0,40 /
0,00.** Beşinci halka ekibin dışındadır.
**TS27 (varsayım) — kök neden eşiği 3 yinelemedir.** Eşiğin kaynağı bir ölçümdür: sınıf başına
yineleme ortalaması 60/14 = 4,29'dur, eşik 3 bunun bir basamak altını da kapsar.

```js
// kutuphane/kusurlar.mjs — birinci dersin kusur kumesi, aynen (tohum 20260731, 60 kusur,
// 14 sinif). Bu ders envanterin bedel sutunlarini kullanmaz, yalniz kusur kumesini kullanir.
const AGIRLIK = [["kural-siniri", 9], ["sema-uyusmazligi", 6], ["goc-veri-kaybi", 4],
  ["sozlesme-alani", 5], ["arayuz-durumu", 8], ["gorsel-sapma", 3], ["veri-buyumesi", 4],
  ["sql-birlestirme", 3], ["bilinen-acik", 3], ["erisilebilirlik", 4], ["bozulmus-yanit", 3],
  ["kurtarma-eksigi", 2], ["senaryo-disi", 5], ["anlam-sapmasi", 4]];
export function kusurlar(tohum, n) {
  let x = tohum;
  const rast = () => { x = (1103515245 * x + 12345) % 2147483648; return x / 2147483648; };
  const toplam = AGIRLIK.reduce((a, [, w]) => a + w, 0);
  const liste = [];
  for (let i = 1; i <= n; i += 1) {
    let p = rast() * toplam, sinif = AGIRLIK[AGIRLIK.length - 1][0];
    for (const [s, w] of AGIRLIK) { p -= w; if (p < 0) { sinif = s; break; } }
    liste.push({ no: i, sinif, olasilik: 1 + Math.floor(rast() * 5),
      etki: 1 + Math.floor(rast() * 5), duzeltme: 15 + 15 * Math.floor(rast() * 8) });
  }
  return liste;
}
```

#### Beş Müdahale, Aynı Kusur Kümesi

Model yirmi sürüm yürür. Bir sınıf eşik kadar yinelediğinde k. halkası için bir inceleme
açılır; bittiğinde o düğümün beslediği bütün sınıfların yinelemesi uygulanabilirlik
payınca kesilir. M1 hiç inceleme açmaz.

```js
// kutuphane/kok.mjs — neden zincirini bir veri yapisi olarak kurar ve belirtiyi duzelten
// mudahaleyi nedeni duzeltenle ayni kusur kumesinde karsilastirir. Girdi: kutuphane/kusurlar.mjs
import { kusurlar } from "./kusurlar.mjs";

const TOHUM = 20260731, N = 60, ADAY = 20, PAY = N / ADAY, KAT = 6;   // TS5, TS6
const INCELEME = 25, BUTCE = 400;           // TS24: halka basina inceleme dk, ufkun inceleme butcesi
const BEDEL = [0, 0, 60, 240, 720, 2400];   // TS25: halka k'deki onleyici eylemin bedeli
const UYG = [0, 0, 0.9, 0.7, 0.4, 0];       // TS26: eylemin uygulanabilirlik payi
const v = (x, n = 2) => x.toFixed(n).replace(".", ",");
const g = (x, n) => String(x).padStart(n);

// Neden zinciri: ilk iki halka sinifa ozgudur, ucuncuden sonrasi siniflar arasinda paylasilir.
// Derinlestikce dugum sayisi azalir, cunku ayni neden daha cok sinifi besler.
const G3 = {}, G4 = {};
for (const [neden, alt] of [
  ["sema-degisikligi-gozden-gecmiyor", ["sema-uyusmazligi", "goc-veri-kaybi", "sozlesme-alani"]],
  ["sinir-degeri-yazili-degil", ["kural-siniri", "arayuz-durumu"]],
  ["taban-goruntu-guncellenmiyor", ["gorsel-sapma"]],
  ["olcek-varsayimi-yazili-degil", ["veri-buyumesi", "bozulmus-yanit", "kurtarma-eksigi"]],
  ["guvenlik-kurali-incelemede-yok", ["sql-birlestirme", "bilinen-acik", "erisilebilirlik"]],
  ["kabul-olcutu-eksik", ["senaryo-disi", "anlam-sapmasi"]]]) for (const s of alt) G3[s] = neden;
for (const [neden, alt] of [
  ["degisiklik-turune-gore-kontrol-yok", ["sema-degisikligi-gozden-gecmiyor",
    "sinir-degeri-yazili-degil", "taban-goruntu-guncellenmiyor"]],
  ["varsayim-yazili-degil", ["olcek-varsayimi-yazili-degil", "guvenlik-kurali-incelemede-yok",
    "kabul-olcutu-eksik"]]]) for (const s of alt) G4[s] = neden;
const halka = (s, k) => (k <= 2 ? `${s}#${k}` : k === 3 ? G3[s]
  : k === 4 ? G4[G3[s]] : "surec-sahibi-atanmamis");

// Ikinci dersin butceye sigan dokuz kaleminin gormedigi bes sinif uretime kacar.
const KACAR = new Set(["erisilebilirlik", "bozulmus-yanit", "kurtarma-eksigi",
  "senaryo-disi", "anlam-sapmasi"]);
const KUSUR = kusurlar(TOHUM, N);
const SINIF = [...new Set(KUSUR.map((k) => k.sinif))];

// Ufuk: ADAY surum, surum basina PAY kusur. Bir sinif esik kadar yinelediginde o sinifin
// k. halkasi icin bir inceleme acilir; inceleme derinlik basina bir surum surer (TS24) ve
// bittiginde dugumun besledigi butun siniflarin yinelemesi UYG[k] payinca kesilir.
function ufuk(derinlik, esik) {
  const sayac = new Map(), onlenmis = new Map(), acik = new Set(), bekleyen = [];
  let yineleme = 0, duzeltici = 0, onleyici = 0, inceleme = 0, kacan = 0, an = 0, gec = 0;
  for (let s = 1; s <= ADAY; s += 1) {
    for (const b of bekleyen) if (b.s === s) onlenmis.set(b.d, b.pay);
    for (const k of KUSUR.slice((s - 1) * PAY, s * PAY)) {
      const d = derinlik >= 2 ? halka(k.sinif, derinlik) : null;
      const olusum = 1 - (d ? onlenmis.get(d) || 0 : 0);
      if (olusum <= 0) continue;
      // Inceleme acilmis ama henuz yururluge girmemisse yineleme gecikmede sayilir.
      if (d && acik.has(d) && onlenmis.has(d) === false) gec += olusum;
      yineleme += olusum;
      duzeltici += olusum * k.duzeltme;
      if (KACAR.has(k.sinif)) kacan += olusum;
      const c = (sayac.get(k.sinif) || 0) + olusum;
      sayac.set(k.sinif, c);
      if (derinlik >= 2 && c >= esik && acik.has(d) === false) {
        acik.add(d);
        inceleme += INCELEME * derinlik;
        onleyici += BEDEL[derinlik];
        bekleyen.push({ d, s: s + derinlik - 1, pay: UYG[derinlik] });
        an += s + derinlik - 1;
      }
    }
  }
  const toplam = duzeltici + inceleme + onleyici + KAT * kacan * 60;   // TS5: kacan hatanin kati
  return { acilan: acik.size, inceleme, onleyici, duzeltici, yineleme, kacan, gec,
    an: acik.size ? an / acik.size : 0, toplam, sigar: inceleme <= BUTCE };
}

console.log(`kusur kumesi tohum ${TOHUM}, ${N} kusur, ${SINIF.length} sinif, ${ADAY} surum`);
const say = SINIF.map((s) => KUSUR.filter((k) => k.sinif === s).length);
console.log(`sinif basina yineleme: en az ${Math.min(...say)}, en cok ${Math.max(...say)}, ortalama ${v(N / SINIF.length)}`);

console.log("\nzincirin yapisi — derinlestikce dugum azalir, uygulanabilirlik duser");
console.log("halka  dugum  dugum basina sinif  uygulanabilirlik  onleyici dk  beklenen onlenen sinif");
for (const k of [1, 2, 3, 4, 5]) {
  const d = new Set(SINIF.map((s) => halka(s, k)));
  const kapsam = SINIF.length / d.size;
  console.log(g(k, 5) + g(d.size, 7) + g(v(kapsam), 20) + g(k >= 2 ? v(UYG[k]) : "-", 18) +
    g(k >= 2 ? BEDEL[k] : "-", 13) + g(k >= 2 ? v(kapsam * UYG[k]) : "-", 24));
}

const AD = ["M1 belirtiyi duzelt", "M2 halka 2'de dur", "M3 halka 3'te dur",
  "M4 halka 4'te dur", "M5 halka 5'te dur"];
console.log(`\nayni kusur kumesinde bes mudahale (esik TS27 = 3, inceleme butcesi TS24 = ${BUTCE} dk)`);
console.log("mudahale              acilan  inceleme  butce   onleyici  duzeltici  yineleme  gecikmede  kacan  yururluk  toplam dk");
for (let k = 1; k <= 5; k += 1) { const o = ufuk(k, 3);
  console.log(AD[k - 1].padEnd(22) + g(o.acilan, 6) + g(o.inceleme, 10) +
    g(o.sigar ? "sigar" : "asiyor", 7) + g(o.onleyici, 11) + g(v(o.duzeltici, 0), 11) +
    g(v(o.yineleme), 10) + g(v(o.gec), 11) + g(v(o.kacan), 7) +
    g(o.acilan ? v(o.an, 1) : "-", 10) + g(v(o.toplam, 0), 11));
}

console.log("\nesik TS27'nin duyarliligi — halka 3'te durulursa");
console.log("esik  acilan  inceleme dk  butce   yineleme  kacan  toplam dk");
for (const e of [2, 3, 5]) { const o = ufuk(3, e);
  console.log(g(e, 4) + g(o.acilan, 8) + g(o.inceleme, 13) + g(o.sigar ? "sigar" : "asiyor", 7) +
    g(v(o.yineleme), 10) + g(v(o.kacan), 7) + g(v(o.toplam, 0), 11));
}
```

```
kusur kumesi tohum 20260731, 60 kusur, 14 sinif, 20 surum
sinif basina yineleme: en az 1, en cok 7, ortalama 4,29

zincirin yapisi — derinlestikce dugum azalir, uygulanabilirlik duser
halka  dugum  dugum basina sinif  uygulanabilirlik  onleyici dk  beklenen onlenen sinif
    1     14                1,00                 -            -                       -
    2     14                1,00              0,90           60                    0,90
    3      6                2,33              0,70          240                    1,63
    4      2                7,00              0,40          720                    2,80
    5      1               14,00              0,00         2400                    0,00

ayni kusur kumesinde bes mudahale (esik TS27 = 3, inceleme butcesi TS24 = 400 dk)
mudahale              acilan  inceleme  butce   onleyici  duzeltici  yineleme  gecikmede  kacan  yururluk  toplam dk
M1 belirtiyi duzelt        0         0  sigar          0       4125     60,00       0,00  18,00         -      10605
M2 halka 2'de dur         12       600 asiyor        720       2870     42,00       1,00  14,40      12,5       9374
M3 halka 3'te dur          5       375  sigar       1200       2813     40,40       8,00  15,20      10,8       9860
M4 halka 4'te dur          2       200  sigar       1440       3075     44,40       7,00  15,60       8,0      10331
M5 halka 5'te dur          1       125  sigar       2400       4125     60,00      11,00  18,00       9,0      13130

esik TS27'nin duyarliligi — halka 3'te durulursa
esik  acilan  inceleme dk  butce   yineleme  kacan  toplam dk
   2       6          450 asiyor     29,90  10,30       7707
   3       5          375  sigar     40,40  15,20       9860
   5       4          300  sigar     53,00  17,30      11204
```

#### Belirti ile Neden

M1 altmış kusuru tek tek düzeltir: 4.125 dakika düzeltici eylem, altmış yineleme, on sekiz
kaçan hata. Hiçbir sınıf durmaz, çünkü her müdahale yalnız o kusuru kapatır. M3 beş inceleme
açar, yinelemeyi 40,40'a, kaçan hatayı 15,20'ye indirir ve düzeltici bedeli 2.813 dakikaya
çeker; aradaki 1.312 dakika yazılmayan düzeltmelerdir.

Bu, önleyici eylemin bedava olduğu anlamına gelmez. M3 önleyici eyleme 1.200, incelemeye 375
dakika öder; toplamı 9.860 dakikadır ve M1'in 10.605'inden 745 dakika iyidir. **Yinelemeyi
kesmenin getirisi, kestiği yinelemenin kaçan hata payındadır.**

M2 en düşük toplamı verir (9.374 dakika), ama on iki inceleme açar ve 600 dakika ister;
TS24'ün bütçesi 400'dür. Kâğıttaki en iyi satır bütçede yoktur; sığan en ucuz müdahale M3'tür.

#### Derinliğin İki Yönü

Yapı tablosu derinliğin iki karşıt etkisini ayırır. Düğüm başına sınıf 1,00'dan 14,00'a
çıkar — daha derin bir neden daha çok sınıfı besler — ama uygulanabilirlik 0,90'dan 0,00'a
iner. İkisinin çarpımı 0,90, 1,63, 2,80 ve 0,00'dır: dördüncü halkada en yüksek, beşincide
sıfır.

Yine de bu sıralama tek başına karar vermez. M4 beklenen kapsamı en yüksek halkadır ama ufukta
M3'ten kötüdür (44,40 yinelemeye karşı 40,40). Nedeni gecikmedir: derinlik k, eylemin
yürürlüğe girmesini k−1 sürüm geciktirir ve gecikmede yinelenen kusur M2'de 1,00, M3'te 8,00,
M5'te 11,00'dır. **Derine inmek yalnız pahalı değil, yavaştır; yavaşlık kendi yinelemesini
üretir.**

#### Beşinci Halkanın Bedeli

M5 bu dersin en açık sayısıdır. Tek bir inceleme açar, 125 dakika inceleme ve 2.400 dakika
önleyici eylem öder, sonra hiçbir şey değişmez: yineleme 60,00, kaçan hata 18,00 — ikisi de
M1'in aynısı. Toplamı 13.130 dakikadır, **hiç inceleme yapmamaktan 2.525 dakika daha pahalı.**

Zincir doğrudur; sorun zincirde değildir. "Süreç sahibi atanmamış" gerçekten on dört sınıfı da
besler. Ama o halkada yazılan eylem ekibin uygulayabileceği bir eylem değildir ve uygulanmayan
eylem sıfır kusur önler. Beş neden bir derinlik yarışı değildir: durulacak yer **ekibin
gerçekten değiştirebileceği son halkadır.**

#### Eşik ve Kararın Geri Dönüşü

Eşik taraması aynı biçimi tekrarlar. Eşik 2 en düşük toplamı verir (7.707 dakika, 29,90
yineleme, 10,30 kaçan) ama altı inceleme açar ve 450 dakika ister — bütçeyi aşar. Eşik 5 sığar
ama yinelemeyi 53,00'te bırakır. Eşiğin kaynağı bir gereksinim değil iki yönlü bir ölçümdür:
sınıf başına ortalama yineleme alt sınırı, inceleme bütçesi üst sınırı verir.

Kararın geri dönüşü iki kuyruktur. Düzeltici eylem kaydı kapatır ve kusurun sahibinde
biter. Önleyici eylem bir süreç değişikliği açar — bir inceleme adımı, bir kontrol listesi
kalemi, yazılı bir varsayım — ve kapanması ekibin dışında bir onaya bağlıdır. Sahibi
yazılmadan açılan kök neden kaydı, planlama konusunun sahipsiz kalemleriyle aynı sonu
bulur: kâğıtta kapsanır, gerçekte koşmaz.

#### Özet

- Neden zinciri bir veri yapısıdır: ilk iki halka sınıfa özgüdür, sonrası paylaşılır ve düğüm
  sayısı 14'ten 1'e iner.
- Belirtiyi düzeltmek altmış yinelemeyi ve on sekiz kaçan hatayı olduğu gibi bırakır; halka
  3'te durmak yinelemeyi 40,40'a, kaçan hatayı 15,20'ye indirir.
- Derinliğin iki yönü vardır: düğüm başına sınıf 1,00'dan 14,00'a çıkarken uygulanabilirlik
  0,90'dan 0,00'a iner. Derinlik ayrıca gecikmedir — gecikmede yinelenen kusur M2'de 1,00,
  M3'te 8,00, M5'te 11,00'dır.
- Beşinci halkada durmak 2.525 dakikayı hiçbir şey için öder: yineleme ve kaçan hata hiç
  incelememekle birebir aynı kalır.
- Eşiğin kaynağı iki yönlü bir ölçümdür: sınıf başına ortalama yineleme alt sınırı, inceleme
  bütçesi üst sınırı belirler.

#### Kurs Kapanışı

Bu kursun on bir dersi aynı biçimi izledi: sınırlı bir kaynak dağıtıldı, dağıtımın ürettiği
kaçan hata ile geri bildirim süresi ölçüldü.

| Ders | Dağıtılan kaynak | Dağıtım kararı | Kaçan hata / geri bildirim süresi |
|---|---|---|---|
| Test Planı | sürüm başına 120 dk test bütçesi | bütçeye sığan dokuz test | 18 kaçan / 16,5 dk (tam kapsam 10 / 40,8) |
| Risk Temelli Önceliklendirme | aynı 120 dk, sıra serbest | tahmin/dakika sırası | 18 kaçan, kaçan risk 155 / 18,2 dk |
| Keşifsel Test | 120 dk oturum = genişlik × derinlik | dört yol, derinlik beş | 10,39 kaçan / 74,8 dk |
| Kabul Ölçütleri | ürün tarafının 300 dk dikkati | on yedi anlamsal ölçüt (204 dk) | 0 kaçan / kabul incelemesi aşaması |
| Kullanıcı Kabul Testi | 600 kullanıcı dk, on iki görev | kullanım payına göre | 10 kaçan / 26,0 dk bulgu anı |
| Sürekli Tümleştirmede Test | 1021 model dk koşum | üç aşama ve erken kesme | 5 kaçan / 228,1 dk |
| Test Ortamlarının Yönetimi | ortam sayısı ve ortam-dakikası | üretim benzeri ortam gecelik aşamaya | 5 kaçan / 263,5 dk |
| Gizli Değer ve Veri Yönetimi | test ortamına giren gizli değer | kapsamı 511.800'den 1.809 kayda daraltma | 5 kaçan, değişmedi / 263,5 dk |
| Raporlama | 30 dk dikkat, açılan adım | öbeklenmiş rapor | 0 kök kaçtı / 255,7 dk |
| Kalite Göstergeleri | dönem başına 180 dk dikkat | karar değiştiren iki gösterge | kaçak oranı %6,25 → %12,50 / 386,8 → 398,0 dk |
| Kök Neden Çözümlemesi | ufuk boyunca 400 dk inceleme | üçüncü halkada durma | 15,20 kaçan / 10,8. sürümde yürürlük |

Son sütundaki iki sıfır bedelini başka sütunda ödüyor. **Her dağıtım bir kaçan hata üretir.**
Sınırlı bir kaynağı bölmenin bölünmemiş bir hâli yoktur; bütçeye sığan plan on sekiz kusur
kaçırır, sığmayan plan zaten koşmaz. Sıfır kaçan hata isteyen bir plan bütçeyi tüketir —
Keşifsel Test dersinde oturum bütçesi iki katına çıkarıldığında kaçan hata 6,27'ye inerken net
bedelin eksiye düşmesi bunun sayılmış hâliydi. Bir dağıtımı savunmanın tek yolu kaçan hatasını
ve geri bildirim süresini birlikte yazmaktır.

Bu kurs, Yazılım Kalitesi ve Test müfredatının beşincisi ve sonuncusuydu. Kalite ve Test
Temelleri kavramı kurdu: kusur, sınıf, seviye ve kâhin. Birim Testi ve Test Odaklı Geliştirme
en ucuz halkayı işledi — en hızlı koşan, en dar gören test. Tümleşim, Sözleşme ve
Uçtan Uca Test gerçek sınırları çalıştırdı ve sahtenin gerçeğe uyup uymadığını sordu. İşlevsel
Olmayan Test yük, saldırı ve arıza altındaki davranışı eşiklerle sınadı. Bu kurs hepsinin
zaman ve dikkat içindeki yerini belirledi: hangi test ne zaman koşar, kırmızı ne karar üretir.
Ortak sonuç tek cümlededir — bir test kümesi ancak bir bütçe içinde anlamlıdır ve o bütçeyi
bölen karar okuyanın kendi ekibinde verilir. Buradaki sayılar o kararın biçimini gösterir,
yerini almaz.

