---
title: 'Mimari Karar ve Dokümantasyon'
source: 'https://academia.sh/tr/kurslar/mimari-dokumantasyon'
language: tr
updated: '2026-08-17T04:42:29+00:00'
license: 'CC BY-SA 4.0'
lessons: 11
outcomes:
    - 'Mimari kararları gerekçesiyle kaydeder'
    - 'Sistemi farklı görünümlerle uygun ayrıntı düzeyinde belgeler'
    - 'Kalite niteliklerini ölçülebilir senaryolara çevirir'
---

# Mimari Karar ve Dokümantasyon

Kararın belleğin dışına çıkarılması: bağlam, karar ve sonuç yapısıyla mimari karar kayıtları, yazılı öneri ve değerlendirme süreci, alternatiflerin nitelik ekseninde puanlandığı ödünleşim çözümlemesi, belirsizliği izleyen risk kaydı, bilinçli ile bilinçsiz teknik borç ayrımı, mantıksal, süreç, geliştirme ve fiziksel görünümler, bağlamdan koda ölçek değiştiren katmanlı diyagram yaklaşımı, hangi diyagramın hangi soruyu yanıtladığı, ölçülebilir kalite nitelik senaryoları, belgenin kodla eşitlenmesi ve belirsizlik bandıyla büyüklük tahmini.

## Karar Kaydı

Kararın izinin bırakılması: bağlam, karar ve sonuç alanlarıyla mimari karar kayıtlarının yapısı, yazılı öneri kültürünün değerlendirme sürecine etkisi, alternatiflerin nitelik ekseninde puanlanarak elenmesi, belirsizliğin olasılık ve etkiyle izlendiği risk kaydı ve bilinçli ile bilinçsiz teknik borcun ayrı ayrı ölçülmesi.

### Mimari Karar Kayıtları

Source: https://academia.sh/tr/kurslar/mimari-dokumantasyon/mimari-karar-kayitlari

Bir kararın bağlam, karar ve sonuç alanlarıyla kayda geçirilmesi: altı ay sonra gelen birinin soruları bir soru kümesi olarak modellenir, aynı karar kümesi kayıtsız ve yapılandırılmış kayıtlı düzende o kümeye uygulanır, yanıtlanan ve yanıtlanamayan soru sayılır, kaydın yazılma maliyeti satır ve dakika olarak ölçülür ve yerine geçilmiş karar alanının eski kararı okunabilir tutup tutmadığı sayılır.

Önceki kurs kararın nasıl verildiğini ölçtü: alternatif–nitelik tablosu bir veri yapısına çevrildi,
ağırlıklar tarandı, kaynağı yazılmamış ağırlık bulundu. O çalışmanın tamamı tek bir yerde duruyor:
kararı verenin belleğinde. Altı ay sonra "bu neden böyle" diye soran birine verilecek bir şey yok.

Bu kursun ölçüsü buradan çıkar. Bir belgenin değeri, sonradan gelen birinin sorduğu soruların
kaçını yanıtladığıdır: yanıtlanan soru sayısı, yanıtlanamayan soru sayısı ve kaydın ödettiği bedel.
"Belge önemlidir" bir karar taşımaz; iki düzen aynı soru kümesinde koşturulur.

#### Soru Kümesi ve Karar Kümesi

Through-line bölgesel bir kütüphane ağının yazılım sistemidir; kurgu bir modeldir. Modelde on dört
şube, dışarıdan alınmış bir katalog sistemi ve içeride yazılmış ödünç servisleri vardır.

**Mimari karar kaydı**, tasarımı anlatmakta kullanılan karar kaydının yapılandırılmış biçimidir:
serbest açıklama yerine adı belli alanları olan bir belge — **bağlam**, **karar**, **sonuç**.
Alanların belli olması ölçmeyi mümkün kılar, çünkü her soru bir alana düşer.

Soru kümesi on sorudur ve her soru bir alanı gösterir; karar kümesi dört karardır. Kayıtsız düzende
yanıt yalnızca çalışan koddan, sürüm geçmişinden ya da kararı verenin belleğinden gelebilir;
kayıtlı düzende alanların hepsi kayıttadır. Alanların dolu hali (**KK1**), kaynak eşlemesi ve
kararı verenin altı ay sonra ulaşılabilir olup olmadığı (**KK2**) modelin girdisidir.

```js
// kayit/model.mjs — soru kumesi ve karar kumesi veri yapisi olarak; kurgu bir modeldir
export const SORU = [
  ["S1", "bu neden boyle", "gerekce"], ["S2", "hangi alternatif elendi", "alternatif"],
  ["S3", "eleme neye dayandi", "olcut"], ["S4", "hangi varsayimla verildi", "varsayim"],
  ["S5", "ne zaman verildi", "tarih"], ["S6", "kim verdi", "veren"],
  ["S7", "bilinen bedeli ne", "sonuc"], ["S8", "hala gecerli mi", "durum"],
  ["S9", "oncekinin yerine mi gecti", "yerine"], ["S10", "bu sayi nereden geldi", "esik"],
].map(([kod, metin, alan]) => ({ kod, metin, alan }));

// Alanlarin dolu hali ve sahibin alti ay sonra ulasilabilirligi modelin girdisidir (KK1, KK2).
export const KARAR = [
  { ad: "odunc-erisim", sahip: false, secim: "subede salt-okunur onbellek",
    gerekce: "sube agi gunde iki kez kopuyor", alternatif: "tek-merkez, sube-kopya, dis-modul",
    olcut: "agirlikli puan taramasi", varsayim: "gunluk hacim onbellege sigar",
    tarih: "2. ceyrek", veren: "platform ekibi", sonuc: "gecersizlestirme bakim ister",
    durum: "gecerli", yerine: "sube-kopya kararinin yerine", esik: "tazelik siniri 90 sn" },
  { ad: "katalog-siniri", sahip: true, secim: "yalniz esleme katmanindan erisim",
    gerekce: "katalog semasi surum basina degisiyor", alternatif: "dogrudan tablo, gorunum okuma",
    olcut: "surum basina kirilan cagri sayisi", varsayim: "disa acik arayuz kararli kalir",
    tarih: "2. ceyrek", veren: "tumlestirme ekibi", sonuc: "her alan iki kez yaziliyor",
    durum: "gecerli", yerine: "yok - ilk karar", esik: null },
  { ad: "ceza-hesabi", sahip: true, secim: "tek kural motorunda toplanir",
    gerekce: "ceza kurali yilda birkac kez degisiyor", alternatif: "katalog ceza modulu, sube ayari",
    olcut: "degisiklikte dokunulan dosya sayisi", varsayim: "kural her subede ayni",
    tarih: "3. ceyrek", veren: "odunc ekibi", sonuc: "istisna gelirse motor buyuyor",
    durum: "gecerli", yerine: "yok - ilk karar", esik: "gecikme esigi 14 gun" },
  { ad: "iade-uyarisi", sahip: false, secim: "kuyruk uzerinden asenkron gonderim",
    gerekce: "gonderim yanit suresine girmemeli", alternatif: "senkron gonderim, gunluk toplu is",
    olcut: "kesintinin odunc islemine yansimasi", varsayim: "bir saatlik gecikme kabul edilebilir",
    tarih: "4. ceyrek", veren: "odunc ekibi", sonuc: "olu mektup kutusu izlenmeli",
    durum: "gecerli", yerine: "yok - ilk karar", esik: "uyari penceresi 3 gun" },
];

// Kayitsiz duzende hangi alan hangi kaynaktan okunabilir (KK2); kayitli duzende hepsi kayitta.
export const KAYNAK = { kod: ["durum"], surum: ["tarih", "veren"],
  bellek: ["gerekce", "varsayim", "sonuc"] };

export const uygulanabilir = (k) => SORU.filter((s) => k[s.alan] !== null);

export function yanitla(k, duzen) {
  return uygulanabilir(k).map((s) => {
    let kaynak = null;
    if (duzen === "kayitli") kaynak = "kayit";
    else if (KAYNAK.kod.includes(s.alan)) kaynak = "kod";
    else if (KAYNAK.surum.includes(s.alan)) kaynak = "surum";
    else if (KAYNAK.bellek.includes(s.alan) && k.sahip) kaynak = "bellek";
    return { soru: s.kod, kaynak, yanitlandi: kaynak !== null };
  });
}

if (import.meta.url.endsWith(process.argv[1].split("/").pop())) {
  const toplam = KARAR.reduce((t, k) => t + uygulanabilir(k).length, 0);
  console.log(`${SORU.length} soru x ${KARAR.length} karar -> uygulanabilir ${toplam} soru`);
  for (const duzen of ["kayitsiz", "kayitli"]) {
    let y = 0;
    const kaynakSay = {};
    for (const k of KARAR)
      for (const s of yanitla(k, duzen).filter((s) => s.yanitlandi))
        { y += 1; kaynakSay[s.kaynak] = (kaynakSay[s.kaynak] ?? 0) + 1; }
    console.log(`[${duzen}] ${y}/${toplam} yanit, ${toplam - y} yanitlanamayan, kaynak: ` +
      Object.entries(kaynakSay).map(([a, n]) => `${a}=${n}`).join(" "));
  }
  console.log("\nkayitsiz duzende soru basina yanit veren karar sayisi:");
  for (const s of SORU) {
    const uygun = KARAR.filter((k) => k[s.alan] !== null);
    const kac = uygun.filter((k) => yanitla(k, "kayitsiz").find((r) => r.soru === s.kod).yanitlandi);
    console.log(`  ${s.kod.padEnd(4)} ${s.metin.padEnd(26)} ${kac.length}/${uygun.length}` +
      `${kac.length ? "" : "   <- hicbir kararda yanit yok"}`);
  }
}
```

```
10 soru x 4 karar -> uygulanabilir 39 soru
[kayitsiz] 18/39 yanit, 21 yanitlanamayan, kaynak: surum=8 kod=4 bellek=6
[kayitli] 39/39 yanit, 0 yanitlanamayan, kaynak: kayit=39

kayitsiz duzende soru basina yanit veren karar sayisi:
  S1   bu neden boyle             2/4
  S2   hangi alternatif elendi    0/4   <- hicbir kararda yanit yok
  S3   eleme neye dayandi         0/4   <- hicbir kararda yanit yok
  S4   hangi varsayimla verildi   2/4
  S5   ne zaman verildi           4/4
  S6   kim verdi                  4/4
  S7   bilinen bedeli ne          2/4
  S8   hala gecerli mi            4/4
  S9   oncekinin yerine mi gecti  0/4   <- hicbir kararda yanit yok
  S10  bu sayi nereden geldi      0/3   <- hicbir kararda yanit yok
```

Kayıtlı düzenin 39/39'u tanım gereğidir: alanlar doluysa her soru yanıtlanır. Bu bir bulgu değil,
ölçüm çubuğunun sıfır noktasıdır.

Kayıtsız düzen 18 soruyu yanıtlıyor ve bunların 12'si sürüm geçmişiyle koddan, yani kimin ne zaman
ne yaptığından geliyor. Gerekçeye, varsayıma ve bedele değinen altı yanıt, tabloda 4 yerine 2 gösteren
S1, S4 ve S7 satırlarındadır: o üç soru yalnızca kararı verenin hâlâ ulaşılabilir olduğu iki kararda
yanıt buluyor. Belleğin kayıt yerine geçmesi bir kişinin işte kalmasına bağlıdır.

Dört soru ise hiçbir kararda yanıt bulmuyor: hangi alternatif elendi, eleme neye dayandı, bu karar
bir öncekinin yerine mi geçti, bu sayı nereden geldi. Bunlar bellekten de gelmiyor; karar anında
yazılmamış bir ölçüt altı ay sonra hatırlanmıyor. Yanıtlanamayan 21 sorunun 15'i bu dörttür.

#### Kaydın Bedeli

Yanıtlanan soru bedelsiz değildir. Aşağıdaki koşum kayıt metnini gerçekten üretir, her alanın kaç
satır tuttuğunu sayar ve satır başına dakikayla çarpar. İki yazma hızı ayrılır: üstbilgi alanları
aranıp yazılır, gövde alanları düşünülerek (**KK3**). Okumada her kaynağın soru başına dakikası
verilir (**KK4**).

```js
// kayit/maliyet.mjs — kaydin yazilma maliyeti ve alan basina getirisi
import { SORU, KARAR, uygulanabilir, yanitla } from "./model.mjs";

const EN = 76, KISA_DK = 0.4, UZUN_DK = 2.0;                 // satir basina dakika (KK3)
const OKUMA = { kayit: 2, kod: 10, surum: 8, bellek: 25 };   // soru basina okuma dakikasi (KK4)
const KISA = ["durum", "tarih", "veren", "yerine"];          // tek satirlik ustbilgi alanlari
const UZUN = ["secim", "gerekce", "varsayim", "esik", "alternatif", "olcut", "sonuc"];

const sar = (m) => m.split(" ").reduce((s, k) =>            // kayit metni gercekten uretilir
  ((s.at(-1) + " " + k).trim().length > EN ? s.push(k) : (s[s.length - 1] += " " + k), s), [""]);

const satir = {}, dk = {};
let tS = 0, tD = 0;
const ekle = (a, n, b) => { satir[a] = (satir[a] ?? 0) + n; dk[a] = (dk[a] ?? 0) + n * b; tS += n; tD += n * b; };
for (const k of KARAR) {
  tS += 1; tD += KISA_DK;                                    // kaydin baslik satiri
  for (const a of KISA) ekle(a, 1, KISA_DK);
  for (const a of UZUN) if (k[a] !== null) ekle(a, 2 + sar(k[a]).length, UZUN_DK);  // bos satir + baslik + govde
}
const N = KARAR.reduce((t, k) => t + uygulanabilir(k).length, 0);
console.log(`${KARAR.length} kayit: ${tS} satir, ${tD.toFixed(0)} dk; kayit basina ` +
  `${(tS / KARAR.length).toFixed(1)} satir, ${(tD / KARAR.length).toFixed(0)} dk`);

const soruSay = Object.fromEntries(SORU.map((s) => [s.alan, KARAR.filter((k) => k[s.alan] !== null).length]));
const kalem = [{ a: "ustbilgi (4 alan)", dk: KISA.reduce((t, a) => t + dk[a], 0),
  soru: KISA.reduce((t, a) => t + (soruSay[a] ?? 0), 0) },
  ...UZUN.filter((a) => soruSay[a]).map((a) => ({ a, dk: dk[a], soru: soruSay[a] }))];
console.log("\nalan, yazma dakikasi, actigi soru (dakika basina getiri sirasiyla):");
let kdk = 0, ks = 0;
for (const r of kalem.sort((x, y) => y.soru / y.dk - x.soru / x.dk)) {
  kdk += r.dk; ks += r.soru;
  console.log(`  ${r.a.padEnd(17)} ${r.dk.toFixed(1).padStart(5)} dk ${r.soru} soru | ` +
    `birikimli ${String(ks).padStart(2)}/${N}, ${kdk.toFixed(1).padStart(5)} dk`);
}
console.log(`  soru kumesine yanit vermeyen bolum: ${(tD - kdk).toFixed(1)} dk (%${(100 * (tD - kdk) / tD).toFixed(0)})`);

const olc = (d, bDk = OKUMA.bellek) => KARAR.reduce((t, k) => t + yanitla(k, d).filter((s) => s.yanitlandi)
  .reduce((u, s) => u + (s.kaynak === "bellek" ? bDk : OKUMA[s.kaynak]), 0), 0);
const say = (d) => KARAR.reduce((t, k) => t + yanitla(k, d).filter((s) => s.yanitlandi).length, 0);
console.log("\nsoru kumesi bir tur sorulunca okuma maliyeti:");
for (const d of ["kayitsiz", "kayitli"])
  console.log(`  ${d.padEnd(9)} ${say(d)} yanit, ${olc(d)} dk, yanit basina ${(olc(d) / say(d)).toFixed(1)} dk`);
const bas = (b) => (tD / (olc("kayitsiz", b) - say("kayitsiz") * OKUMA.kayit)).toFixed(2);
console.log(`basabas (ortak ${say("kayitsiz")} soru): bellek 25 dk ise ${bas(25)} tur, 5 dk ise ${bas(5)} tur`);
```

```
4 kayit: 101 satir, 170 dk; kayit basina 25.3 satir, 43 dk

alan, yazma dakikasi, actigi soru (dakika basina getiri sirasiyla):
  ustbilgi (4 alan)   6.4 dk 16 soru | birikimli 16/39,   6.4 dk
  gerekce            24.0 dk 4 soru | birikimli 20/39,  30.4 dk
  varsayim           24.0 dk 4 soru | birikimli 24/39,  54.4 dk
  esik               18.0 dk 3 soru | birikimli 27/39,  72.4 dk
  alternatif         24.0 dk 4 soru | birikimli 31/39,  96.4 dk
  olcut              24.0 dk 4 soru | birikimli 35/39, 120.4 dk
  sonuc              24.0 dk 4 soru | birikimli 39/39, 144.4 dk
  soru kumesine yanit vermeyen bolum: 25.6 dk (%15)

soru kumesi bir tur sorulunca okuma maliyeti:
  kayitsiz  18 yanit, 254 dk, yanit basina 14.1 dk
  kayitli   39 yanit, 78 dk, yanit basina 2.0 dk
basabas (ortak 18 soru): bellek 25 dk ise 0.78 tur, 5 dk ise 1.73 tur
```

Dört kayıt 101 satır ve 170 dakika. Asıl bilgi toplamda değil, getirinin dağılımındadır.

Dört üstbilgi alanı 6,4 dakikada 16 soru açıyor; kalan 23 soru 138 dakika istiyor. Durum, tarih,
karar veren ve yerine geçilen karar alanları tek satırlıktır ve düşünmeyi değil bakmayı
gerektirir. Kayıt tutmaya başlayan bir ekibin ilk yazacağı şey gerekçe değil, bu dört alandır.

Toplam sürenin yüzde 15'i soru kümesine hiç yanıt vermeyen bölüme gidiyor: kaydın başlığı ve
kararın ne olduğunu anlatan bölüm. Kararın ne olduğu zaten koddan okunabiliyor; o bölüm kaydı
okunabilir kılar, ölçüye girmez, kısa tutulur.

Okumada kayıtsız düzen yanıt başına 14,1, kayıtlı düzen 2,0 dakika harcıyor. Ortak yanıtlanan 18
soru üzerinden yazma maliyeti, bellekten yanıt 25 dakika sürüyorsa 0,78 turda, 5 dakika sürüyorsa
1,73 turda kapanıyor. Aradaki beş kat fark sonucu değiştirmiyor: soru kümesi iki kez sorulduğunda
kayıt her iki uçta da kendini ödemiş oluyor.

#### Yerine Geçilmiş Karar

Kayıt bir kez yazılıp bitmiyor: karar değişince kayda ne olacağı, eski kararın okunabilir kalıp
kalmadığını belirler. Aşağıdaki koşum aynı kararın üç sürümünü (**KK5**) üç kayıt biçiminde
karşılaştırır — güncel kaydın üzerine yazan, üzerine yazıp eski metni sürüm geçmişinden kazıyan ve
her sürümü ayrı kayıt olarak tutup **yerine geçilmiş karar** alanıyla bağlayan biçim. Okuma
dakikaları modelin girdisidir (**KK6**).

```js
// kayit/yerine.mjs — yerine gecilmis karar: eski karar okunabilir kaliyor mu (KK5)
const ZINCIR = [                                     // ayni kararin uc surumu, modelin girdisi
  { ad: "1", secim: "her subede tam kopya", gerekce: "kesinti dayanimi en agir sayildi",
    birakma: "kopya gideri olculunce maliyet agirligi 0.25 cikti", yerine: "2" },
  { ad: "2", secim: "subede salt-okunur onbellek", gerekce: "olculmus agirlikla en yuksek puan",
    birakma: "iki buyuk subede hacim sigayi asti", yerine: "3" },
  { ad: "3", secim: "iki subede kopya, kalanda onbellek", gerekce: "esigi asan sube ikide kaldi",
    birakma: null, yerine: null },
];
const SORU = [["G1", "o zaman ne yapiliyordu", "secim"], ["G2", "neden secilmisti", "gerekce"],
  ["G3", "neden birakildi", "birakma"], ["G4", "yerine ne gecti", "yerine"]];
const KAZILABILIR = ["secim", "gerekce"];            // eski dosya metninde yazili olan alanlar
const BICIM = { "uzerine-yaz": null, "uzerine-yaz-kazi": "kazi", "yerine-gec": "kayit" };
const DK = { kayit: 2, kazi: 12 };                   // soru basina okuma dakikasi (KK6)

const guncel = ZINCIR.length - 1;
const oku = (bicim, i, alan) => {
  const kaynak = i === guncel ? "kayit" : BICIM[bicim];
  return kaynak === "kayit" || (kaynak === "kazi" && KAZILABILIR.includes(alan)) ? kaynak : null;
};
const satirSay = (s) => Object.values(s).filter((v) => v !== null).length;

for (const bicim of Object.keys(BICIM)) {
  const tutulan = BICIM[bicim] === "kayit"
    ? ZINCIR.reduce((t, s) => t + satirSay(s), 0) : satirSay(ZINCIR[guncel]);
  let y = 0, n = 0, dk = 0;
  const eksik = [];
  for (const [i, s] of ZINCIR.entries())
    for (const [kod, , alan] of SORU) {
      if (s[alan] === null) continue;
      n += 1;
      const k = oku(bicim, i, alan);
      if (k) { y += 1; dk += DK[k]; } else eksik.push(`${kod}@${s.ad}`);
    }
  console.log(`${bicim.padEnd(17)} tutulan ${String(tutulan).padStart(2)} satir | ` +
    `${y}/${n} soru, ${dk} dk | yanitlanamayan: ${eksik.join(" ") || "-"}`);
}
```

```
uzerine-yaz       tutulan  3 satir | 2/10 soru, 4 dk | yanitlanamayan: G1@1 G2@1 G3@1 G4@1 G1@2 G2@2 G3@2 G4@2
uzerine-yaz-kazi  tutulan  3 satir | 6/10 soru, 52 dk | yanitlanamayan: G3@1 G4@1 G3@2 G4@2
yerine-gec        tutulan 13 satir | 10/10 soru, 20 dk | yanitlanamayan: -
```

Üzerine yazan biçim 3 satır tutup 10 sorunun 2'sini yanıtlıyor; yerine geçilmiş karar alanını
kullanan biçim 13 satır tutup 10'unu yanıtlıyor. Fark 10 satır ve 8 sorudur, satır başına
neredeyse bir soru.

Kazı satırı tek başına bir bulgudur. Sürüm geçmişi eski metni saklıyor, dolayısıyla "o zaman ne
yapılıyordu" ve "neden seçilmişti" kazıyla yanıtlanabiliyor: 2 yerine 6 yanıt. Bedeli 52 dakika,
yani yanıt başına 8,7 dakika ve kayıttan okumanın dört katı. Kazı iki soruyu ise hiçbir bedelle
açmıyor: "neden bırakıldı" ve "yerine ne geçti". Bu iki alan hiçbir
sürümde yazılmamıştı, çünkü üzerine yazarken kimse bıraktığı kararın gerekçesini yazmaz. Sürüm
geçmişi yazılmış olanı saklar, yazılmamış olanı üretmez.

Geçmiş kararların yeniden gündeme gelmesi de buradan çıkar: bırakılma gerekçesi hiçbir yerde
durmuyorsa, iki yıl önce elenmiş bir alternatif yeni bir öneri olarak geri gelir. Yerine geçilmiş
karar alanının işi eski kararı arşivlemek değil, neden bırakıldığını okunabilir tutmaktır.

#### Özet

- Bir kaydın değeri, soru kümesinden kaç soruyu yanıtladığıyla ölçülür; aynı dört karar kayıtsız
  düzende 39 sorunun 18'ini, yapılandırılmış kayıtla 39'unu yanıtladı.
- O 18 yanıtın 12'si sürüm geçmişiyle koddan geliyor; gerekçeye, varsayıma ve bedele değinen altı
  yanıt yalnızca kararı verenin ulaşılabilir olduğu iki kararda çıktı.
- Dört soru hiçbir kararda yanıt bulmadı: hangi alternatif elendi, eleme neye dayandı, öncekinin
  yerine mi geçti, bu sayı nereden geldi — yanıtlanamayan 21 sorunun 15'i bu dörttür.
- Dört kayıt 101 satır ve 170 dakika tuttu; dört üstbilgi alanı 6,4 dakikada 16 soru açarken kalan
  23 soru 138 dakika istedi, toplamın yüzde 15'i hiç soru yanıtlamayan bölüme gitti.
- Yerine geçilmiş karar alanı 10 satır fazladan tutup 8 fazladan soru yanıtladı; sürüm geçmişini
  kazımak yazılmış alanları yanıt başına 8,7 dakikaya çıkarıyor, hiç yazılmamış bırakma gerekçesini
  ise açmıyor.

#### Sonraki Adım

Kayıt, kararın verildikten sonraki halidir; bu dersin ölçtüğü her şey karar çoktan verilmişken
başladı. Dört kaydın "karar veren" alanında bir ekip adı yazıyor, ama o ekibin kararı nasıl
verdiği — kimin önerdiği, kimin itiraz ettiği, itirazın kararı değiştirip değiştirmediği — kayda
girmiyor. Sözlü onayla geçen karar ile yazılı bir öneri üzerinden değerlendirilen karar aynı kaydı
üretir. Sonraki ders bu farkı ölçer: aynı karar iki süreçte koşturulur ve değerlendirme turunda
kaç kararın yön değiştirdiği sayılır.

### Öneri ve Değerlendirme Süreci

Source: https://academia.sh/tr/kurslar/mimari-dokumantasyon/oneri-ve-degerlendirme-sureci

Kararın verilme biçiminin ölçülmesi: aynı karar kümesi sözlü onayla ve yazılı öneri artı değerlendirme turuyla koşturulur, değerlendirmede yön değiştiren karar oranı, tur sayısı, karara katılan rol sayısı ve geçen süre sayılır, sürecin bıraktığı kayda bir soru kümesi uygulanarak yanıtın doğru, eksik ya da yanlış olduğu ayrılır ve yazılı sürecin yalnızca geciktirdiği kararlar ölçülür.

Önceki ders kararların kayda geçirilmesini ölçtü ve dört kaydın "karar veren" alanında bir ekip
adı bıraktı. O alan kararın kimden çıktığını söylüyor, nasıl çıktığını söylemiyor. Sözlü bir onayla
geçen karar ile bütün ilgili rollere gönderilmiş **yazılı öneri** üzerinden geçen karar aynı kaydı
üretir; ikisinin farkı kayıtta görünmez.

Bu dersin sorusu şudur: sürecin kendisi kararı değiştiriyor mu, değiştiriyorsa hangi bedelle. "Yazılı
süreç iyidir" bir karar taşımaz. Aynı karar kümesi iki düzende koşturulur ve **değerlendirmede yön
değiştiren karar oranı**, tur sayısı, katılan rol sayısı ve geçen süre sayılır.

#### Aynı Karar, Üç Süreç

Modelde sekiz karar vardır ve her kararın arkasında bir ya da iki **bilgi parçası** durur: sistemin
bir yerini bilen birinin bildiği, ama öneriyi yazan kişinin bilmediği bir olgu. Her bilgi parçasının
üç niteliği vardır: hangi rolde durduğu, kaçıncı değerlendirme turunda yüzeye çıktığı ve kararı
çevirip çeviremeyeceği. Bu üçü modelin girdisidir (**KK7**); kurgu bir modeldir.

Süreçler bilgi parçalarına erişimle ayrılır. Sözlü onayda öneriyi veren ile onaylayan iki rol
vardır, dolayısıyla yalnızca o ikisinin bildiği yüzeye çıkar. Yazılı öneri düzeninde öneri altı
role gider. İkinci ayrım tur sayısındadır: bir düzende tur ancak karar değiştiğinde tekrarlanır,
ötekinde en az iki tur koşulur. Süreler ve tur kuralları da modelin girdisidir (**KK8**).

```js
// oneri/surec.mjs — ayni karar kumesi uc surecte: sozlu onay ve iki yazili oneri duzeni
// Kararlar ve bilgi parcalari modelin girdisidir (KK7); kurgu bir modeldir.
export const KARAR = [
  { ad: "uye-verisi-saklama", bilgi: [
    { rol: "isletme", tur: 1, cevirir: true, not: "yonetmelik iki yil sonra silmeyi gerektiriyor" }] },
  { ad: "sube-raporlama", bilgi: [
    { rol: "isletme", tur: 1, cevirir: false, not: "sube ay sonu raporunu gun icinde istiyor" },
    { rol: "butce", tur: 2, cevirir: true, not: "merkezde raporlama ikinci sunucu gerektiriyor" }] },
  { ad: "katalog-yukseltme", bilgi: [
    { rol: "tumlestirme", tur: 1, cevirir: true, not: "yeni surum esleme katmanindaki dort alani kiriyor" },
    { rol: "butce", tur: 1, cevirir: false, not: "yukseltme lisans farki gerektiriyor" }] },
  { ad: "odunc-limiti", bilgi: [
    { rol: "odunc", tur: 1, cevirir: false, not: "limit sube turune gore degisiyor" }] },
  { ad: "arama-dizini", bilgi: [
    { rol: "tumlestirme", tur: 2, cevirir: false, not: "dizin katalogla ayni veriyi tutuyor" }] },
  { ad: "yedekleme-penceresi", bilgi: [
    { rol: "oneren", tur: 1, cevirir: false, not: "pencere disinda yuk dusuyor" }] },
  { ad: "kart-basimi", bilgi: [
    { rol: "butce", tur: 1, cevirir: false, not: "dis basimda birim maliyet daha dusuk" },
    { rol: "isletme", tur: 2, cevirir: true, not: "kart uye kaydiyla ayni gun basilmali" }] },
  { ad: "bildirim-kanali", bilgi: [
    { rol: "odunc", tur: 1, cevirir: true, not: "uyelerin ucte biri kanal secmis durumda" }] },
];

// Sozlu onayda yalnizca toplantidaki iki rolun bildigi yuzeye cikar; yazili duzende
// oneri butun rollere gider. Sureler ve tur kurallari modelin girdisidir (KK8).
export const SUREC = {
  "sozlu-onay": { rol: ["oneren", "yonetici"], yazma: 0, kisiDk: 30, gun: 0, enAz: 1, enCok: 1 },
  "yazili-tek-tur": { rol: ["oneren", "yonetici", "isletme", "odunc", "tumlestirme", "butce"],
    yazma: 60, kisiDk: 20, gun: 2, enAz: 1, enCok: 3 },
  "yazili-iki-tur": { rol: ["oneren", "yonetici", "isletme", "odunc", "tumlestirme", "butce"],
    yazma: 60, kisiDk: 20, gun: 2, enAz: 2, enCok: 3 },
};

export function kostur(karar, surecAdi) {
  const s = SUREC[surecAdi];
  const gorunen = [];
  let tur = 0, degisim = 0;
  while (tur < s.enCok) {
    tur += 1;
    const yeni = karar.bilgi.filter((b) => b.tur === tur && s.rol.includes(b.rol));
    gorunen.push(...yeni);
    if (yeni.some((b) => b.cevirir)) { degisim += 1; continue; }
    if (tur >= s.enAz) break;
  }
  return { tur, degisim, kisiDk: s.yazma + tur * s.rol.length * s.kisiDk, gun: tur * s.gun,
    gorunen, kacan: karar.bilgi.filter((b) => !gorunen.includes(b)) };
}

if (import.meta.url.endsWith(process.argv[1].split("/").pop())) {
  console.log(`${KARAR.length} karar, ${KARAR.flatMap((k) => k.bilgi).length} bilgi parcasi, ` +
    `bunlarin ${KARAR.flatMap((k) => k.bilgi).filter((b) => b.cevirir).length} tanesi karari cevirir`);
  for (const ad of Object.keys(SUREC)) {
    const r = KARAR.map((k) => kostur(k, ad));
    const degisen = r.map((x, i) => x.degisim && KARAR[i].ad).filter(Boolean);
    const kacan = r.flatMap((x) => x.kacan);
    console.log(`\n[${ad}] ${SUREC[ad].rol.length} rol, tur ${SUREC[ad].enAz}-${SUREC[ad].enCok}`);
    console.log(`  degisen karar ${degisen.length}/${KARAR.length}: ${degisen.join(", ") || "-"}`);
    console.log(`  ortalama tur ${(r.reduce((t, x) => t + x.tur, 0) / r.length).toFixed(2)}, ` +
      `toplam ${r.reduce((t, x) => t + x.kisiDk, 0)} kisi-dk, ${r.reduce((t, x) => t + x.gun, 0)} gun`);
    console.log(`  yuzeye cikmayan bilgi ${kacan.length}, karari cevirebilecek olan ` +
      `${kacan.filter((b) => b.cevirir).length}`);
  }
}
```

```
8 karar, 11 bilgi parcasi, bunlarin 5 tanesi karari cevirir

[sozlu-onay] 2 rol, tur 1-1
  degisen karar 0/8: -
  ortalama tur 1.00, toplam 480 kisi-dk, 0 gun
  yuzeye cikmayan bilgi 10, karari cevirebilecek olan 5

[yazili-tek-tur] 6 rol, tur 1-3
  degisen karar 3/8: uye-verisi-saklama, katalog-yukseltme, bildirim-kanali
  ortalama tur 1.38, toplam 1800 kisi-dk, 22 gun
  yuzeye cikmayan bilgi 3, karari cevirebilecek olan 2

[yazili-iki-tur] 6 rol, tur 2-3
  degisen karar 5/8: uye-verisi-saklama, sube-raporlama, katalog-yukseltme, kart-basimi, bildirim-kanali
  ortalama tur 2.25, toplam 2640 kisi-dk, 36 gun
  yuzeye cikmayan bilgi 0, karari cevirebilecek olan 0
```

Sözlü onayda sekiz kararın hiçbiri yön değiştirmiyor. Bunun nedeni kararların doğru olması değil,
kararı çevirebilecek beş bilginin de o odada bulunmamasıdır. Sözlü süreç on bilgi parçasını hiç
görmüyor ve bunların beşi kararı çevirecek olanlardır. Sürecin ürettiği sonuç, katılan rollerin
bildiğiyle sınırlıdır; sınırın nerede olduğu ise süreç bittikten sonra görünmez.

Yazılı öneri düzeni aynı kararlarda sekizin üçünü çeviriyor. Bu üç karar tesadüfen seçilmiş
değildir: her birinde kararı çeviren bilgi, öneriyi yazan kişinin erişemeyeceği bir rolde duruyordu
— yönetmelikte, eşleme katmanında, üye kayıtlarında. Yazılı sürecin yaptığı iş, bir kararı daha iyi
düşünmek değil, kararı çevirebilecek bilginin bulunduğu rolü tartışmaya sokmaktır.

Tek turluk düzenin kaçırdığı iki bilgi ise ayrı bir bulgudur. `sube-raporlama` ve `kart-basimi`
kararlarında birinci turda çıkan bilgi kararı çevirmedi, dolayısıyla ikinci tur hiç açılmadı ve
ikinci turda çıkacak olan iki bilgi görülmedi. En az iki tur koşan düzen aynı kararlarda beş
karar çeviriyor ve kaçan bilgi sıfıra iniyor. Tur sayısı, bilginin yüzeye çıkmasını belirleyen
ayrı bir değişkendir: bir öneriyi bir kez okutmak, iki kez okutmakla aynı şey değildir.

#### Yanıt Yokluğu ve Yanlış Yanıt

Her süreç geriye bir kayıt bırakır ve altı ay sonra gelen biri o kayda soru sorar. Buradaki ölçü
önceki dersinkinden bir adım ileri gider: bir soru yanıtsız kalabileceği gibi **yanlış**
yanıtlanabilir de. Süreç bütün itirazları topladığını sanıyorsa "itiraz yoktu" der; oysa itiraz
sorulmadığı için ortaya çıkmamıştır. Yanıtın hangi durumda sayılacağı modelin girdisidir (**KK9**):
yüzeye çıkmayan bilgi varsa "itiraz var mıydı" sorusunun yanıtı yanlış, "hangi bilgi
değerlendirildi" sorusunun yanıtı eksiktir.

```js
// oneri/soru.mjs — soru kumesi uc surece uygulanir; yanit dogru, eksik ya da yanlis olabilir
import { KARAR, SUREC, kostur } from "./surec.mjs";

// Yanitin durumunu belirleyen kural modelin girdisidir (KK9).
const SORU = [
  ["Y1", "kimler gordu", () => "dogru"],
  ["Y2", "itiraz var miydi", (r) => (r.kacan.length ? "yanlis" : "dogru")],
  ["Y3", "karar neden bu yonde", (r) => (r.kacan.some((b) => b.cevirir) ? "yanlis" : "dogru")],
  ["Y4", "hangi bilgi degerlendirildi", (r) => (r.kacan.length ? "eksik" : "dogru")],
  ["Y5", "kac turda kesinlesti", (r, s) => (s.yazma ? "dogru" : "eksik")],
];

const rapor = {};
for (const ad of Object.keys(SUREC)) {
  const say = { dogru: 0, eksik: 0, yanlis: 0 };
  const soruDurum = {};
  for (const k of KARAR) {
    const r = kostur(k, ad);
    for (const [kod, , kural] of SORU) {
      const d = kural(r, SUREC[ad]);
      say[d] += 1;
      soruDurum[kod] = (soruDurum[kod] ?? []).concat(d);
    }
  }
  const x = KARAR.map((k) => kostur(k, ad));
  rapor[ad] = { say, soruDurum,
    kisiDk: x.reduce((t, y) => t + y.kisiDk, 0), gun: x.reduce((t, y) => t + y.gun, 0),
    degisen: x.filter((y) => y.degisim).length,
    bosKisiDk: x.filter((y) => !y.degisim).reduce((t, y) => t + y.kisiDk, 0),
    bosGun: x.filter((y) => !y.degisim).reduce((t, y) => t + y.gun, 0) };
}

const N = KARAR.length * SORU.length;
console.log(`soru kumesi ${SORU.length} soru x ${KARAR.length} karar = ${N} yanit`);
for (const [ad, r] of Object.entries(rapor))
  console.log(`  ${ad.padEnd(15)} dogru ${String(r.say.dogru).padStart(2)}  eksik ` +
    `${String(r.say.eksik).padStart(2)}  yanlis ${String(r.say.yanlis).padStart(2)}`);

console.log("\nsoru basina dogru olmayan yanit (y = yanlis, e = eksik):");
console.log(" ".repeat(32) + Object.keys(SUREC).map((a) => a.padStart(15)).join(""));
for (const [kod, metin] of SORU)
  console.log(`  ${kod} ${metin.padEnd(27)}` + Object.values(rapor).map((r) => {
    const y = r.soruDurum[kod].filter((d) => d === "yanlis").length;
    const e = r.soruDurum[kod].filter((d) => d === "eksik").length;
    return (y ? `${y}y` : e ? `${e}e` : "-").padStart(15);
  }).join(""));

console.log("\nek maliyetin karsiligi:");
const ad = Object.keys(SUREC);
for (let i = 1; i < ad.length; i++) {
  const a = rapor[ad[i - 1]], b = rapor[ad[i]];
  const ekKarar = b.degisen - a.degisen;
  console.log(`  ${ad[i - 1]} -> ${ad[i]}: +${b.kisiDk - a.kisiDk} kisi-dk, +${b.gun - a.gun} gun, ` +
    `+${ekKarar} karar -> ceviren karar basina ${((b.kisiDk - a.kisiDk) / ekKarar).toFixed(0)} kisi-dk`);
}
console.log("\nkarari degismeyen kararlara harcanan (saf gecikme):");
for (const [a, r] of Object.entries(rapor))
  console.log(`  ${a.padEnd(15)} ${KARAR.length - r.degisen} karar, ${r.bosKisiDk} kisi-dk, ${r.bosGun} gun`);
```

```
soru kumesi 5 soru x 8 karar = 40 yanit
  sozlu-onay      dogru 13  eksik 15  yanlis 12
  yazili-tek-tur  dogru 32  eksik  3  yanlis  5
  yazili-iki-tur  dogru 40  eksik  0  yanlis  0

soru basina dogru olmayan yanit (y = yanlis, e = eksik):
                                     sozlu-onay yazili-tek-tur yazili-iki-tur
  Y1 kimler gordu                             -              -              -
  Y2 itiraz var miydi                        7y             3y              -
  Y3 karar neden bu yonde                    5y             2y              -
  Y4 hangi bilgi degerlendirildi             7e             3e              -
  Y5 kac turda kesinlesti                    8e              -              -

ek maliyetin karsiligi:
  sozlu-onay -> yazili-tek-tur: +1320 kisi-dk, +22 gun, +3 karar -> ceviren karar basina 440 kisi-dk
  yazili-tek-tur -> yazili-iki-tur: +840 kisi-dk, +14 gun, +2 karar -> ceviren karar basina 420 kisi-dk

karari degismeyen kararlara harcanan (saf gecikme):
  sozlu-onay      8 karar, 480 kisi-dk, 0 gun
  yazili-tek-tur  5 karar, 900 kisi-dk, 10 gun
  yazili-iki-tur  3 karar, 900 kisi-dk, 12 gun
```

Kırk yanıtın dağılımı üç süreçte 13, 32 ve 40 doğru veriyor. Ama asıl sayı doğruların değil,
yanlışların sütunudur. Sözlü onay kırk sorunun on ikisine yanlış yanıt veriyor; on beş soruda ise
hiç yanıt yok. Aradaki fark önemlidir: yanıtsız bir soru sorulmaya devam eder, yanlış yanıtlanmış
bir soru kapanır. "Bu karara itiraz eden olmadı" cümlesi, itiraz sorulmadığı için doğru
görünmektedir ve o cümle kayda geçtiğinde kimse bir daha sormaz.

Soru bazındaki tablo yanlışın nerede toplandığını gösteriyor. Y1 üç süreçte de doğru: kimin
katıldığı her düzende bilinir, çünkü katılım sürecin kendisidir. Y2 ve Y3 ise yüzeye çıkmayan
bilgiyle birlikte bozuluyor — sözlü onayda yedi ve beş, tek turlu yazılı düzende üç ve iki karar.
Y4 ve Y5 yanlış değil eksik üretiyor: hangi bilginin değerlendirildiği ve kararın kaç turda
kesinleştiği, kayıt tutulmadığında yanıtsız kalır ama uydurulmaz.

#### Ödenen Bedel ve Saf Gecikme

Yazılı sürecin bedeli çıktının son iki bölümündedir. Sözlü onaydan tek turlu yazılı düzene geçmek
1320 kişi-dakika ve 22 takvim günü ekliyor, karşılığında üç kararı çeviriyor: çevrilen karar başına
440 kişi-dakika. İkinci turu zorunlu kılmak 840 kişi-dakika ve 14 gün daha ekliyor ve iki karar
daha çeviriyor: 420 kişi-dakika. İki geçişin birim maliyeti neredeyse aynı çıkıyor, yani ikinci tur
birinciye göre azalan getirili değildir. Bu modelde tur sayısını artırmanın getirisi, rol sayısını
artırmanınkiyle aynı düzeydedir.

Son tablo madalyonun öteki yüzüdür. Tek turlu düzende beş karar hiç yön değiştirmedi ve o beş karar
900 kişi-dakika ile 10 takvim günü yedi. İki turlu düzende üç karar değişmedi, yine 900 kişi-dakika
ve 12 gün. Bu, sürecin **geciktirdiği ama değiştirmediği** kararların maliyetidir ve yazılı sürece
yöneltilen itirazın ölçülmüş halidir. İtiraz haklıdır: harcanan zamanın önemli bir bölümü hiçbir
kararı çevirmez.

İtirazın yanıtı ise şudur: hangi kararın çevrileceği ancak süreç koştuktan sonra bilinir.
`bildirim-kanali` kararı üç satırlık bir konu gibi görünüyordu ve çevrildi; `arama-dizini` kararı
tartışmalı görünüyordu ve iki turun sonunda aynı kaldı. Saf gecikme, hangi kararın çevrileceğini
önceden bilememenin bedelidir. Ölçülebilir olan da budur: gecikmenin sıfırlanması değil, çevrilen
karar başına düşen kişi-dakikanın bilinmesi.

#### Özet

- Sözlü onay sekiz kararın hiçbirini çevirmedi; nedeni kararların doğruluğu değil, kararı
  çevirebilecek beş bilginin ikişer kişilik odada bulunmamasıdır — on bilgi parçası hiç görülmedi.
- Yazılı öneri düzeni aynı kararların üçünü çevirdi; en az iki tur koşan düzen beşini çevirdi ve
  yüzeye çıkmayan bilgiyi sıfıra indirdi, yani tur sayısı rol sayısından bağımsız bir değişkendir.
- Soru kümesinde doğru yanıt 13, 32 ve 40 çıktı; asıl ayrım yanlış yanıttadır: sözlü onay on iki
  soruya yanlış, on beş soruya eksik yanıt verdi ve yanlış yanıt soruyu kapatır.
- Yanlış yanıtlar "itiraz var mıydı" ile "karar neden bu yönde" sorularında toplandı; kayıt
  tutulmayan alanlar yanlış değil eksik üretti.
- Ek maliyetin karşılığı iki geçişte de aynı düzeydedir: çevrilen karar başına 440 ve 420
  kişi-dakika; buna karşılık tek turlu düzende beş karar 900 kişi-dakika ve 10 günü hiç yön
  değiştirmeden harcadı.

#### Sonraki Adım

Değerlendirme turu bir kararı çevirdiğinde geriye "değişti" bilgisi kalıyor, ama neyin neye tercih
edildiği kalmıyor. `katalog-yukseltme` kararı eşleme katmanındaki dört alan yüzünden çevrildi;
kayıtta bu, tek satırlık bir gerekçedir. Oysa o kararın arkasında birden çok alternatif, birden çok
nitelik ve her alternatifin her nitelikteki durumu vardı. Önceki kursta bu tablonun nasıl
kurulacağı ve ağırlıkların kazananı nasıl belirlediği ölçüldü. Açıkta kalan şey tablonun kendisinin
kayda geçip geçmediğidir: üçüncü bir kişi, kayıttaki tabloya bakarak aynı sonuca varabiliyor mu.
Sonraki ders bunu ölçer.

### Ödünleşim Çözümlemesi

Source: https://academia.sh/tr/kurslar/mimari-dokumantasyon/odunlesim-cozumlemesi

Alternatiflerin nitelik ekseninde puanlandığı tablonun kayda geçmiş halinin ölçülmesi: üçüncü bir kişinin kayıttaki tabloyla aynı sonuca varıp varamadığı sayılır, eksik ölçüt, yazılmamış ağırlık ve kaynağı belirsiz puan üç ayrı kusur olarak ayrılır, her kusur tek tek giderilerek yeniden üretilebilen karar oranı ölçülür ve tamamlamanın dakika cinsinden bedeli kazanılan çözümleme başına hesaplanır.

Önceki ders bir değerlendirme turunun kararı çevirdiğini ölçtü ve geriye "şu bilgi yüzünden
değişti" biçiminde tek satırlık bir gerekçe bıraktı. O satırın arkasında birden çok alternatif,
birden çok nitelik ve her alternatifin her nitelikteki durumu vardı. **Ödünleşim çözümlemesi** bu
tablonun adıdır.

Tablonun nasıl kurulacağı, ağırlıkların kazananı nasıl belirlediği ve ağırlık uzayının taranmasıyla
kararın ne kadar dayanıklı olduğu önceki kursta ölçüldü. Burada tekrarlanmaz. Bu dersin sorusu
başkadır: çözümleme yapıldıktan ve kayda geçtikten sonra, o kaydı okuyan **üçüncü bir kişi** aynı
sonuca varabiliyor mu. Ölçülecek nicelik budur — **yeniden üretilebilirlik**.

#### Kayıttaki Tablo ve Üçüncü Kişi

Bir çözümlemenin yeniden üretilebilmesi için kayıtta dört şeyin bulunması gerekir: alternatifler,
ölçütler, puanlar ve ağırlıklar. Pratikte üç kusur görülür ve üçü farklı biçimde bozar.

**Eksik ölçüt.** Kararda etkili olmuş bir nitelik tabloya hiç girmemiştir. Tablo eksiksiz görünür,
çünkü eksik olan sütunun yeri boş kalmaz — sütun yoktur.

**Yazılmamış ağırlık.** Ölçütler yazılmıştır, ağırlıklar yazılmamıştır. Okuyan kişi bir varsayım
yapmak zorunda kalır ve yapacağı varsayım eşit ağırlıktır; önceki kursta ölçüldüğü gibi eşit
ağırlık da bir ağırlık kümesidir, yalnızca seçildiği söylenmemiştir.

**Kaynağı belirsiz puan.** Puan yazılıdır ama nereden geldiği yazılı değildir. Bu kusur kazananı
değiştirmez, doğrulamayı engeller: okuyan kişi aynı sonuca varır, ama vardığı sonucun doğru olup
olmadığını sınayamaz.

Aşağıdaki model beş çözümleme kaydı içerir. Her kaydın yanında modelin girdisi olarak kayda
girmemiş olan gerçek bilgi durur: eksik ölçütün adı ve puanları ile kararın gerçekten dayandığı
ağırlık vektörü (**KK10**). Üçüncü kişi bunları görmez; yalnızca kaydı okur. Kurgu bir modeldir.

```js
// odun/tablo.mjs — kayda gecmis odunlesim cozumlemeleri; kurgu bir modeldir
// Kayittaki tablo, eksik olan olcut ve gercek agirlik modelin girdisidir (KK10).
export const COZUMLEME = [
  { ad: "arama-dizini", alt: ["katalog-ici", "ayri-servis", "dis-hizmet"],
    olcut: ["yanit-suresi", "isletme-yuku", "maliyet"],
    puan: [[2, 2, 2], [5, 4, 3], [4, 5, 2]], agirlik: null, kaynaksiz: 2,
    eksik: { ad: "veri-tazeligi", puan: [5, 2, 2] }, gercek: [0.20, 0.15, 0.15, 0.50],
    kayitli: "katalog-ici" },
  { ad: "rapor-uretimi", alt: ["merkez", "sube", "gece-toplu"],
    olcut: ["tazelik", "sunucu-yuku", "gelistirme-suresi"],
    puan: [[4, 2, 4], [5, 4, 2], [2, 5, 5]], agirlik: [0.50, 0.30, 0.20], kaynaksiz: 1,
    eksik: null, gercek: [0.50, 0.30, 0.20], kayitli: "sube" },
  { ad: "uye-kimligi", alt: ["merkezi-servis", "sube-tablosu", "gunluk-kopya"],
    olcut: ["tutarlilik", "kesinti-dayanimi", "isletme-yuku"],
    puan: [[5, 2, 3], [2, 5, 4], [3, 4, 2]], agirlik: null, kaynaksiz: 0,
    eksik: null, gercek: [0.55, 0.25, 0.20], kayitli: "merkezi-servis" },
  { ad: "ceza-motoru", alt: ["odunc-servisi", "katalog-modulu", "sube-ayari"],
    olcut: ["degistirilebilirlik", "kurulum-yuku"],
    puan: [[5, 3], [2, 5], [3, 4]], agirlik: [0.60, 0.40], kaynaksiz: 2,
    eksik: { ad: "sube-istisnasi", puan: [2, 3, 5] }, gercek: [0.35, 0.25, 0.40],
    kayitli: "sube-ayari" },
  { ad: "yedekleme-yolu", alt: ["gece-penceresi", "surekli-akis", "haftalik-tam"],
    olcut: ["veri-kaybi-penceresi", "isletme-yuku", "maliyet"],
    puan: [[4, 4, 4], [5, 2, 2], [2, 5, 5]], agirlik: [0.45, 0.30, 0.25], kaynaksiz: 3,
    eksik: null, gercek: [0.45, 0.30, 0.25], kayitli: "gece-penceresi" },
];

export const kazanan = (alt, puan, w) => alt
  .map((ad, i) => ({ ad, toplam: puan[i].reduce((t, p, j) => t + p * w[j], 0) }))
  .sort((a, b) => b.toplam - a.toplam)[0].ad;

export const esit = (n) => Array(n).fill(1 / n);

// Okuyanin elindeki tablo: eksik olcut yok, agirlik yazilmamissa esit varsayilir.
export function okunan(c, duzelt = {}) {
  const olcut = duzelt.olcutEkle && c.eksik ? [...c.olcut, c.eksik.ad] : [...c.olcut];
  const puan = c.alt.map((_, i) => duzelt.olcutEkle && c.eksik
    ? [...c.puan[i], c.eksik.puan[i]] : [...c.puan[i]]);
  let w = duzelt.agirlikYaz ? c.gercek.slice(0, olcut.length)
    : c.agirlik ? c.agirlik.slice(0, olcut.length) : esit(olcut.length);
  while (w.length < olcut.length) w = [...w, 1 / olcut.length];  // eklenen olcut icin varsayilan
  const s = w.reduce((t, x) => t + x, 0);
  return { olcut, puan, w: w.map((x) => x / s) };
}

if (import.meta.url.endsWith(process.argv[1].split("/").pop())) {
  const k = (a) => COZUMLEME.filter(a).length;
  console.log(`${COZUMLEME.length} cozumleme; kusur: eksik olcut ${k((c) => c.eksik)}, ` +
    `yazilmamis agirlik ${k((c) => !c.agirlik)}, kaynagi belirsiz puan ` +
    `${COZUMLEME.reduce((t, c) => t + c.kaynaksiz, 0)}\n`);
  console.log("kayittaki tabloyla yeniden uretim:");
  for (const c of COZUMLEME) {
    const o = okunan(c);
    const kusur = [c.eksik && "eksik olcut", !c.agirlik && "yazilmamis agirlik",
      c.kaynaksiz && `${c.kaynaksiz} kaynaksiz puan`].filter(Boolean);
    console.log(`  ${c.ad.padEnd(15)} kayitli ${c.kayitli.padEnd(15)} okunan ` +
      `${kazanan(c.alt, o.puan, o.w).padEnd(15)} kusur: ${kusur.join(", ") || "-"}`);
  }
  const DUZELTME = [["kayitta oldugu gibi", {}], ["agirlik yazilinca", { agirlikYaz: true }],
    ["eksik olcut eklenince", { olcutEkle: true }],
    ["ikisi de", { agirlikYaz: true, olcutEkle: true }],
    ["kaynaklar da yazilinca", { agirlikYaz: true, olcutEkle: true, kaynakYaz: true }]];
  console.log("\nduzeltme etkisi (yeniden uretilebilen / ayrica dogrulanabilen):");
  for (const [ad, d] of DUZELTME) {
    const uret = COZUMLEME.filter((c) => {
      const o = okunan(c, d);
      return kazanan(c.alt, o.puan, o.w) === c.kayitli;
    });
    const dogrulanan = uret.filter((c) => d.kaynakYaz || c.kaynaksiz === 0);
    console.log(`  ${ad.padEnd(22)} ${uret.length}/${COZUMLEME.length}   ${dogrulanan.length}/${COZUMLEME.length}`);
  }
}
```

```
5 cozumleme; kusur: eksik olcut 2, yazilmamis agirlik 2, kaynagi belirsiz puan 8

kayittaki tabloyla yeniden uretim:
  arama-dizini    kayitli katalog-ici     okunan ayri-servis     kusur: eksik olcut, yazilmamis agirlik, 2 kaynaksiz puan
  rapor-uretimi   kayitli sube            okunan sube            kusur: 1 kaynaksiz puan
  uye-kimligi     kayitli merkezi-servis  okunan sube-tablosu    kusur: yazilmamis agirlik
  ceza-motoru     kayitli sube-ayari      okunan odunc-servisi   kusur: eksik olcut, 2 kaynaksiz puan
  yedekleme-yolu  kayitli gece-penceresi  okunan gece-penceresi  kusur: 3 kaynaksiz puan

duzeltme etkisi (yeniden uretilebilen / ayrica dogrulanabilen):
  kayitta oldugu gibi    2/5   0/5
  agirlik yazilinca      3/5   1/5
  eksik olcut eklenince  3/5   0/5
  ikisi de               5/5   1/5
  kaynaklar da yazilinca 5/5   5/5
```

Beş çözümlemenin ikisi kayıttaki haliyle yeniden üretilebiliyor. Üçünde okuyan kişi kayıtta yazan
karardan farklı bir kazanana varıyor: `arama-dizini` kaydında `katalog-ici` yazıyor ama tablo
`ayri-servis` veriyor, `uye-kimligi` kaydında `merkezi-servis` yazıyor ama tablo `sube-tablosu`
veriyor. Bu, kaydı okuyanın kararı yanlış anlaması değildir; kayıttaki tablo gerçekten o sonucu
üretir. Kararın kendisi doğrudur, kaydı yanlıştır.

Düzeltme tablosu üç kusurun ağırlığını ayırıyor. Ağırlıkları yazmak yeniden üretilebilen çözümleme
sayısını 2'den 3'e çıkarıyor; eksik ölçütü eklemek de 2'den 3'e. Tek başına ikisi de yetmiyor,
çünkü `arama-dizini` her iki kusuru birden taşıyor: ağırlık yazıldığında eksik sütun hâlâ yok,
sütun eklendiğinde ağırlık hâlâ eşit varsayılıyor. İkisi birden giderildiğinde beş çözümlemenin
beşi yeniden üretilebilir oluyor. Kusurlar toplanmıyor, çarpışıyor.

Üçüncü kusurun etkisi ikinci sütundadır. Beş çözümleme yeniden üretilebilir hale geldiğinde bile
yalnızca biri doğrulanabilir: kalan dördünde puanların kaynağı yazılı değil, dolayısıyla okuyan
kişi aynı kazanana varıyor ama puanların doğru olduğunu sınayamıyor. Bu iki durum arasındaki fark
küçük görünüp büyüktür. Yeniden üretilebilen bir çözümleme kararın **tutarlı** olduğunu gösterir;
doğrulanabilen bir çözümleme kararın **dayanaklı** olduğunu gösterir. `yedekleme-yolu` kaydı
kayıttaki haliyle doğru kazananı veriyor, ama üç puanın hiçbirinin nereden geldiği yazılmamış —
üçünün de yanlış olması ve kazananın yine de aynı çıkması mümkündür.

#### Soru Kümesi ve Tamamlama

Aynı beş kayıt bir soru kümesine uygulanır. Yanıt önceki derste ayrıldığı gibi doğru, eksik ya da
yanlış olabilir; ayrım burada özellikle önemlidir, çünkü eksik ölçüt taşıyan bir tablo "hangi
ölçütler kullanıldı" sorusuna **yanlış** yanıt verir: liste eksiktir ama tam görünür.

```js
// odun/soru.mjs — soru kumesi cozumleme kayitlarina uygulanir; tamamlama maliyeti olculur
import { COZUMLEME, kazanan, okunan } from "./tablo.mjs";

const uretilebilir = (c, d) => {
  const o = okunan(c, d);
  return kazanan(c.alt, o.puan, o.w) === c.kayitli;
};
const SORU = [
  ["O1", "hangi olcutler kullanildi", (c, t) => (!t && c.eksik ? "yanlis" : "dogru")],
  ["O2", "olcutlerin agirligi neydi", (c, t) => (!t && !c.agirlik ? "eksik" : "dogru")],
  ["O3", "bu puan nereden geldi", (c, t) => (!t && c.kaynaksiz ? "eksik" : "dogru")],
  ["O4", "ayni tabloyla ayni sonuc cikar mi",
    (c, t) => (uretilebilir(c, t ? { agirlikYaz: true, olcutEkle: true } : {}) ? "dogru" : "yanlis")],
  ["O5", "hangi alternatifler degerlendirildi", () => "dogru"],
];

const durum = {};
for (const tam of [false, true]) {
  const say = { dogru: 0, eksik: 0, yanlis: 0 };
  for (const c of COZUMLEME)
    for (const [kod, , kural] of SORU) {
      const d = kural(c, tam);
      say[d] += 1;
      (durum[kod] ??= { false: [], true: [] })[tam].push(d);
    }
  console.log(`${tam ? "tamamlandiktan sonra" : "kayitta oldugu gibi  "}: ` +
    `dogru ${say.dogru}, eksik ${say.eksik}, yanlis ${say.yanlis} ` +
    `(${SORU.length} soru x ${COZUMLEME.length} cozumleme)`);
}
console.log("\nkayitta oldugu haliyle soru basina dogru olmayan yanit (y = yanlis, e = eksik):");
for (const [kod, metin] of SORU) {
  const d = durum[kod][false];
  const y = d.filter((x) => x === "yanlis").length, e = d.filter((x) => x === "eksik").length;
  console.log(`  ${kod} ${metin.padEnd(36)} ${y ? `${y}y` : e ? `${e}e` : "-"}`);
}

// Tamamlama sureleri modelin girdisidir (KK11).
const DK = { agirlik: 15, olcut: 20, kaynak: 5 };
const say = { agirlik: COZUMLEME.filter((c) => !c.agirlik).length,
  olcut: COZUMLEME.filter((c) => c.eksik).length,
  kaynak: COZUMLEME.reduce((t, c) => t + c.kaynaksiz, 0) };
const toplam = Object.entries(say).reduce((t, [a, n]) => t + n * DK[a], 0);
console.log("\ntamamlama maliyeti:");
for (const [a, n] of Object.entries(say))
  console.log(`  ${a.padEnd(8)} ${n} kalem x ${DK[a]} dk = ${n * DK[a]} dk`);
const once = COZUMLEME.filter((c) => uretilebilir(c, {})).length;
const sonra = COZUMLEME.filter((c) => uretilebilir(c, { agirlikYaz: true, olcutEkle: true })).length;
console.log(`  toplam ${toplam} dk; yeniden uretilebilen ${once}/${COZUMLEME.length} -> ` +
  `${sonra}/${COZUMLEME.length}, kazanilan cozumleme basina ${(toplam / (sonra - once)).toFixed(1)} dk`);
```

```
kayitta oldugu gibi  : dogru 14, eksik 6, yanlis 5 (5 soru x 5 cozumleme)
tamamlandiktan sonra: dogru 25, eksik 0, yanlis 0 (5 soru x 5 cozumleme)

kayitta oldugu haliyle soru basina dogru olmayan yanit (y = yanlis, e = eksik):
  O1 hangi olcutler kullanildi            2y
  O2 olcutlerin agirligi neydi            2e
  O3 bu puan nereden geldi                4e
  O4 ayni tabloyla ayni sonuc cikar mi    3y
  O5 hangi alternatifler degerlendirildi  -

tamamlama maliyeti:
  agirlik  2 kalem x 15 dk = 30 dk
  olcut    2 kalem x 20 dk = 40 dk
  kaynak   8 kalem x 5 dk = 40 dk
  toplam 110 dk; yeniden uretilebilen 2/5 -> 5/5, kazanilan cozumleme basina 36.7 dk
```

Yirmi beş yanıtın on dördü doğru, altısı eksik, beşi yanlış. Yanlışların dağılımı kusurların
niteliğini gösteriyor: eksik ölçüt ve yeniden üretilemezlik yanlış üretiyor, yazılmamış ağırlık ve
kaynağı belirsiz puan eksik üretiyor. Aradaki fark okuyanın davranışını belirler. Ağırlığın
yazılmadığını gören kişi bir şeyin eksik olduğunu bilir ve sorabilir; eksik ölçütü gören kişi
hiçbir şey görmez, çünkü tablo tamdır.

Tamamlamanın bedeli 110 dakika: iki ağırlık vektörü, iki ölçüt sütunu ve sekiz puan kaynağı. Bu
sürenin sonunda yeniden üretilebilen çözümleme sayısı 2'den 5'e çıkıyor, yani kazanılan çözümleme
başına 36,7 dakika. Sayının kendisi modelin girdilerine bağlıdır; taşıdığı bilgi oranlardadır. Puan
kaynaklarını yazmak toplam sürenin yüzde 36'sını alıyor ve yeniden üretilebilirliği hiç
değiştirmiyor — yalnızca doğrulanabilirliği birden beşe çıkarıyor. Ağırlıkları yazmak ise 30
dakikada üç çözümlemeye dokunuyor.

Buradan çıkan sıra şudur: önce eksik ölçüt, sonra ağırlık, en son puan kaynağı. Bu sıra kusurların
ağırlığından değil, fark edilebilirliklerinden gelir. En sinsi kusur en ucuz olanı değil, kendini
göstermeyenidir.

#### Özet

- Bir ödünleşim çözümlemesinin kayda geçmiş hali, üçüncü bir kişinin aynı kazanana varıp
  varamamasıyla ölçülür; beş kaydın ikisi kayıttaki haliyle yeniden üretilebildi.
- Üç kusur farklı biçimde bozar: eksik ölçüt tabloyu tam gösterir, yazılmamış ağırlık okuyanı eşit
  ağırlık varsaymaya iter, kaynağı belirsiz puan kazananı değiştirmeden doğrulamayı engeller.
- Ağırlıkları yazmak yeniden üretilebilen sayıyı 2'den 3'e, eksik ölçütü eklemek de 2'den 3'e
  çıkardı; iki kusuru birden taşıyan çözümleme yalnızca ikisi birden giderildiğinde üretilebilir
  oldu ve sayı 5'e çıktı.
- Yeniden üretilebilirlik ile doğrulanabilirlik ayrı ölçülerdir: beş çözümleme üretilebilir hale
  geldiğinde bile puan kaynakları yazılmadan yalnızca biri doğrulanabiliyordu.
- Soru kümesinde eksik ölçüt ve yeniden üretilemezlik yanlış yanıt, yazılmamış ağırlık ve kaynaksız
  puan eksik yanıt üretti; tamamlamanın bedeli 110 dakika, kazanılan çözümleme başına 36,7 dakika.

#### Sonraki Adım

Buraya kadar ölçülen her şey verilmiş bir kararla ilgiliydi: kararın kaydı, verilme süreci ve
dayandığı tablo. Üçünde de karar bir noktada kesinleşiyor ve kayıt kapanıyor. Oysa her kararın
yanında kapanmayan bir şey kalır: kararın dayandığı varsayımın tutmama ihtimali. `arama-dizini`
çözümlemesinde `veri-tazeligi` ölçütüne 0,50 ağırlık verildi, çünkü tazeliğin kritik olduğu
varsayıldı; o varsayım yanlışsa karar da yanlıştır ve bunu ancak zaman gösterir. Bu, kayda
geçirilecek bir karar değil, izlenecek bir belirsizliktir. Sonraki ders belirsizliği olasılık ve
etkiyle modelleyip bir dönem koşturur, kaydedilen riskin gerçekleşme oranını ve hiç kaydedilmemiş
olduğu halde gerçekleşen riski sayar.

### Risk Kaydı

Source: https://academia.sh/tr/kurslar/mimari-dokumantasyon/risk-kaydi

Belirsizliğin bir kayıt üzerinden izlenmesi: riskler olasılık ve etkiyle modellenir, bir dönem tohumu görünür bir üreteçle koşturulur, kayıttaki olasılık tahmini ile ölçülen gerçekleşme oranı bantlar halinde karşılaştırılır, hiç kaydedilmediği halde gerçekleşen riskler sayılır ve gözden geçirme sıklığının gerçekleşmeden önce yakalanan risk sayısına etkisi kişi-dakika karşılığıyla birlikte ölçülür.

Önceki üç ders verilmiş bir kararın kaydını, verilme sürecini ve dayandığı tabloyu ölçtü. Üçünde de
karar bir noktada kesinleşir ve kayıt kapanır. Kapanmayan bir şey kalır: kararın dayandığı
varsayımın tutmama ihtimali. Ödünleşim çözümlemesinde bir ölçüte 0,50 ağırlık verilmesi, o niteliğin
kritik olduğu varsayımına dayanır; varsayım yanlışsa karar da yanlıştır ve bunu ancak zaman
gösterir.

Bu, kayda geçirilecek bir karar değil, izlenecek bir belirsizliktir ve kendi kaydını gerektirir.
Riskin test bütçesini nasıl böldüğü test planlama konusunda ölçüldü; burada tekrarlanmaz. Buradaki soru kaydın kendisiyle ilgilidir: kayıt tutulduğunda ne yakalanıyor, ne
kaçıyor ve kaydın ne sıklıkta gözden geçirildiği bunu nasıl değiştiriyor.

#### Risk Kaydı ve Bir Dönem

**Risk kaydı** iki alanla modellenir: **olasılık** ve **etki**. Olasılık kayda yazılan bir
tahmindir; etki gerçekleştiğinde ne kadar iş çıkaracağının ölçeğidir. Modeldeki on dört riskin
sekizi kayıtta, altısı kayıt dışıdır — kayıt dışı olanlar sistemde vardır, kimse yazmamıştır.

Her riskin iki olasılığı ayrılır: kayda yazılan **tahmin** ve dönem boyunca gözlenen davranışı
belirleyen gerçek olasılık. İkincisi modelin girdisidir (**KK12**); kayıt tutan kişi onu görmez,
yalnızca dönemler geçtikçe ölçer. Beş yüz dönem koşturulur ve üreteç kendi yazılır, tohum
görünürdür.

```js
// risk/kayit.mjs — risk kaydi olasilik ve etkiyle modellenir, bir donem kosturulur
// [ad, kayittaki tahmin (null ise kayitta yok), gercek olasilik, etki]; hepsi modelin girdisi (KK12)
export const RISK = [
  ["katalog-surum-kirilmasi", 0.60, 0.55, 4],
  ["sube-agi-kopmasi", 0.70, 0.75, 3],
  ["onbellek-tazelik-asimi", 0.50, 0.15, 3],
  ["kimlik-servisi-kesintisi", 0.40, 0.35, 5],
  ["yedekleme-penceresi-tasmasi", 0.30, 0.45, 2],
  ["uye-verisi-uyumsuzlugu", 0.20, 0.20, 5],
  ["kart-basim-gecikmesi", 0.15, 0.10, 2],
  ["kural-motoru-buyumesi", 0.10, 0.50, 3],
  ["arama-dizini-tutarsizligi", null, 0.40, 3],
  ["rapor-sunucusu-yetersizligi", null, 0.35, 4],
  ["bildirim-kuyrugu-birikmesi", null, 0.30, 2],
  ["lisans-yenileme-gecikmesi", null, 0.25, 4],
  ["sube-personeli-egitim-eksigi", null, 0.20, 2],
  ["odunc-limiti-istisnasi", null, 0.15, 1],
].map(([ad, tahmin, gercek, etki]) => ({ ad, tahmin, gercek, etki, kayitli: tahmin !== null }));

export const AY = 12;                    // bir donem
export const rastgele = (tohum) => () => {   // uretec burada yazilir, tohum gorunurdur
  tohum = (tohum + 0x6d2b79f5) | 0;
  let t = Math.imul(tohum ^ (tohum >>> 15), 1 | tohum);
  t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
  return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};

// Bir donem: her risk gercek olasiligiyla gerceklesir, gerceklesirse ayi duzgun dagilir.
export function donem(rnd) {
  return RISK.map((r) => {
    const oldu = rnd() < r.gercek;
    return { risk: r, oldu, ay: oldu ? 1 + Math.floor(rnd() * AY) : null };
  });
}

export const BANT = (p) => (p >= 0.5 ? "yuksek" : p >= 0.2 ? "orta" : "dusuk");

if (import.meta.url.endsWith(process.argv[1].split("/").pop())) {
  const TEKRAR = 500, TOHUM = 20240517, rnd = rastgele(TOHUM);
  const kez = RISK.map(() => 0);
  for (let i = 0; i < TEKRAR; i++)
    donem(rnd).forEach((s, j) => { if (s.oldu) kez[j] += 1; });
  const olculen = kez.map((k) => k / TEKRAR);
  const kayitli = RISK.filter((r) => r.kayitli);
  const toplam = kez.reduce((t, k) => t + k, 0);
  const disi = RISK.reduce((t, r, j) => t + (r.kayitli ? 0 : kez[j]), 0);
  console.log(`${RISK.length} risk: ${kayitli.length} kayitli, ${RISK.length - kayitli.length} ` +
    `kayit disi; ${TEKRAR} donem, tohum ${TOHUM}`);
  console.log(`gerceklesen risk ${toplam}, bunlarin ${disi} tanesi kayit disi ` +
    `(%${(100 * disi / toplam).toFixed(1)})\n`);

  console.log("kayittaki tahmin bandi ile olculen gerceklesme orani:");
  for (const bant of ["yuksek", "orta", "dusuk"]) {
    const g = RISK.map((r, j) => ({ r, o: olculen[j] })).filter((x) => x.r.kayitli && BANT(x.r.tahmin) === bant);
    console.log(`  ${bant.padEnd(7)} ${g.length} risk, ortalama tahmin ` +
      `${(g.reduce((t, x) => t + x.r.tahmin, 0) / g.length).toFixed(2)}, olculen oran ` +
      `${(g.reduce((t, x) => t + x.o, 0) / g.length).toFixed(2)}`);
  }
  console.log("\ntahmini olculen orandan en cok sapan kayitli riskler:");
  for (const x of RISK.map((r, j) => ({ r, o: olculen[j] })).filter((x) => x.r.kayitli)
    .sort((a, b) => Math.abs(b.o - b.r.tahmin) - Math.abs(a.o - a.r.tahmin)).slice(0, 3))
    console.log(`  ${x.r.ad.padEnd(28)} tahmin ${x.r.tahmin.toFixed(2)} olculen ${x.o.toFixed(2)} ` +
      `sapma ${(x.o - x.r.tahmin > 0 ? "+" : "") + (x.o - x.r.tahmin).toFixed(2)} etki ${x.r.etki}`);
  console.log("\nkayit disi kalip gerceklesen riskler (olculen oran x etki):");
  for (const x of RISK.map((r, j) => ({ r, o: olculen[j] })).filter((x) => !x.r.kayitli)
    .sort((a, b) => b.o * b.r.etki - a.o * a.r.etki))
    console.log(`  ${x.r.ad.padEnd(28)} olculen ${x.o.toFixed(2)} etki ${x.r.etki} ` +
      `carpim ${(x.o * x.r.etki).toFixed(2)}`);
}
```

```
14 risk: 8 kayitli, 6 kayit disi; 500 donem, tohum 20240517
gerceklesen risk 2348, bunlarin 808 tanesi kayit disi (%34.4)

kayittaki tahmin bandi ile olculen gerceklesme orani:
  yuksek  3 risk, ortalama tahmin 0.60, olculen oran 0.49
  orta    3 risk, ortalama tahmin 0.30, olculen oran 0.34
  dusuk   2 risk, ortalama tahmin 0.13, olculen oran 0.30

tahmini olculen orandan en cok sapan kayitli riskler:
  kural-motoru-buyumesi        tahmin 0.10 olculen 0.50 sapma +0.40 etki 3
  onbellek-tazelik-asimi       tahmin 0.50 olculen 0.15 sapma -0.35 etki 3
  yedekleme-penceresi-tasmasi  tahmin 0.30 olculen 0.45 sapma +0.15 etki 2

kayit disi kalip gerceklesen riskler (olculen oran x etki):
  rapor-sunucusu-yetersizligi  olculen 0.35 etki 4 carpim 1.41
  arama-dizini-tutarsizligi    olculen 0.39 etki 3 carpim 1.18
  lisans-yenileme-gecikmesi    olculen 0.24 etki 4 carpim 0.98
  bildirim-kuyrugu-birikmesi   olculen 0.29 etki 2 carpim 0.59
  sube-personeli-egitim-eksigi olculen 0.18 etki 2 carpim 0.36
  odunc-limiti-istisnasi       olculen 0.15 etki 1 carpim 0.15
```

İlk sayı kaydın kapsama alanını veriyor: gerçekleşen 2348 riskin 808'i, yani yüzde 34,4'ü kayıtta
hiç yoktu. Risk kaydının ölçülebilir ilk niteliği içindekilerin ne kadar doğru tahmin edildiği
değil, dışında ne kaldığıdır. Sekiz maddelik bir kayda bakan biri, gerçekleşen olayların üçte
birini hiç görmemiş olur.

Bant tablosu ikinci sayıyı veriyor ve kaydın kendi içindeki doğruluğunu gösteriyor. Yüksek bandın
ortalama tahmini 0,60, ölçülen oranı 0,49; düşük bandın tahmini 0,13, ölçülen oranı 0,30. Bantlar
sıralamayı bile korumuyor: düşük diye işaretlenmiş riskler orta banda yakın bir sıklıkla
gerçekleşiyor. Bir risk kaydının olasılık sütunu, ölçülen oranla karşılaştırılmadığı sürece bir
sıralama değil, bir izlenim listesidir.

Sapma tablosu bu bozulmanın nereden geldiğini gösteriyor. `kural-motoru-buyumesi` 0,10 tahmin
edilmişti, 0,50 ölçüldü; `onbellek-tazelik-asimi` 0,50 tahmin edilmişti, 0,15 ölçüldü. İki sapma
zıt yönde ve ikisi de büyük. Kayda bakan biri ikinci riske hazırlanır, birinciyle karşılaşır. Bu,
kaydın gözden geçirilmesinin neden bir formalite olmadığını gösteren sayıdır: tahmin bir kez
yazılıp bırakıldığında yanlışlığı kendini göstermez.

Son tablo kayıt dışı kalanları etkiyle birlikte sıralıyor. En üstteki iki madde,
`rapor-sunucusu-yetersizligi` ve `arama-dizini-tutarsizligi`, çarpım olarak kayıttaki pek çok
maddenin üzerinde. Kayıt dışı kalmalarının nedeni önemsiz olmaları değil, kimsenin akla
getirmemesidir.

#### Gözden Geçirme Sıklığı

Kayıt dışı bir risk kendiliğinden kayda girmez; birinin onu fark etmesi gerekir. Fark etme, kaydın
gözden geçirildiği anlarda olur. Aşağıdaki koşum dört sıklığı aynı dönemler üzerinde karşılaştırır:
hiç gözden geçirmemek, yılda bir, üç ayda bir ve ayda bir. Bir gözden geçirmede kayıt dışı bir
riskin fark edilme olasılığı ile gözden geçirmenin kişi-dakikası modelin girdisidir (**KK13**).

Ölçülen şey fark edilen risk sayısı değil, **gerçekleşmeden önce** fark edilen risk sayısıdır. Bir
riski gerçekleştikten sonra kayda geçirmek kaydı düzeltir, olayı önlemez.

```js
// risk/gozden.mjs — gozden gecirme sikligi kac riski gerceklesmeden once yakaliyor
import { RISK, AY, rastgele, donem } from "./kayit.mjs";

const FARK = 0.20;        // bir gozden gecirmede kayit disi bir riskin fark edilme olasiligi (KK13)
const KISI_DK = 45;       // bir gozden gecirmenin kisi-dakikasi (KK13)
const SIKLIK = {
  hic: [], yillik: [12], "uc-aylik": [3, 6, 9, 12], aylik: [...Array(AY)].map((_, i) => i + 1),
};

const TEKRAR = 500, TOHUM = 20240518;
console.log(`${TEKRAR} donem, tohum ${TOHUM}; fark etme olasiligi ${FARK}, ` +
  `gozden gecirme basina ${KISI_DK} kisi-dk`);
console.log("siklik      gecirme  fark edilen  onceden yakalanan  kayit disi gerceklesen  kisi-dk");

const sonuc = {};
for (const [ad, aylar] of Object.entries(SIKLIK)) {
  const rnd = rastgele(TOHUM);       // her siklik ayni tohumla, ayni donemler uzerinde kosar
  let farkEdilen = 0, once = 0, kacan = 0;
  for (let i = 0; i < TEKRAR; i++)
    for (const s of donem(rnd)) {
      if (s.risk.kayitli) continue;
      let bulunduAy = null;
      for (const m of aylar) if (rnd() < FARK) { bulunduAy = m; break; }
      if (bulunduAy !== null) farkEdilen += 1;
      if (s.oldu && (bulunduAy === null || bulunduAy >= s.ay)) kacan += 1;
      else if (s.oldu) once += 1;
    }
  const kisiDk = aylar.length * KISI_DK;
  sonuc[ad] = { once: once / TEKRAR, kacan: kacan / TEKRAR, kisiDk, fark: farkEdilen / TEKRAR };
  console.log(`  ${ad.padEnd(10)} ${String(aylar.length).padStart(4)} ` +
    `${(farkEdilen / TEKRAR).toFixed(2).padStart(11)} ${(once / TEKRAR).toFixed(2).padStart(17)} ` +
    `${(kacan / TEKRAR).toFixed(2).padStart(22)} ${String(kisiDk).padStart(9)}`);
}

console.log("\nsikligi artirmanin karsiligi (donem basina):");
const ad = Object.keys(SIKLIK);
for (let i = 1; i < ad.length; i++) {
  const a = sonuc[ad[i - 1]], b = sonuc[ad[i]];
  const ek = b.once - a.once;
  console.log(`  ${ad[i - 1]} -> ${ad[i]}: +${(b.kisiDk - a.kisiDk)} kisi-dk, ` +
    `+${ek.toFixed(2)} onceden yakalanan risk -> risk basina ` +
    `${ek > 0 ? `${((b.kisiDk - a.kisiDk) / ek).toFixed(0)} kisi-dk` : "karsiligi yok"}`);
}

// Soru kumesi: 14 risk x 3 soru; donem sonunda kayitta olan risk sayisina gore beklenen deger
const KAYITLI = RISK.filter((r) => r.kayitli).length, N = RISK.length;
console.log(`\nsoru kumesi (${N} risk x 3 soru = ${3 * N} yanit; R1 bu risk dusunuldu mu, ` +
  `R2 olasiligi ne kabul edilmisti, R3 tahmin tuttu mu):`);
for (const [ad, aylar] of Object.entries(SIKLIK)) {
  const kayitta = KAYITLI + sonuc[ad].fark, disi = N - kayitta;
  const dogru = kayitta * (aylar.length ? 3 : 2);
  const eksik = disi * 2 + (aylar.length ? 0 : kayitta);
  console.log(`  ${ad.padEnd(10)} kayitta ${kayitta.toFixed(2)}/${N} risk -> dogru ` +
    `${dogru.toFixed(1)}, eksik ${eksik.toFixed(1)}, yanlis ${disi.toFixed(1)}`);
}
```

```
500 donem, tohum 20240518; fark etme olasiligi 0.2, gozden gecirme basina 45 kisi-dk
siklik      gecirme  fark edilen  onceden yakalanan  kayit disi gerceklesen  kisi-dk
  hic           0        0.00              0.00                   1.67         0
  yillik        1        1.26              0.00                   1.63        45
  uc-aylik      4        3.54              0.44                   1.22       180
  aylik        12        5.62              1.03                   0.59       540

sikligi artirmanin karsiligi (donem basina):
  hic -> yillik: +45 kisi-dk, +0.00 onceden yakalanan risk -> risk basina karsiligi yok
  yillik -> uc-aylik: +135 kisi-dk, +0.44 onceden yakalanan risk -> risk basina 310 kisi-dk
  uc-aylik -> aylik: +360 kisi-dk, +0.60 onceden yakalanan risk -> risk basina 602 kisi-dk

soru kumesi (14 risk x 3 soru = 42 yanit; R1 bu risk dusunuldu mu, R2 olasiligi ne kabul edilmisti, R3 tahmin tuttu mu):
  hic        kayitta 8.00/14 risk -> dogru 16.0, eksik 20.0, yanlis 6.0
  yillik     kayitta 9.26/14 risk -> dogru 27.8, eksik 9.5, yanlis 4.7
  uc-aylik   kayitta 11.54/14 risk -> dogru 34.6, eksik 4.9, yanlis 2.5
  aylik      kayitta 13.62/14 risk -> dogru 40.9, eksik 0.8, yanlis 0.4
```

Yıllık gözden geçirme dönem başına 1,26 risk fark ediyor ve bunların hiçbirini gerçekleşmeden önce
yakalamıyor. Sayı sıfır olduğu için tesadüf değildir: gözden geçirme on ikinci ayda, gerçekleşme
ise on iki aya yayılmış durumda, dolayısıyla fark etme neredeyse her zaman olaydan sonra geliyor.
Yılda bir gözden geçirilen bir risk kaydı, sonraki dönem için doğru bir kayıt üretir ve içinde
bulunulan dönem için hiçbir şey yapmaz.

Üç aylık sıklık 0,44, aylık sıklık 1,03 riski gerçekleşmeden önce yakalıyor; kayıt dışı kalıp
gerçekleşen risk 1,67'den 0,59'a iniyor. Karşılığı ise artan bir eğridir: üç aylığa geçmek önceden
yakalanan risk başına 310 kişi-dakika, aylığa geçmek 602 kişi-dakika. İkinci geçişin birim maliyeti
birincinin iki katına yakın, çünkü kolay fark edilen riskler daha seyrek gözden geçirmede zaten
yakalanmıştır. Sıklığı artırmanın getirisi azalır; nerede durulacağı, riskin etkisiyle kişi-dakikanın
karşılaştırılabildiği bir soruya dönüşür.

Soru kümesi tablosu aynı eğriyi kaydın okunabilirliği tarafında gösteriyor. Kırk iki yanıttan
gözden geçirilmeyen kayıtta 16'sı doğru, 20'si eksik, 6'sı yanlıştır. Yanlış olanlar "bu risk
düşünüldü mü" sorusuna verilen yanıtlardır: kayıt dışı bir risk için verilecek yanıt "hayır" olur
ve bu yanıt doğru görünür, çünkü kayıt gerçekten sessizdir. Aylık gözden geçirmede doğru yanıt
40,9'a çıkıyor. Kaydın değeri, içine bir kez yazılanla değil, ne sıklıkta yeniden okunduğuyla
belirleniyor.

#### Özet

- Gerçekleşen 2348 riskin 808'i, yani yüzde 34,4'ü kayıtta hiç yoktu; bir risk kaydının ilk ölçüsü
  içindekilerin doğruluğu değil, dışında kalanın büyüklüğüdür.
- Kayıttaki bantlar sıralamayı korumadı: yüksek bandın tahmini 0,60 iken ölçülen oranı 0,49, düşük
  bandın tahmini 0,13 iken ölçülen oranı 0,30 çıktı.
- İki büyük sapma zıt yönlüdür — `kural-motoru-buyumesi` 0,10 tahminle 0,50 ölçüldü,
  `onbellek-tazelik-asimi` 0,50 tahminle 0,15; bir kez yazılıp bırakılan tahmin yanlışlığını
  göstermez.
- Yıllık gözden geçirme dönem başına 1,26 risk fark etti ve hiçbirini gerçekleşmeden önce
  yakalamadı; üç aylık 0,44, aylık 1,03 risk yakaladı ve kaçan risk 1,67'den 0,59'a indi.
- Sıklığı artırmanın birim bedeli yükseliyor: önceden yakalanan risk başına 310 ve 602 kişi-dakika;
  soru kümesinde doğru yanıt 16'dan 40,9'a çıkarken yanlış yanıt 6'dan 0,4'e indi.

#### Sonraki Adım

Risk kaydı gerçekleşmemiş bir şeyi izler: olasılığı olan, henüz olmamış bir olay. Yanında bir
kayıt daha durur ve o, çoktan gerçekleşmiş bir şeyi izler. Bir karar verildiğinde bazen bilerek
eksik bir çözüm seçilir — hızlı çıkmak için, ya da doğru çözümün ne olduğu henüz bilinmediği için.
O eksiklik sistemde kalır ve her değişiklikte biraz fazladan iş çıkarır. Bazen de eksiklik bilerek
seçilmez, sonradan fark edilir. Sonraki ders bu iki durumu ayırır, aradaki farkın ölçülebilir olup
olmadığını sınar ve eksikliğin değişiklik başına çıkardığı fazladan işi sayar.

### Teknik Borç Yönetimi

Source: https://academia.sh/tr/kurslar/mimari-dokumantasyon/teknik-borc-yonetimi

Bilinçli ve bilinçsiz borcun ayrılması ve ölçülmesi: borç kalemleri iki sınıfa bölünür, borç faizi aynı değişikliğin borçlu ve borçsuz modülde çıkardığı iş farkı olarak modellenir, kalem başına yıllık faiz ve modül başına fazladan iş oranı hesaplanır, bilinçsiz borcun fark edilme gecikmesi tekrar eşiğine bağlı olarak ölçülür ve o gecikme boyunca fark edilmeden ödenen faiz sayılır.

Risk kaydı gerçekleşmemiş bir şeyi izler: olasılığı olan, henüz olmamış bir olay. Yanında ikinci
bir kayıt durur ve o, çoktan gerçekleşmiş bir şeyi izler. Bir karar verilirken bazen bilerek eksik
bir çözüm seçilir; eksiklik sistemde kalır ve her değişiklikte fazladan iş çıkarır. Bu eksikliğin
adı **teknik borç**tur.

Teknik borcun bir gösterge olarak nasıl tanımlandığı kalite göstergeleri konusunda ele alındı;
burada tekrarlanmaz. Bu dersin iki sorusu vardır. Birincisi: borç kalemleri iki sınıfa ayrıldığında
— bilerek alınan ve farkında olmadan alınan — bu ayrım ölçülebilir bir fark yaratıyor mu. İkincisi:
**borç faizi** nedir ve nasıl sayılır.

#### İki Sınıf ve Faiz

**Bilinçli borç**, karar anında bilinerek seçilmiş eksikliktir: hızlı çıkmak için, ya da doğru
çözümün ne olduğu henüz bilinmediği için. Kayda geçirilebilir, çünkü onu seçen kişi seçtiğini
bilir. **Bilinçsiz borç** ise seçilmemiştir; bir varsayımın yanlış olduğu, bir kopyanın iki yere
düştüğü sonradan anlaşılır. Kayda geçirilemez, çünkü kayıt yazılırken kimse onun farkında değildir.

Faizi ölçmenin yolu tek bir soruya indirgenir: aynı değişiklik borçlu modülde, borçsuz modüldekine
göre kaç dakika fazla sürüyor. Bu, borcun "kötü" olduğunu söylemez; ne kadar kötü olduğunu söyler.
Aşağıdaki modelde beş değişiklik türü, taban süreleri ve yıllık sayıları modelin girdisidir
(**KK14**). Her borç kalemi hangi değişiklik türlerini etkilediğini ve o türde kaç dakika ek iş
çıkardığını taşır. Kurgu bir modeldir.

```js
// borc/faiz.mjs — borc kalemleri iki sinifa ayrilir, faiz degisiklik basina eklenen is olarak olculur
// Degisiklik turleri, taban sureler ve yillik sayilar modelin girdisidir (KK14); kurgu bir modeldir.
export const DEGISIKLIK = [
  { tur: "kural-degisikligi", taban: 120, yillik: 12 },
  { tur: "yeni-alan", taban: 90, yillik: 8 },
  { tur: "yeni-ekran", taban: 240, yillik: 6 },
  { tur: "hata-duzeltme", taban: 60, yillik: 30 },
  { tur: "basarim-ayari", taban: 150, yillik: 4 },
];

// ekIs: ayni degisiklik borclu modulde borcsuz moduldekine gore kac dakika fazla suruyor
export const KALEM = [
  { ad: "kural-kopyasi", sinif: "bilincli", modul: "odunc", ekIs: 35,
    etkiler: ["kural-degisikligi", "hata-duzeltme"] },
  { ad: "elle-alan-eslemesi", sinif: "bilincli", modul: "katalog-esleme", ekIs: 45,
    etkiler: ["yeni-alan", "hata-duzeltme"] },
  { ad: "gecici-uyelik-tablosu", sinif: "bilincli", modul: "uyelik", ekIs: 20,
    etkiler: ["yeni-alan", "yeni-ekran"] },
  { ad: "sabit-rapor-sorgusu", sinif: "bilincli", modul: "raporlama", ekIs: 30,
    etkiler: ["yeni-ekran"] },
  { ad: "testsiz-ceza-dali", sinif: "bilincsiz", modul: "odunc", ekIs: 40,
    etkiler: ["kural-degisikligi", "hata-duzeltme", "basarim-ayari"] },
  { ad: "iki-yerde-tarih-bicimi", sinif: "bilincsiz", modul: "katalog-esleme", ekIs: 25,
    etkiler: ["yeni-alan", "hata-duzeltme"] },
  { ad: "gizli-sube-varsayimi", sinif: "bilincsiz", modul: "uyelik", ekIs: 55,
    etkiler: ["yeni-ekran", "hata-duzeltme"] },
  { ad: "kopyalanmis-yetki-kontrolu", sinif: "bilincsiz", modul: "raporlama", ekIs: 35,
    etkiler: ["yeni-ekran", "yeni-alan", "hata-duzeltme"] },
  { ad: "elle-onbellek-temizligi", sinif: "bilincsiz", modul: "odunc", ekIs: 15,
    etkiler: ["basarim-ayari", "hata-duzeltme"] },
];

export const MODUL = [...new Set(KALEM.map((k) => k.modul))];
export const yillik = (tur) => DEGISIKLIK.find((d) => d.tur === tur).yillik;
export const kalemFaizi = (k) => k.ekIs * k.etkiler.reduce((t, tur) => t + yillik(tur), 0);
export const tabanIs = DEGISIKLIK.reduce((t, d) => t + d.taban * d.yillik, 0);  // modul basina

if (import.meta.url.endsWith(process.argv[1].split("/").pop())) {
  const bilincli = KALEM.filter((k) => k.sinif === "bilincli");
  console.log(`${KALEM.length} borc kalemi: ${bilincli.length} bilincli (kayitli), ` +
    `${KALEM.length - bilincli.length} bilincsiz (kayit disi)`);
  console.log(`modul basina yilda ${DEGISIKLIK.reduce((t, d) => t + d.yillik, 0)} degisiklik, ` +
    `borcsuz modulde ${tabanIs} dk\n`);

  console.log("kalem basina yillik faiz (degisiklik basina ek is x yillik degisiklik sayisi):");
  for (const k of [...KALEM].sort((a, b) => kalemFaizi(b) - kalemFaizi(a)))
    console.log(`  ${k.ad.padEnd(26)} ${k.sinif.padEnd(10)} ${k.modul.padEnd(15)} ` +
      `${String(k.ekIs).padStart(2)} dk x ${String(k.etkiler.reduce((t, tur) => t + yillik(tur), 0)).padStart(2)} ` +
      `degisiklik = ${String(kalemFaizi(k)).padStart(4)} dk/yil`);

  console.log("\nsinif basina toplam faiz:");
  for (const s of ["bilincli", "bilincsiz"]) {
    const g = KALEM.filter((k) => k.sinif === s);
    const f = g.reduce((t, k) => t + kalemFaizi(k), 0);
    console.log(`  ${s.padEnd(10)} ${g.length} kalem, ${f} dk/yil, kalem basina ${(f / g.length).toFixed(0)} dk`);
  }

  console.log("\nmodul basina yillik is (taban + faiz):");
  for (const m of MODUL) {
    const f = KALEM.filter((k) => k.modul === m).reduce((t, k) => t + kalemFaizi(k), 0);
    console.log(`  ${m.padEnd(15)} taban ${tabanIs.toFixed(0)} + faiz ${String(f).padStart(4)} = ` +
      `${(tabanIs + f).toFixed(0)} dk  (%${(100 * f / tabanIs).toFixed(1)} fazla)`);
  }
}
```

```
9 borc kalemi: 4 bilincli (kayitli), 5 bilincsiz (kayit disi)
modul basina yilda 60 degisiklik, borcsuz modulde 6000 dk

kalem basina yillik faiz (degisiklik basina ek is x yillik degisiklik sayisi):
  gizli-sube-varsayimi       bilincsiz  uyelik          55 dk x 36 degisiklik = 1980 dk/yil
  testsiz-ceza-dali          bilincsiz  odunc           40 dk x 46 degisiklik = 1840 dk/yil
  elle-alan-eslemesi         bilincli   katalog-esleme  45 dk x 38 degisiklik = 1710 dk/yil
  kopyalanmis-yetki-kontrolu bilincsiz  raporlama       35 dk x 44 degisiklik = 1540 dk/yil
  kural-kopyasi              bilincli   odunc           35 dk x 42 degisiklik = 1470 dk/yil
  iki-yerde-tarih-bicimi     bilincsiz  katalog-esleme  25 dk x 38 degisiklik =  950 dk/yil
  elle-onbellek-temizligi    bilincsiz  odunc           15 dk x 34 degisiklik =  510 dk/yil
  gecici-uyelik-tablosu      bilincli   uyelik          20 dk x 14 degisiklik =  280 dk/yil
  sabit-rapor-sorgusu        bilincli   raporlama       30 dk x  6 degisiklik =  180 dk/yil

sinif basina toplam faiz:
  bilincli   4 kalem, 3640 dk/yil, kalem basina 910 dk
  bilincsiz  5 kalem, 6820 dk/yil, kalem basina 1364 dk

modul basina yillik is (taban + faiz):
  odunc           taban 6000 + faiz 3820 = 9820 dk  (%63.7 fazla)
  katalog-esleme  taban 6000 + faiz 2660 = 8660 dk  (%44.3 fazla)
  uyelik          taban 6000 + faiz 2260 = 8260 dk  (%37.7 fazla)
  raporlama       taban 6000 + faiz 1720 = 7720 dk  (%28.7 fazla)
```

Kalem tablosunun ilk bilgisi sıralamadadır: en yüksek faizli kalem, değişiklik başına en çok iş
çıkaran kalem değildir. `gecici-uyelik-tablosu` değişiklik başına 20 dakika ekliyor ve yılda 280
dakika tutuyor; `elle-onbellek-temizligi` 15 dakika ekliyor ve 510 dakika tutuyor. Fark, kalemin
kaç değişikliğe dokunduğundadır. Bir borç kaleminin ağırlığı ne kadar çirkin olduğuyla değil, ne
sıklıkta yola çıktığıyla ölçülür.

Sınıf tablosu ikinci bilgiyi veriyor. Beş bilinçsiz kalem yılda 6820 dakika, dört bilinçli kalem
3640 dakika tutuyor; kalem başına 1364'e karşı 910 dakika. Bilinçsiz borç kalem başına daha
pahalıdır. Nedeni ahlaki değil yapısaldır: bilinçli borç bir kararın parçası olarak alınır ve
alınırken kapsamı düşünülür, bilinçsiz borç ise kimse bakmadan büyür. Ölçülen fark bu iki durumun
aynı şey olmadığının sayısal karşılığıdır.

Modül tablosu faizi taban işle karşılaştırıyor. `odunc` modülünde aynı yıllık değişiklik kümesi
9820 dakika sürüyor, borçsuz bir modülde 6000; fazladan iş yüzde 63,7. `raporlama` modülünde aynı
oran yüzde 28,7. İki modül aynı işi yapmıyor sayılmaz — tam olarak aynı değişiklik kümesi
koşturuldu. Aradaki farkın tamamı borçtur.

#### Fark Edilme Gecikmesi

Bilinçli borcun kayda geçme gecikmesi sıfırdır: seçildiği anda bilinir. Bilinçsiz borç ise bir
noktada fark edilir ve o noktaya kadar faizi ödenmeye devam eder. Fark etme, tek bir acı verici
değişiklikle değil, aynı fazladan işin tekrarlanmasıyla olur — biri iki farklı işte aynı yerde
takıldığında bir örüntü görür. Modelde bir kalem, aynı fazladan işi eşik sayıda kez çıkardığında
fark edilir (**KK15**). Bu, gecikmenin kalemin büyüklüğüne değil, dokunduğu değişikliklerin
sıklığına bağlanması demektir.

```js
// borc/gecikme.mjs — bilincsiz borcun fark edilme gecikmesi ve o ana kadar odenen faiz
import { KALEM, yillik, kalemFaizi } from "./faiz.mjs";

// Bir kalem, ayni fazladan isi ESIK kez cikardiginda fark edilir; degisiklikler yil icinde
// duzgun dagilir. Ikisi de modelin girdisidir (KK15).
const ESIK = [8, 24, 48];
const AY = 12;
const etkilenen = (k) => k.etkiler.reduce((t, tur) => t + yillik(tur), 0);
const gecikme = (k, esik) => (AY * esik) / etkilenen(k);
const bilincsiz = KALEM.filter((k) => k.sinif === "bilincsiz");

console.log(`${bilincsiz.length} bilincsiz kalem; bilincli borc kayda yazildigi icin gecikmesi 0 ay`);
console.log("esik  ortalama gecikme  12. ayda fark edilmemis  fark edilmeden odenen faiz  yillik faizin payi");
const yillikFaiz = bilincsiz.reduce((t, k) => t + kalemFaizi(k), 0);
for (const esik of ESIK) {
  const g = bilincsiz.map((k) => gecikme(k, esik));
  const odenen = bilincsiz.reduce((t, k) => t + esik * k.ekIs, 0);
  console.log(`  ${String(esik).padStart(2)}  ${(g.reduce((a, b) => a + b, 0) / g.length).toFixed(1).padStart(13)} ay ` +
    `${String(g.filter((x) => x > AY).length).padStart(20)}  ${String(odenen).padStart(21)} dk ` +
    `${("%" + (100 * odenen / yillikFaiz).toFixed(0)).padStart(15)}`);
}

console.log(`\nkalem basina (esik 24; yillik bilincsiz faiz ${yillikFaiz} dk):`);
for (const k of [...bilincsiz].sort((a, b) => gecikme(a, 24) - gecikme(b, 24)))
  console.log(`  ${k.ad.padEnd(26)} ${String(etkilenen(k)).padStart(2)} degisiklik/yil, ` +
    `${gecikme(k, 24).toFixed(1).padStart(4)} ay sonra fark edilir, o ana kadar ${24 * k.ekIs} dk odenir`);

// Soru kumesi: 9 kalem x 4 soru; 12. ayda kayitta olan kalemlere gore
const SORU = ["B1 bu eksiklik bilerek mi secildi", "B2 ne zaman girdi",
  "B3 geri donus kosulu neydi", "B4 degisiklik basina ne kadar is cikariyor"];
console.log(`\nsoru kumesi 12. ayda (${KALEM.length} kalem x ${SORU.length} soru = ` +
  `${KALEM.length * SORU.length} yanit):`);
// Bilincli kalem dort soruyu da yanitlar; fark edilmis bilincsiz kalem B1 ve B4'u yanitlar,
// B2 ve B3 eksik kalir; hic fark edilmemis kalem B1'e yanlis yanit verir, kalani eksiktir.
const B = KALEM.length - bilincsiz.length;
for (const esik of ESIK) {
  const bulunan = bilincsiz.filter((k) => gecikme(k, esik) <= AY).length;
  const disi = bilincsiz.length - bulunan;
  console.log(`  esik ${String(esik).padStart(2)}: kayitta ${B + bulunan}/${KALEM.length} kalem -> ` +
    `dogru ${4 * B + 2 * bulunan}, eksik ${2 * bulunan + 3 * disi}, yanlis ${disi}`);
}
```

```
5 bilincsiz kalem; bilincli borc kayda yazildigi icin gecikmesi 0 ay
esik  ortalama gecikme  12. ayda fark edilmemis  fark edilmeden odenen faiz  yillik faizin payi
   8            2.5 ay                    0                   1360 dk             %20
  24            7.4 ay                    0                   4080 dk             %60
  48           14.7 ay                    5                   8160 dk            %120

kalem basina (esik 24; yillik bilincsiz faiz 6820 dk):
  testsiz-ceza-dali          46 degisiklik/yil,  6.3 ay sonra fark edilir, o ana kadar 960 dk odenir
  kopyalanmis-yetki-kontrolu 44 degisiklik/yil,  6.5 ay sonra fark edilir, o ana kadar 840 dk odenir
  iki-yerde-tarih-bicimi     38 degisiklik/yil,  7.6 ay sonra fark edilir, o ana kadar 600 dk odenir
  gizli-sube-varsayimi       36 degisiklik/yil,  8.0 ay sonra fark edilir, o ana kadar 1320 dk odenir
  elle-onbellek-temizligi    34 degisiklik/yil,  8.5 ay sonra fark edilir, o ana kadar 360 dk odenir

soru kumesi 12. ayda (9 kalem x 4 soru = 36 yanit):
  esik  8: kayitta 9/9 kalem -> dogru 26, eksik 10, yanlis 0
  esik 24: kayitta 9/9 kalem -> dogru 26, eksik 10, yanlis 0
  esik 48: kayitta 4/9 kalem -> dogru 16, eksik 15, yanlis 5
```

Eşik tablosu gecikmenin bedelini veriyor. Sekiz tekrarda fark edilen bir borç ortalama 2,5 ay
sonra kayda girer ve o zamana kadar 1360 dakika ödetir; yirmi dört tekrarda 7,4 ay ve 4080 dakika.
Kırk sekiz tekrar gerektiren bir örüntü ise bir yıl içinde hiç fark edilmez ve ödenen faiz yıllık
bilinçsiz faizin yüzde 120'sine çıkar — yani bir yıldan fazla süre boyunca kimse neyin yavaşlattığını
bilmez. Bu üç satır arasındaki fark tek bir şeye bağlıdır: fazladan işin bir örüntü olarak
görülmesi ne kadar sürüyor.

Kalem tablosu bu bağın sonucunu gösteriyor ve sezgiye ters düşüyor. `gizli-sube-varsayimi` en
yüksek faizli kalemdir — yılda 1980 dakika — ama fark edilmesi en geç olanlardan biridir, 8,0 ay.
`testsiz-ceza-dali` daha az faiz taşır ve 6,3 ayda fark edilir. Nedeni modelin kuralıdır: fark
etme, her seferinde ne kadar acıdığına değil, kaç kez tekrarlandığına bağlıdır. Az sayıda ama ağır
darbe indiren bir borç, sık ama hafif darbe indirenden daha uzun süre görünmez kalır. Fark etme
kuralı büyüklüğe bağlansaydı sıra tersine dönerdi; ölçülen sonuç kuralın kendisine bağlıdır ve
kural yazılı olmadan sonuç yorumlanamaz.

Soru kümesi tablosu iki eşikte aynı çıkıyor, üçüncüsünde ayrılıyor. Fark edilmiş bir bilinçsiz
borç kaleminin dört sorudan ikisi yanıtlanır: bilerek seçilmediği söylenebilir ve değişiklik başına
çıkardığı iş ölçülebilir. Ne zaman girdiği ve geri dönüş koşulunun ne olduğu yanıtsız kalır, çünkü
o bilgi kalem girerken yazılmamıştır ve sonradan üretilemez. Bilinçli borcun bilinçsiz borçtan tek
gerçek üstünlüğü budur: aynı faizi öder, ama dört soruyu da yanıtlar.

#### Özet

- Bir borç kaleminin yıllık faizi, değişiklik başına eklediği iş ile dokunduğu değişiklik sayısının
  çarpımıdır; en yüksek faizli kalem, değişiklik başına en çok iş çıkaran kalem değildir.
- Beş bilinçsiz kalem yılda 6820, dört bilinçli kalem 3640 dakika tuttu; kalem başına 1364'e karşı
  910 dakika, yani bilinçsiz borç kalem başına daha pahalı çıktı.
- Aynı yıllık değişiklik kümesi `odunc` modülünde 9820, borçsuz bir modülde 6000 dakika sürüyor —
  yüzde 63,7 fazladan iş; `raporlama` modülünde aynı oran yüzde 28,7.
- Bilinçsiz borcun fark edilme gecikmesi tekrar eşiğine bağlıdır: sekiz tekrarda 2,5 ay ve 1360
  dakika, yirmi dörtte 7,4 ay ve 4080 dakika, kırk sekizde bir yılda hiç fark edilmiyor ve ödenen
  faiz yıllık bilinçsiz faizin yüzde 120'sine çıkıyor.
- Fark edilme sırası faiz sırasıyla aynı değildir; en yüksek faizli kalem 8,0 ayla en geç fark
  edilenlerdendir, çünkü bu modelde fark etme büyüklüğe değil tekrara bağlanmıştır.

#### Sonraki Adım

Bu konu boyunca yazılan her şey bir kararın etrafında toplandı. Karar kaydı kararın gerekçesini,
öneri süreci kararın nasıl verildiğini, ödünleşim çözümlemesi neye tercih edildiğini, risk kaydı
neyin tutmayabileceğini, borç kaydı neyin bilerek eksik bırakıldığını tutuyor. Beş kayıt biçimi ve
her biri için ölçülmüş bir soru kümesi var.

Hepsinin ortak bir sınırı var: her biri **tek bir kararın** kaydıdır. Kütüphane ağının sistemine
yeni gelen biri bu kayıtların hepsini okuduğunda kırk kararın gerekçesini öğrenir, ama sistemin
neye benzediğini öğrenemez. Hangi parçalar var, hangisi hangisine bağlı, veri nereden nereye
akıyor, hangi parça hangi makinede duruyor — bu soruların hiçbiri tek tek kararların kaydında
yanıtlanmaz, çünkü hiçbiri tek bir karara ait değildir. Kayıtları üst üste koymak da bir resim
vermez; kırk kaydın toplamı sistemin haritası değildir. Sonraki konu bu boşluğu ele alır: sistemin
bütününü gösteren bir anlatımın ne olduğunu, kaç farklı bakışa ihtiyaç duyduğunu ve her bakışın
hangi soruyu yanıtladığını ölçer.

## Görünümler ve Modeller

Sistemin hangi soruya hangi resimle yanıt verdiği: mantıksal, süreç, geliştirme ve fiziksel görünümlerin ayrı sorular yanıtlaması, bağlamdan koda ölçek değiştiren katmanlı diyagram yaklaşımı, diyagram türünün soruya göre seçilmesi, kalite gereksinimlerinin ölçülebilir senaryolara çevrilmesi, belgenin kodla eşitlenmesi ve belirsizlik bandıyla verilen büyüklük tahminleri.

### Mimari Görünümler

Source: https://academia.sh/tr/kurslar/mimari-dokumantasyon/mimari-gorunumler

Bir soru kümesinin mantıksal, süreç, geliştirme ve fiziksel görünüme uygulanması: her görünümün yanıtladığı soru sayısı, hiçbir görünümün yanıtlamadığı soru sınıfı, iki görünümün birden yanıtladığı yineleme ve tek görünümle yetinmenin kaçırdığı soru sayısı.

Önceki konu kararı bir nesneye çevirdi: her kararın bağlamı, elenen alternatifi, taşıdığı risk ve
bıraktığı borç yazılı duruyor. Bölgesel kütüphane ağının kayıt yığını artık "dış kataloga tek
bağlayıcı üzerinden erişilir" ya da "bildirimler kuyruğa yazılır" diyebiliyor. Ama bu yığın tek tek
kararların kaydıdır. Altı ay sonra gelen biri hepsini arka arkaya okuduğunda kararların listesini
öğrenir, sistemin resmini öğrenmez. "Bir ödünç isteği geldiğinde ne oluyor" sorusunun yanıtı hiçbir
kaydın içinde bütün hâlde durmaz; parçaları on ayrı kayda dağılmıştır.

Görünüm bu boşluğu doldurmak için vardır. Bir **görünüm**, sistemin tek bir ilgi ekseninden
bakılmış hâlidir; o eksende anlamlı olan bilgiyi taşır, gerisini taşımaz. Yaygın bölme dörttür:
**mantıksal görünüm**, **süreç görünümü**, **geliştirme görünümü** ve **fiziksel görünüm**. Bu ders
dördünü çizimleriyle değil, **taşıdıkları bilgi ögeleriyle** tanımlar ve tek bir soruyla ölçer: bu
görünüm, sonradan gelen birinin sorduğu soruların kaçını yanıtlıyor.

#### Görünüm Bir Bilgi Kümesidir

Diyagram çizilmiyor. Çizilen şeyin altındaki yapı modelleniyor: bir görünüm, belirli türden bilgi
ögelerini taşıyabilen bir kaptır. Mantıksal görünüm modülleri, sorumlulukları, modüller arası
bağımlılığı, arayüzleri ve veri varlıklarını taşır — ama bir modülün hangi makinede koştuğunu
taşımaz. Süreç görünümü koşan süreçleri, çağrı sırasını, eşzamanlılık noktalarını ve kuyrukları
taşır — ama kaynak dizin yapısını taşımaz. Geliştirme görünümü kaynak ağacını, yapı birimlerini,
takım sahipliğini ve dış bağımlılıkları taşır. Fiziksel görünüm düğümleri, ağ bağlantılarını,
yerleşimi ve kapasiteyi taşır.

Öge listeleri elle seçilmiştir (**GR1**); başka bir ağda sınırlar biraz farklı çizilebilir. Seçimin
kendisi ölçümün girdisidir, sonucu değil.

Soru kümesi de bir veri yapısıdır (**GR2**). Yirmi dört soru, altı ay sonra gelen birinin gerçekten
sorduğu türden: bir kural nerede duruyor, bir istek hangi süreçte işleniyor, bir kod hangi dizinde,
bir servis hangi düğümde. Her sorunun yanıtı için gereken bilgi ögeleri yazılıdır. Kural tek
cümledir (**GR3**): **bir görünüm bir soruyu ancak gereken ögelerin hepsini taşıyorsa yanıtlar.**
Yarısını taşımak yanıt değildir; okuyucu kalanı başka bir yerden bulmak zorundadır.

```js
// gorunum/model.mjs — MODEL bolgesel kutuphane agi: dort gorunumun tasidigi bilgi
// ogeleri ve alti ay sonra gelen birinin sordugu soru kumesi. Gercek bir kurum
// degildir; oge listeleri ve sorular elle secilmistir.

// Bir gorunum yalnizca listesindeki bilgi ogelerini tasir (GR1).
export const GORUNUM = {
  mantiksal: ["modul", "sorumluluk", "modul-bagimliligi", "arayuz", "veri-varligi"],
  surec: ["modul", "arayuz", "surec", "cagri-sirasi", "es-zamanlilik", "kuyruk"],
  gelistirme: ["modul", "kaynak-agaci", "yapi-birimi", "takim", "dis-bagimlilik"],
  fiziksel: ["dugum", "ag-baglantisi", "yerlesim", "kapasite", "yapi-birimi"],
};
export const GORUNUMLER = Object.keys(GORUNUM);

// SORU — gerek: yaniti icin gereken bilgi ogeleri (GR2). Bir gorunum soruyu ancak
// gereken ogelerin hepsini tasiyorsa yanitlar (GR3).
export const SORU = [
  { id: "S01", metin: "odunc suresi kurali hangi modulde duruyor", gerek: ["modul", "sorumluluk"] },
  { id: "S02", metin: "ceza hesabi uye kaydina bagli mi", gerek: ["modul", "modul-bagimliligi"] },
  { id: "S03", metin: "katalog baglayicisi disa hangi arayuzu veriyor", gerek: ["modul", "arayuz"] },
  { id: "S04", metin: "uye ile odunc kaydi arasindaki iliski ne", gerek: ["veri-varligi"] },
  { id: "S05", metin: "sistemde hangi adlandirilmis parcalar var", gerek: ["modul"] },
  { id: "S06", metin: "ayirtma istegi hangi surecte islenir", gerek: ["modul", "surec"] },
  { id: "S07", metin: "bildirim kuyrugunu hangi surec tuketir", gerek: ["kuyruk", "surec"] },
  { id: "S08", metin: "iki sube ayni kopyayi ayni anda verirse ne kilitlenir", gerek: ["es-zamanlilik"] },
  { id: "S09", metin: "odunc verme akisinda cagrilar hangi sirayla gider", gerek: ["cagri-sirasi"] },
  { id: "S10", metin: "kiosk ucundan gelen cagri es zamansiz mi", gerek: ["cagri-sirasi", "kuyruk"] },
  { id: "S11", metin: "ceza hesabi kodu hangi dizinde duruyor", gerek: ["kaynak-agaci"] },
  { id: "S12", metin: "bu module hangi takim bakiyor", gerek: ["modul", "takim"] },
  { id: "S13", metin: "kac ayri yapi birimi var", gerek: ["yapi-birimi"] },
  { id: "S14", metin: "katalog baglayicisi hangi dis kutuphaneyi kullaniyor", gerek: ["modul", "dis-bagimlilik"] },
  { id: "S15", metin: "odunc servisi hangi dugumde kosuyor", gerek: ["yerlesim", "dugum"] },
  { id: "S16", metin: "sube ucu ile merkez arasindaki bag hangi tur", gerek: ["ag-baglantisi", "dugum"] },
  { id: "S17", metin: "merkez dugumunun kapasitesi ne", gerek: ["dugum", "kapasite"] },
  { id: "S18", metin: "hangi yapi birimi hangi dugume yerlesir", gerek: ["yapi-birimi", "yerlesim"] },
  { id: "S19", metin: "ceza hesabina bakan takim hangi dugumdeki yayini etkiler", gerek: ["takim", "yerlesim"] },
  { id: "S20", metin: "odunc akisindaki hangi cagri ag uzerinden gider", gerek: ["cagri-sirasi", "ag-baglantisi"] },
  { id: "S21", metin: "hangi veri varligi hangi dugumde durur", gerek: ["veri-varligi", "yerlesim"] },
  { id: "S22", metin: "kilit tutulan modulun sorumlulugu ne", gerek: ["es-zamanlilik", "sorumluluk"] },
  { id: "S23", metin: "bir yapi birimini yayinlamak kac sureci yeniden baslatir", gerek: ["yapi-birimi", "surec"] },
  { id: "S24", metin: "ayirtma kuyrugunun tikanmasi hangi takimin isi", gerek: ["kuyruk", "takim"] },
];

// sabit sira
export const OGELER = ["ag-baglantisi", "arayuz", "cagri-sirasi", "dis-bagimlilik", "dugum",
  "es-zamanlilik", "kapasite", "kaynak-agaci", "kuyruk", "modul", "modul-bagimliligi",
  "sorumluluk", "surec", "takim", "veri-varligi", "yapi-birimi", "yerlesim"];
export const yanitlar = (g, s) => s.gerek.every((o) => GORUNUM[g].includes(o));
export const yanitlayan = (s) => GORUNUMLER.filter((g) => yanitlar(g, s));
```

#### Ölçüm

İkinci dosya soru kümesini dört görünüme uygular, yanıtlanan ve yanıtlanamayan soruları sayar,
yinelemeyi çıkarır ve üç düzeni aynı değişiklik akışında koşturur.

```js
// gorunum/olcum.mjs — soru kumesi dort gorunume uygulanir; yanitlanan, yanitlanamayan,
// yinelenen soru ve bakim maliyeti sayilir.
import { GORUNUM, GORUNUMLER, SORU, OGELER, yanitlayan } from "./model.mjs";

const sut = (s, n) => String(s).padEnd(n);
const kisa = { mantiksal: "man", surec: "sur", gelistirme: "gel", fiziksel: "fiz" };

const olcum = SORU.map((s) => ({ ...s, y: yanitlayan(s) }));
console.log(sut("soru", 6) + sut("gereken oge", 34) + "yanitlayan gorunum");
console.log("-".repeat(72));
for (const o of olcum)
  console.log(sut(o.id, 6) + sut(o.gerek.join("+"), 34) + (o.y.length ? o.y.map((g) => kisa[g]).join(",") : "- yok -"));

const bir = olcum.filter((o) => o.y.length === 1).length;
const cok = olcum.filter((o) => o.y.length > 1);
const sifir = olcum.filter((o) => o.y.length === 0);
console.log(`\n${SORU.length} soru: tek gorunum ${bir}, birden cok gorunum ${cok.length}, hicbiri ${sifir.length}`);
console.log(`dort gorunum birlikte ${SORU.length - sifir.length}/${SORU.length} soruyu yanitliyor`);

console.log("\ngorunum      yanit  kacirdigi  en sik eksik oge");
console.log("-".repeat(62));
for (const g of GORUNUMLER) {
  const kacan = olcum.filter((o) => !o.y.includes(g));
  const sayac = {};
  for (const o of kacan) for (const e of o.gerek.filter((x) => !GORUNUM[g].includes(x))) sayac[e] = (sayac[e] || 0) + 1;
  const ilk = Object.entries(sayac).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, 3);
  console.log(sut(g, 13) + sut(SORU.length - kacan.length, 7) + sut(kacan.length, 11) + ilk.map(([e, n]) => `${e}(${n})`).join(" "));
}

// ---- yineleme: bir oge kac gorunumde tekrarlaniyor ----
const kacGorunumde = (o) => GORUNUMLER.filter((g) => GORUNUM[g].includes(o)).length;
const yerlesim = OGELER.reduce((t, o) => t + kacGorunumde(o), 0);
console.log(`\n${OGELER.length} ayri bilgi ogesi ${yerlesim} kez yaziliyor; ${yerlesim - OGELER.length} yazim yineleme`);
console.log("yinelenen oge: " + OGELER.filter((o) => kacGorunumde(o) > 1).map((o) => `${o} x${kacGorunumde(o)}`).join(", "));
console.log("yinelenen soru: " + cok.map((o) => `${o.id}(${o.y.length})`).join(", "));

// ---- yanitlanamayan sorularin istedigi capraz eslemeler ----
const ciftAdi = (a, b) => [a, b].sort().join(" <-> ");
const eslemeler = [...new Set(sifir.map((o) => ciftAdi(o.gerek[0], o.gerek[1])))];
console.log(`\nyanitlanamayan ${sifir.length} soru ${eslemeler.length} capraz esleme istiyor:`);
for (const o of sifir) {
  const sahip = o.gerek.map((e) => GORUNUMLER.filter((g) => GORUNUM[g].includes(e)).map((g) => kisa[g]).join("/"));
  console.log(`  ${o.id} ${sut(ciftAdi(o.gerek[0], o.gerek[1]), 36)}(${sahip.join(" ile ")})`);
}

// ---- bakim: 200 degisiklik uc duzende ----
// AGIRLIK — her bilgi ogesinin degisme sikligi (GR4); elle secilmistir
const AGIRLIK = {
  modul: 10, sorumluluk: 8, "modul-bagimliligi": 12, arayuz: 9, "veri-varligi": 6,
  surec: 5, "cagri-sirasi": 7, "es-zamanlilik": 3, kuyruk: 4,
  "kaynak-agaci": 6, "yapi-birimi": 4, takim: 3, "dis-bagimlilik": 5,
  dugum: 2, "ag-baglantisi": 2, yerlesim: 3, kapasite: 2,
};
function uretec(tohum) { let s = tohum >>> 0; return () => (s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32; }
const rast = uretec(70321); // tohum gorunur; ayni tohum ayni diziyi verir
const toplamAgirlik = OGELER.reduce((t, o) => t + AGIRLIK[o], 0);
const secOge = () => { let r = rast() * toplamAgirlik; for (const o of OGELER) if ((r -= AGIRLIK[o]) < 0) return o; return OGELER.at(-1); };

const N = 200;
let tekYazim = 0, dortYazim = 0, dortYineleme = 0, eslemeTazeleme = 0;
for (let i = 0; i < N; i++) {
  const o = secOge();
  if (GORUNUM.surec.includes(o)) tekYazim++;
  const k = kacGorunumde(o);
  dortYazim += k; dortYineleme += k - 1;
  eslemeTazeleme += eslemeler.filter((e) => e.split(" <-> ").includes(o)).length;
}
const enIyiTek = GORUNUMLER.map((g) => [g, olcum.filter((o) => o.y.includes(g)).length]).sort((a, b) => b[1] - a[1])[0];
console.log(`\n${N} degisiklik olayi, en iyi tek gorunum = ${enIyiTek[0]}`);
console.log(sut("duzen", 26) + sut("yanit", 8) + sut("yazim", 8) + "yaniti basina yazim");
console.log("-".repeat(64));
const satir = (ad, yanit, yazim) => console.log(sut(ad, 26) + sut(`${yanit}/${SORU.length}`, 8) + sut(yazim, 8) + (yazim / yanit).toFixed(2));
satir("tek gorunum", enIyiTek[1], tekYazim);
satir("dort gorunum", SORU.length - sifir.length, dortYazim);
satir("dort gorunum + esleme", SORU.length, dortYazim + eslemeTazeleme);
console.log(`dort gorunumun ${dortYazim} yaziminin ${dortYineleme}'i ayni olguyu ikinci kez yazmak`);
console.log(`${eslemeler.length} esleme ${N} degisiklikte ${eslemeTazeleme} kez tazeleniyor, karsiliginda ${sifir.length} soru aciliyor`);
```

```
soru  gereken oge                       yanitlayan gorunum
------------------------------------------------------------------------
S01   modul+sorumluluk                  man
S02   modul+modul-bagimliligi           man
S03   modul+arayuz                      man,sur
S04   veri-varligi                      man
S05   modul                             man,sur,gel
S06   modul+surec                       sur
S07   kuyruk+surec                      sur
S08   es-zamanlilik                     sur
S09   cagri-sirasi                      sur
S10   cagri-sirasi+kuyruk               sur
S11   kaynak-agaci                      gel
S12   modul+takim                       gel
S13   yapi-birimi                       gel,fiz
S14   modul+dis-bagimlilik              gel
S15   yerlesim+dugum                    fiz
S16   ag-baglantisi+dugum               fiz
S17   dugum+kapasite                    fiz
S18   yapi-birimi+yerlesim              fiz
S19   takim+yerlesim                    - yok -
S20   cagri-sirasi+ag-baglantisi        - yok -
S21   veri-varligi+yerlesim             - yok -
S22   es-zamanlilik+sorumluluk          - yok -
S23   yapi-birimi+surec                 - yok -
S24   kuyruk+takim                      - yok -

24 soru: tek gorunum 15, birden cok gorunum 3, hicbiri 6
dort gorunum birlikte 18/24 soruyu yanitliyor

gorunum      yanit  kacirdigi  en sik eksik oge
--------------------------------------------------------------
mantiksal    5      19         yerlesim(4) cagri-sirasi(3) dugum(3)
surec        7      17         yerlesim(4) dugum(3) takim(3)
gelistirme   5      19         yerlesim(4) cagri-sirasi(3) dugum(3)
fiziksel     5      19         modul(7) cagri-sirasi(3) kuyruk(3)

17 ayri bilgi ogesi 21 kez yaziliyor; 4 yazim yineleme
yinelenen oge: arayuz x2, modul x3, yapi-birimi x2
yinelenen soru: S03(2), S05(3), S13(2)

yanitlanamayan 6 soru 6 capraz esleme istiyor:
  S19 takim <-> yerlesim                  (gel ile fiz)
  S20 ag-baglantisi <-> cagri-sirasi      (sur ile fiz)
  S21 veri-varligi <-> yerlesim           (man ile fiz)
  S22 es-zamanlilik <-> sorumluluk        (sur ile man)
  S23 surec <-> yapi-birimi               (gel/fiz ile sur)
  S24 kuyruk <-> takim                    (sur ile gel)

200 degisiklik olayi, en iyi tek gorunum = surec
duzen                     yanit   yazim   yaniti basina yazim
----------------------------------------------------------------
tek gorunum               7/24    86      12.29
dort gorunum              18/24   279     15.50
dort gorunum + esleme     24/24   396     16.50
dort gorunumun 279 yaziminin 79'i ayni olguyu ikinci kez yazmak
6 esleme 200 degisiklikte 117 kez tazeleniyor, karsiliginda 6 soru aciliyor
```

#### Yanıtlanan ve Yanıtlanamayan

Dört görünüm birlikte yirmi dört sorunun **on sekizini** yanıtlıyor. On beşinin yanıtı tek bir
görünümde, üçünün yanıtı birden çok görünümde duruyor. Altı soru hiçbir görünümde yanıt bulmuyor.

Yanıtlanamayan altının ortak bir özelliği var ve bu özellik dersin asıl bulgusudur: **her biri iki
farklı görünümde duran iki ögeyi birleştirmeyi istiyor.** Ceza hesabına bakan takımın hangi
düğümdeki yayını etkilediği sorusu, geliştirme görünümündeki takım bilgisiyle fiziksel görünümdeki
yerleşim bilgisini yan yana koymadan yanıtlanmaz. Ödünç akışındaki hangi çağrının ağ üzerinden
gittiği, süreç görünümündeki çağrı sırasını fiziksel görünümdeki ağ bağlantısıyla eşlemeyi ister.
Bir yapı birimini yayınlamanın kaç süreci yeniden başlattığı, geliştirme ile süreç görünümü
arasındaki eşlemedir.

Bu, dört görünümün eksik seçilmiş olmasından çıkmıyor. Görünümler tanımı gereği ilgi eksenine göre
ayrıldığı için, **eksenler arasına düşen soru sınıfı boşta kalır.** Dört görünüm ne kadar iyi
yazılırsa yazılsın bu altı soru orada durmayacak; onlar için ayrı bir şey, görünümler arasında bir
eşleme gerekir. Altı sorunun istediği altı ayrı eşleme çıktının sonunda listeli.

Yanıtlanamayan sorunun sayılması bu kursun en değerli ölçüsüdür, çünkü belgeyi okuyan biri sorusuna
yanıt bulamadığında bunu kimseye bildirmez; kodun içine bakar, birine sorar ya da tahmin eder. Sayı
tutulmazsa boşluk görünmez.

#### Tek Görünümün Kaçırdığı

Alt tablo her görünümün tek başına ne yaptığını gösteriyor. En iyisi süreç görünümü: yirmi dört
sorunun **yedisini** yanıtlıyor, on yedisini kaçırıyor. Mantıksal, geliştirme ve fiziksel görünümün
her biri beş soru yanıtlayıp on dokuz soru kaçırıyor.

"En sık eksik öge" sütunu kaçırılan soruların **sınıfını** adlandırıyor. Mantıksal görünüm tek
başına kullanıldığında en çok yerleşim (4 soru), çağrı sırası (3) ve düğüm (3) bilgisini arıyor —
yani kaçırdığı şey çalışma anı ve yerleşim sınıfıdır. Fiziksel görünümün eksiği bambaşka: yedi
soruda modül bilgisi eksik kalıyor. Fiziksel görünüm nerede koşulduğunu bilir, neyin koştuğunu
adlandırmaz.

Buradan çıkan pratik sonuç şudur: bir sistemin belgesi tek görünümden ibaretse, kaçırdığı sorular
rastgele dağılmaz; **hep aynı sınıftan** olur. Mantıksal görünümle yetinen bir belge yerleşim ve
çalışma anı sorularını sistematik olarak yanıtsız bırakır; fiziksel görünümle yetinen bir belge de
"bu düğümde ne koşuyor" sorusunu.

#### Yinelemenin Bedeli

Üç soru birden çok görünümde yanıtlanıyor. Bu ilk bakışta iyi görünür — iki yerden de bulunabilen
bilgi. Bedeli öge tarafında duruyor: on yedi ayrı bilgi ögesi görünümlere yirmi bir kez yazılıyor.
Dört yazım yinelemedir. Modül kimliği üç görünümde, arayüz iki görünümde, yapı birimi iki görünümde
tekrarlanır. Modül adı değiştiğinde üç yerde değişmesi gerekir; üçünden biri unutulursa iki görünüm
birbiriyle çelişir.

Alt tablo bunu değişiklik akışında sayıyor. Ögelerin değişme sıklığı elle seçilmiş bir ağırlık
tablosundan geliyor (**GR4**); tohumu 70321 olan üreteçle iki yüz değişiklik olayı üretiliyor. Aynı
tohum aynı diziyi verir.

Dört görünüm düzeni iki yüz değişikliğe karşılık **279 yazım** istiyor; bunun **79'u** aynı olguyu
ikinci ya da üçüncü kez yazmaktır. Buna karşılık on sekiz soru yanıtlanıyor: yanıt başına 15,50
yazım. Tek görünüm düzeni 86 yazımla yetiniyor ama yalnız yedi soru yanıtlıyor: yanıt başına 12,29
yazım. Dördün üstüne altı çapraz eşleme eklendiğinde bütün küme yanıtlanıyor; eşlemeler iki yüz
değişiklikte 117 kez tazeleniyor ve yanıt başına yazım 16,50'ye çıkıyor.

Sıralamanın okunuşu şudur: kapsam arttıkça yanıt başına maliyet de artıyor, ama **artış yavaş.**
Yanıt sayısı yediden yirmi dörde, üç kattan fazla çıkarken yanıt başına yazım 12,29'dan 16,50'ye,
üçte bir kadar artıyor. Tek görünümün ucuzluğu bir yanılsamadır: ucuz olan yazım değil, yazılmayan
on yedi sorunun okuyucuya devredilmiş olmasıdır. Bu sayılar modelin girdilerine bağlıdır; ağırlık
tablosu yerleşim ve düğüm ögelerine daha yüksek değer verseydi fiziksel görünümün payı artar,
sıralama değişmezdi.

#### Özet

- Görünüm, sistemin tek bir ilgi ekseninden bakılmış hâlidir ve çizimiyle değil taşıdığı bilgi
  ögesi kümesiyle tanımlanır; mantıksal, süreç, geliştirme ve fiziksel görünümün öge listeleri
  ayrıdır ve yalnız üç ögede örtüşür.
- Yirmi dört soruluk kümede dört görünüm birlikte 18 soruyu yanıtlıyor; 15 soru tek görünümde, 3
  soru birden çok görünümde yanıt buluyor, 6 soru hiçbir görünümde yanıt bulmuyor.
- Yanıtlanamayan altı sorunun hepsi iki farklı görünümdeki ögeyi birleştirmeyi istiyor; bu boşluk
  görünümlerin kötü seçilmesinden değil eksene göre ayrılmalarından çıkar ve altı çapraz eşleme
  ister.
- Tek görünümle yetinildiğinde en iyi durum 7/24'tür (süreç görünümü); kaçırılan sorular rastgele
  değil aynı sınıftandır — mantıksal görünüm yerleşim ve çalışma anı sorularını, fiziksel görünüm
  yedi soruda modül kimliğini eksik bırakır.
- On yedi bilgi ögesi görünümlere 21 kez yazılıyor, 4 yazım yinelemedir; tohumu 70321 olan üreteçle
  200 değişiklikte dört görünüm 279 yazım istiyor ve bunun 79'u aynı olgunun ikinci yazımıdır.
  Yanıt başına yazım tek görünümde 12,29, dört görünümde 15,50, eşlemeler eklendiğinde 16,50'dir.

#### Sonraki Adım

Dört görünüm sistemi **ilgi eksenine** göre kesti: ne, ne zaman, kimin kodunda, nerede. Kesilmeyen
bir eksen kaldı. Mantıksal görünüm bu modelde bütün modülleri tek bir listede taşıyor; ağın dışarıya
nasıl göründüğünü soran biriyle bir modülün içindeki işlev yerleşimini soran biri aynı listeye
bakmak zorunda. Birincisi aradığından çok fazlasını okur, ikincisi aradığı ayrıntıyı hiç bulamaz.
Sonraki ders aynı sistemi bağlamdan koda dört ölçek düzeyinde modeller ve şunu ölçer:
düzey başına kaç düğüm ve kenar var, bir soru hangi düzeyde yanıtlanıyor, yanlış düzeyde bakan biri
kaç gereksiz düğüm okuyor.

### Katmanlı Diyagram Yaklaşımı

Source: https://academia.sh/tr/kurslar/mimari-dokumantasyon/katmanli-diyagram-yaklasimi

Aynı sistemin bağlam, kap, bileşen ve kod düzeyinde modellenmesi: düzey başına düğüm ve kenar sayısı, bir sorunun hangi düzeyde yanıtlandığı, yanlış düzeyde bakan okuyucunun okuduğu gereksiz düğüm ve dört düzeyi bakımda tutmanın başabaş noktası.

Önceki ders sistemi ilgi eksenine göre dört görünüme böldü ve her görünümün yanıtladığı soruyu
saydı. Kesilmeyen bir eksen kaldı: **ayrıntı.** Bölgesel kütüphane ağının dışarıyla ne alışveriş
ettiğini soran biri de, gecikme cezası formülünün neyi çağırdığını soran biri de aynı modül
listesine bakmak zorunda. Birincisi için liste fazla kalabalık, ikincisi için fazla kaba.

Katmanlı diyagram yaklaşımı bu ekseni keser. Aynı sistem **dört ölçek düzeyinde** modellenir:
**bağlam düzeyi** sistemi tek bir kutu olarak ve dışarıdaki taraflarla ilişkisini; **kap düzeyi**
sistemin içindeki ayrı çalışan ve ayrı yayınlanan birimleri; **bileşen düzeyi** bir kabın içindeki
sorumluluk parçalarını; **kod düzeyi** bir bileşenin içindeki işlevleri gösterir. Düzeyler farklı
şeyleri değil, **aynı şeyi farklı ölçekte** gösterir.

İki terimin sınırı yazılı olsun. **Kap**, ayrı çalışan ve ayrı yayınlanan birimdir — bir uç
uygulaması, bir servis, bir veri deposu, bir ileti yolu. **Bileşen** daha önce modül içi sorumluluk
parçası olarak kurulmuştu; buradaki fark bağlamdır: orada bileşen bir kod tabanının içindeki
ayrılabilir birimdi, burada **bir kabın içindeki** ayrılabilir birimdir ve kendi başına
yayınlanmaz.

#### Model

Diyagram çizilmiyor. Çizimin altındaki yapı modelleniyor: düğümler, kenarlar ve düğümleri
birbirine bağlayan ağaç. Ağaç elle yazılmıştır (**GR5**), bileşen düzeyindeki bağımlılıklar da
öyle (**GR6**). Kod düzeyindeki kenarlar ikisinden türetiliyor: her bileşen bağımlılığı kod
düzeyinde tek bir öge çiftiyle gerçekleşir (**GR7**) ve her bileşenin ögeleri kendi içinde bir
zincirle bağlanır (**GR8**). Tohum görünür.

Üst düzeylerin kenarları hesaplanır, yazılmaz: bir kod kenarının iki ucu istenen düzeydeki
atalarıyla değiştirilir, kendine dönen kenarlar atılır, kalanlar tekilleştirilir. Buna **kenarın
yükseltilmesi** deniyor. Yöntemin doğruluğu ölçülebilir bir iddiadır ve çıktıda sınanıyor: elle
yazılan bileşen kenarları, kod kenarlarından yükseltilince geri gelmelidir.

Soru kümesi yine bir veri yapısıdır (**GR9**). Her sorunun bir hedef düğümü ve bir **yanıt düzeyi**
var: hedefin komşularının ayırt edilebildiği en sığ düzey. Okuma maliyeti şöyle sayılıyor: yanıt
düzeyinde okunan düğüm, hedef ile komşularıdır. Daha derin bir düzeyde okuyan biri hedefin ve
komşularının bütün alt düğümlerini okumak zorundadır. Daha sığ bir düzeyde ise hedef kendi atasının
içinde kaybolur ve soru **yanıtsız kalır**.

```js
// gorunum/olcek.mjs — MODEL bolgesel kutuphane agi dort olcek duzeyinde: baglam, kap,
// bilesen, kod. Gercek bir kurum degildir; agac ve bilesen kenarlari elle yazilmistir.

// AGAC — kap > bilesen > kod ogesi (GR5)
const AGAC = {
  "web-portali": { "oturum-ucu": ["giris-akisi", "oturum-cerezi", "sifre-sifirlama"],
    "arama-ucu": ["sorgu-formu", "sonuc-listesi", "suzgec"], "hesap-ucu": ["odunc-listesi", "ceza-ozeti"] },
  "personel-uygulamasi": { "tezgah-ucu": ["odunc-ekrani", "iade-ekrani", "ceza-ekrani"],
    "yonetim-ucu": ["uye-yonetimi", "rapor-ekrani"] },
  "kiosk-ucu": { "kiosk-arayuz": ["kart-okuma", "hizli-odunc"], "kiosk-onbellek": ["yerel-katalog", "yerel-sure"] },
  "odunc-servisi": { "odunc-cekirdek": ["odunc-ac", "iade-al", "uzatma"],
    "odunc-kural": ["sure-hesabi", "limit-denetimi", "gecikme-cezasi"], ayirtma: ["ayirtma-sirasi", "ayirtma-bildirimi"] },
  "uye-servisi": { "uye-kayit": ["uye-olustur", "uye-guncelle", "kart-baglama"], "uye-ceza": ["ceza-birikimi", "ceza-tahsilat"] },
  "katalog-tumlestirme": { "katalog-baglayici": ["dis-sorgu", "kayit-donusturme", "yeniden-deneme"],
    "katalog-onbellek": ["onbellek-anahtari", "gecerlilik-suresi"] },
  "envanter-servisi": { "sube-envanter": ["kopya-durumu", "raf-yeri"], "sube-esitleme": ["esitleme-isi", "catisma-cozumu"] },
  "bildirim-servisi": { "bildirim-kuyruk": ["kuyruga-yaz", "kuyruktan-oku"], "bildirim-eposta": ["ileti-govdesi", "gonderim-denemesi"] },
  "rapor-servisi": { "rapor-gunluk": ["gunluk-toplam", "sube-kirilimi"], "rapor-toplu": ["aylik-ozet", "arsiv-yazimi"] },
  "ana-veritabani": { "iliskisel-sema": ["uye-tablosu", "kopya-tablosu", "odunc-tablosu", "ceza-tablosu"] },
  "olay-yolu": { "konu-yonlendirme": ["konu-tanimi", "abonelik"] },
  onbellek: { "anahtar-deger": ["yazma", "okuma", "sureli-silme"] },
};
const SISTEM = "kutuphane-agi";
const DIS = ["uye", "personel", "dis-katalog", "eposta-gecidi", "kimlik-saglayici"];

// BILESEN_KENAR — bilesen duzeyindeki bagimliliklar (GR6); elle secilmistir
const BILESEN_KENAR = [
  ["uye", "arama-ucu"], ["uye", "oturum-ucu"], ["uye", "hesap-ucu"], ["uye", "kiosk-arayuz"],
  ["personel", "tezgah-ucu"], ["personel", "yonetim-ucu"],
  ["oturum-ucu", "kimlik-saglayici"], ["oturum-ucu", "uye-kayit"],
  ["arama-ucu", "katalog-onbellek"], ["arama-ucu", "sube-envanter"],
  ["hesap-ucu", "odunc-cekirdek"], ["hesap-ucu", "uye-ceza"],
  ["tezgah-ucu", "odunc-cekirdek"], ["tezgah-ucu", "uye-ceza"], ["tezgah-ucu", "sube-envanter"],
  ["yonetim-ucu", "uye-kayit"], ["yonetim-ucu", "rapor-gunluk"],
  ["kiosk-arayuz", "odunc-cekirdek"], ["kiosk-arayuz", "kiosk-onbellek"], ["kiosk-onbellek", "katalog-onbellek"],
  ["odunc-cekirdek", "odunc-kural"], ["odunc-cekirdek", "iliskisel-sema"],
  ["odunc-cekirdek", "konu-yonlendirme"], ["odunc-cekirdek", "sube-envanter"],
  ["ayirtma", "odunc-cekirdek"], ["ayirtma", "bildirim-kuyruk"], ["ayirtma", "iliskisel-sema"],
  ["uye-kayit", "iliskisel-sema"],
  ["uye-ceza", "uye-kayit"], ["uye-ceza", "odunc-kural"], ["uye-ceza", "iliskisel-sema"],
  ["katalog-baglayici", "dis-katalog"], ["katalog-onbellek", "katalog-baglayici"], ["katalog-onbellek", "anahtar-deger"],
  ["sube-envanter", "iliskisel-sema"], ["sube-envanter", "konu-yonlendirme"],
  ["sube-esitleme", "sube-envanter"], ["sube-esitleme", "konu-yonlendirme"], ["sube-esitleme", "katalog-baglayici"],
  ["bildirim-kuyruk", "konu-yonlendirme"], ["bildirim-kuyruk", "iliskisel-sema"],
  ["bildirim-eposta", "bildirim-kuyruk"], ["bildirim-eposta", "eposta-gecidi"],
  ["rapor-gunluk", "iliskisel-sema"], ["rapor-toplu", "iliskisel-sema"], ["rapor-toplu", "sube-envanter"],
];

// ---- agactan turetilen eslemeler ----
const KAPLAR = Object.keys(AGAC);
const UST = { bilesen: {}, kap: {} }; // kod->bilesen, bilesen->kap
for (const k of KAPLAR) for (const b of Object.keys(AGAC[k])) {
  UST.kap[b] = k;
  for (const c of AGAC[k][b]) UST.bilesen[c] = b;
}
const BILESENLER = Object.keys(UST.kap);
const KODLAR = Object.keys(UST.bilesen);
const DUZEY = ["baglam", "kap", "bilesen", "kod"];
const dis = (n) => DIS.includes(n);
const seviye = (n) => (dis(n) ? -1 : n === SISTEM ? 0 : KAPLAR.includes(n) ? 1 : BILESENLER.includes(n) ? 2 : 3);
const temsil = (n, L) => { let m = n; while (!dis(m) && seviye(m) > L) m = seviye(m) === 3 ? UST.bilesen[m] : seviye(m) === 2 ? UST.kap[m] : SISTEM; return m; };
const icerik = { baglam: [SISTEM], kap: KAPLAR, bilesen: BILESENLER, kod: KODLAR };
const dugumler = (L) => [...icerik[DUZEY[L]], ...DIS];
const altlar = (n, L) => (seviye(n) >= L || dis(n) ? [temsil(n, L)] : dugumler(L).filter((m) => temsil(m, seviye(n)) === n));

// ---- kod duzeyi kenarlar: her bilesen kenari tek bir oge ciftiyle gerceklesir (GR7),
// her bilesenin ogeleri kendi icinde zincirle baglanir (GR8) ----
function uretec(tohum) { let s = tohum >>> 0; return () => (s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32; }
const rast = uretec(31607); // tohum gorunur
const kodOgesi = (n) => (dis(n) ? n : AGAC[UST.kap[n]][n][Math.floor(rast() * AGAC[UST.kap[n]][n].length)]);
const KOD_KENAR = BILESEN_KENAR.map(([a, b]) => [kodOgesi(a), kodOgesi(b)]);
for (const b of BILESENLER) { const g = AGAC[UST.kap[b]][b]; for (let i = 1; i < g.length; i++) KOD_KENAR.push([g[i - 1], g[i]]); }

const kenarlar = (L) => [...new Set(KOD_KENAR.map(([a, b]) => [temsil(a, L), temsil(b, L)])
  .filter(([a, b]) => a !== b).map(([a, b]) => `${a}->${b}`))].map((s) => s.split("->"));

// ---- duzey basina dugum ve kenar ----
const sut = (s, n) => String(s).padEnd(n);
console.log(sut("duzey", 10) + sut("dugum", 8) + sut("kenar", 8) + "ic dugum + dis taraf");
console.log("-".repeat(52));
DUZEY.forEach((d, L) => console.log(sut(d, 10) + sut(dugumler(L).length, 8) + sut(kenarlar(L).length, 8) + `${icerik[d].length} + ${DIS.length}`));

const geriGelen = new Set(kenarlar(2).map((e) => e.join("->")));
const elYazimi = BILESEN_KENAR.map((e) => e.join("->"));
console.log(`\nel yazimi ${elYazimi.length} bilesen kenarinin ${elYazimi.filter((e) => geriGelen.has(e)).length}'i kod kenarlarindan yukseltilince geri geliyor`);

// ---- soru kumesi: her sorunun yanit duzeyi ve hedefi (GR9) ----
const SORU = [
  { id: "D01", metin: "ag disaridan hangi taraflarla konusuyor", hedef: SISTEM, duzey: 0 },
  { id: "D02", metin: "kimlik saglayici sistemin icinde mi disinda mi", hedef: "kimlik-saglayici", duzey: 0 },
  { id: "D03", metin: "odunc servisi hangi kaplarla konusuyor", hedef: "odunc-servisi", duzey: 1 },
  { id: "D04", metin: "eposta gecidine hangi kap baglaniyor", hedef: "eposta-gecidi", duzey: 1 },
  { id: "D05", metin: "ana veritabanina hangi kaplar yaziyor", hedef: "ana-veritabani", duzey: 1 },
  { id: "D06", metin: "olay yolunu hangi kaplar kullaniyor", hedef: "olay-yolu", duzey: 1 },
  { id: "D07", metin: "odunc kurali bileseni neye bagli", hedef: "odunc-kural", duzey: 2 },
  { id: "D08", metin: "katalog onbellegini hangi bilesenler okuyor", hedef: "katalog-onbellek", duzey: 2 },
  { id: "D09", metin: "ceza hesabi hangi bilesenlere dokunuyor", hedef: "uye-ceza", duzey: 2 },
  { id: "D10", metin: "bildirim kuyruguna hangi bilesen yaziyor", hedef: "bildirim-kuyruk", duzey: 2 },
  { id: "D11", metin: "gecikme cezasi hesabi neyi cagiriyor", hedef: "gecikme-cezasi", duzey: 3 },
  { id: "D12", metin: "sure hesabi hangi ogeye bagli", hedef: "sure-hesabi", duzey: 3 },
  { id: "D13", metin: "uzatma islevi neye dokunuyor", hedef: "uzatma", duzey: 3 },
  { id: "D14", metin: "onbellek anahtari nerede uretiliyor", hedef: "onbellek-anahtari", duzey: 3 },
];

const komsu = (n, L) => { const t = temsil(n, L); return [...new Set(kenarlar(L).filter((e) => e.includes(t)).flat())].filter((m) => m !== t); };
const okuma = (s, L) => (L < s.duzey ? null : L === s.duzey ? 1 + komsu(s.hedef, L).length
  : altlar(s.hedef, L).length + komsu(s.hedef, s.duzey).reduce((t, m) => t + altlar(m, L).length, 0));

console.log("\nsoru  yanit duzeyi  okunan dugum  kod duzeyinde  gereksiz");
console.log("-".repeat(60));
let enAz = 0, hepsiKod = 0;
for (const s of SORU) {
  const az = okuma(s, s.duzey), kod = okuma(s, 3);
  enAz += az; hepsiKod += kod;
  console.log(sut(s.id, 6) + sut(DUZEY[s.duzey], 14) + sut(az, 14) + sut(kod, 15) + (kod - az));
}
console.log("-".repeat(60));
console.log(sut("toplam", 20) + sut(enAz, 14) + sut(hepsiKod, 15) + (hepsiKod - enAz));

console.log("\nayni soru dort duzeyde okunursa:");
console.log((sut("soru", 6) + DUZEY.map((d) => sut(d, 10)).join("")).trimEnd());
for (const id of ["D01", "D03", "D11"]) {
  const s = SORU.find((x) => x.id === id);
  console.log((sut(s.id, 6) + DUZEY.map((d, L) => sut(okuma(s, L) ?? "yanit yok", 10)).join("")).trimEnd());
}

console.log("\nyanit duzeyi dagilimi: " + DUZEY.map((d, L) => `${d}=${SORU.filter((s) => s.duzey === L).length}`).join(", "));
DUZEY.forEach((d, L) => {
  const yanit = SORU.filter((s) => s.duzey <= L);
  const yok = SORU.length - yanit.length;
  console.log(`${sut("yalniz " + d, 18)}yanit ${sut(`${yanit.length}/${SORU.length}`, 8)}yanitsiz ${sut(yok, 4)}okuma ${yanit.reduce((t, s) => t + okuma(s, L), 0)}`);
});

// ---- bakim ve basabas ----
// GR10: bir dugumu okumak ile bir dugumu guncel tutmak esit maliyet sayilir
const bakimDort = DUZEY.reduce((t, d, L) => t + dugumler(L).length, 0);
const bakimKod = dugumler(3).length;
const kazanc = hepsiKod - enAz;
console.log(`\ndort duzey ${bakimDort} dugum bakimda tutar, yalniz kod duzeyi ${bakimKod}; fark ${bakimDort - bakimKod}`);
console.log(`bir soru turu ${kazanc} gereksiz dugum okumasindan kurtariyor (${enAz} yerine ${hepsiKod})`);
console.log(`basabas (GR10): soru kumesi her ${(kazanc / (bakimDort - bakimKod)).toFixed(2)} degisiklik turunda bir kez sorulursa fazla bakim geri odenir`);
```

```
duzey     dugum   kenar   ic dugum + dis taraf
----------------------------------------------------
baglam    6       5       1 + 5
kap       17      30      12 + 5
bilesen   28      46      23 + 5
kod       61      79      56 + 5

el yazimi 46 bilesen kenarinin 46'i kod kenarlarindan yukseltilince geri geliyor

soru  yanit duzeyi  okunan dugum  kod duzeyinde  gereksiz
------------------------------------------------------------
D01   baglam        6             61             55
D02   baglam        2             57             55
D03   kap           9             44             35
D04   kap           2             5              3
D05   kap           6             29             23
D06   kap           4             18             14
D07   bilesen       3             8              5
D08   bilesen       5             13             8
D09   bilesen       6             17             11
D10   bilesen       5             12             7
D11   kod           3             3              0
D12   kod           3             3              0
D13   kod           5             5              0
D14   kod           3             3              0
------------------------------------------------------------
toplam              62            278            216

ayni soru dort duzeyde okunursa:
soru  baglam    kap       bilesen   kod
D01   6         17        28        61
D03   yanit yok 9         18        44
D11   yanit yok yanit yok yanit yok 3

yanit duzeyi dagilimi: baglam=2, kap=4, bilesen=4, kod=4
yalniz baglam     yanit 2/14    yanitsiz 12  okuma 8
yalniz kap        yanit 6/14    yanitsiz 8   okuma 51
yalniz bilesen    yanit 10/14   yanitsiz 4   okuma 112
yalniz kod        yanit 14/14   yanitsiz 0   okuma 278

dort duzey 112 dugum bakimda tutar, yalniz kod duzeyi 61; fark 51
bir soru turu 216 gereksiz dugum okumasindan kurtariyor (62 yerine 278)
basabas (GR10): soru kumesi her 4.24 degisiklik turunda bir kez sorulursa fazla bakim geri odenir
```

#### Düzeylerin Büyüklüğü

Dört düzeyin düğüm sayısı 6, 17, 28 ve 61; kenar sayısı 5, 30, 46 ve 79. Bağlam düzeyi bir sistem
kutusu ve beş dış taraftan ibaret; kod düzeyinde elli altı iç düğüm var. Ölçek düzeyleri arasındaki
büyüme kabaca ikişer katlıdır ve bu bilinçli bir tasarımdır: bir düzeyden diğerine geçen okuyucu
her adımda gördüğü düğüm sayısını iki katına çıkarır, elli katına değil.

Yükseltme yönteminin doğruluğu çıktının ikinci satırında sınanıyor: elle yazılan kırk altı bileşen
kenarının kırk altısı da kod kenarlarından yükseltilince geri geliyor. Üst düzeyler bağımsız
belgeler değil, aynı grafın kaba çözünürlükte okunmuş hâlidir — türetildikleri sürece alt düzeyle
çelişemezler, elle yazıldıklarında çelişebilirler.

#### Soru Hangi Düzeyde Yanıtlanır

On dört soru dört düzeye eşit dağılmıyor: ikisi bağlam, dördü kap, dördü bileşen, dördü kod
düzeyinde yanıt buluyor. Toplam okuma maliyeti, her soru kendi yanıt düzeyinde okunduğunda **62
düğüm.** Bu, dersin ölçüsünün sıfır noktasıdır: on dört sorunun hepsini yanıtlamak için gereken en
az okuma.

Aynı on dört soru yalnız kod düzeyine bakılarak yanıtlansaydı okunan düğüm sayısı **278** olurdu.
Fark **216 gereksiz düğüm** — sorulan şeyin yanıtına katkısı olmayan, sırf doğru ölçek elde
olmadığı için okunan düğümler. En pahalısı ilk iki soru: ağın dışarıyla ilişkisini soran biri
bağlam düzeyinde altı düğüm okur, kod düzeyinde altmış bir. Elli beş düğüm, yanıtı değiştirmeyen
ayrıntıdır.

Kod düzeyindeki dört soruda gereksiz düğüm sıfır; katmanlı yaklaşım onlara bir şey kazandırmıyor.
Kazanç, sorunun yanıt düzeyi ile okuyucunun baktığı düzey arasındaki mesafeyle büyüyor.

#### Yanlış Düzeyin İki Yönü

"Aynı soru dört düzeyde okunursa" tablosu maliyeti tek soru üzerinde gösteriyor. Ağın dış
ilişkisini soran D01 bağlam düzeyinde 6, kap düzeyinde 17, bileşen düzeyinde 28, kod düzeyinde 61
düğüm okutuyor — yanıt her düzeyde bulunabiliyor ama bedeli on kata çıkıyor. Ödünç servisinin
komşularını soran D03 bağlam düzeyinde **yanıtsız**: o düzeyde ödünç servisi diye bir düğüm yok,
sistem kutusunun içinde kalmış. Gecikme cezası hesabını soran D11 ilk üç düzeyin hepsinde yanıtsız.

İki yön de bir sayı üretiyor ve sayılar simetrik değil. **Fazla derine bakmak** yanıtı verir ama
gereksiz düğüm okutur; ölçülebilir, tahammül edilebilir bir israftır. **Fazla yükseğe bakmak** yanıt
vermez; okuyucu ya vazgeçer ya da tahmin eder. Alt tablo bunu düzen düzeyinde sayıyor: yalnız bağlam
düzeyini tutan bir belge on dört sorunun ikisini yanıtlıyor, on ikisi yanıtsız. Yalnız kap düzeyi
altısını, yalnız bileşen düzeyi onunu yanıtlıyor. Yalnız kod düzeyi hepsini yanıtlıyor ama 278
düğüm okutuyor.

#### Bakımın Bedeli

Dört düzeyi birden tutmak 112 düğümü bakımda tutmak demektir; yalnız kod düzeyini tutmak 61. Fark
51 düğümdür ve bu fark bedavaya gelmiyor: kap ve bileşen düzeyindeki her düğümün karşılığı kod
tarafında değiştikçe güncellenmesi gerekir.

Karşılığında kazanılan şey soru turu başına 216 gereksiz düğüm okumasıdır. İki büyüklük farklı
birimlerde olduğu için bir varsayım gerekiyor: bir düğümü okumak ile bir düğümü güncel tutmak eşit
maliyet sayılıyor (**GR10**). Bu varsayımla başabaş noktası şudur: soru kümesi her **4,24 değişiklik
turunda** bir kez sorulursa dört düzeyin fazla bakımı geri ödenir. Soru sıklığı bunun altındaysa —
yani belgeye kimse bakmıyor, kod ise sürekli değişiyorsa — dört düzeyi tutmak zarar eder.

Sayının kendisi modelin girdilerine bağlıdır; asıl taşınan sonuç şudur: katmanlı düzenin kârı
**okunma sıklığına** bağlıdır, belgenin güzelliğine değil.

#### Özet

- Katmanlı yaklaşım aynı sistemi bağlam, kap, bileşen ve kod düzeyinde modeller; düzeyler farklı
  şeyleri değil aynı şeyi farklı ölçekte gösterir. Kap ayrı çalışan ve ayrı yayınlanan birimdir,
  bileşen bir kabın içindeki ayrılabilir parçadır ve kendi başına yayınlanmaz.
- Model ağda düğüm sayıları 6, 17, 28, 61; kenar sayıları 5, 30, 46, 79. Üst düzeylerin kenarları
  yazılmaz, kod kenarlarının uçları atalarıyla değiştirilerek yükseltilir; elle yazılan 46 bileşen
  kenarının 46'sı bu yolla geri geliyor.
- On dört soru kendi yanıt düzeyinde okunduğunda toplam 62 düğüm okunuyor; hepsi kod düzeyinde
  yanıtlanmaya kalkılırsa 278 düğüm okunuyor ve aradaki 216 düğüm gereksizdir.
- Fazla derine bakmak yanıtı pahalıya verir (D01 için 6 düğüm yerine 61), fazla yükseğe bakmak hiç
  vermez: D03 bağlam düzeyinde, D11 ilk üç düzeyin hepsinde yanıtsızdır.
- Dört düzey 112 düğüm bakımda tutar, yalnız kod düzeyi 61; okuma ile bakım eşit maliyet sayılırsa
  (GR10) soru kümesi her 4,24 değişiklik turunda bir kez sorulduğunda fazla bakım geri ödenir.

#### Sonraki Adım

İki ders de düğüm ve kenardan oluşan tek bir yapıyı ölçtü: neyin neye bağlı olduğu. Sorulan
soruların hepsi bu türdendi. Ama sonradan gelen birinin soruları hep "ne neye bağlı" biçiminde
değil. "Ödünç isteği geldiğinde çağrılar hangi sırayla gider", "bir kopya hangi durumlardan geçer",
"bu birim hangi makinede koşar" soruları düğüm–kenar yapısına bakarak yanıtlanamaz; sıra, durum ve
yerleşim taşımayan bir gösterim bu soruların yanıtını içermez. Sonraki ders diyagram türlerini
işlevleriyle ayırır — yapı gösteren, sıra gösteren, durum gösteren, dağıtım gösteren — ve şunu
ölçer: soru başına doğru tür hangisi, yanlış türle yanıtlanmaya çalışılan soru hangi bilgiyi eksik
bırakır, her tür kod değiştiğinde kaç ögesini güncellemek zorunda kalır.

### Diyagram Türünün Soruya Göre Seçilmesi

Source: https://academia.sh/tr/kurslar/mimari-dokumantasyon/uml-mimaride-kullanimi

Yapı, sıra, durum ve dağıtım gösteren diyagram türlerinin bir soru kümesine eşlenmesi: soru başına doğru tür, yanlış türle yanıtlanmaya çalışılan sorunun bıraktığı eksik bilgi ögesi sayısı ve kod değiştiğinde her türün güncellemek zorunda kaldığı öge sayısı.

Önceki iki ders tek bir yapıyı ölçtü: düğüm ve kenar. Görünümler o yapıyı ilgi eksenine, katmanlı
yaklaşım ölçek eksenine göre böldü. Sorulan soruların hepsi aynı biçimdeydi — ne neye bağlı.

Sonradan gelen birinin soruları hep bu biçimde değil. Bölgesel kütüphane ağında "ödünç isteği
geldiğinde çağrılar hangi sırayla gider" diye soran biri bağımlılık listesine bakarak yanıt bulamaz:
liste kimin kimi çağırabildiğini söyler, hangisinin önce çağrıldığını söylemez. "Bir kopya hangi
durumlardan geçer" sorusu ise ne bağımlılık ne sıra ister; durum ve geçiş ister. Bunlar farklı
**diyagram türleri** gerektirir.

Bu ders türleri gösterim adıyla anmıyor. Bir tür, taşıyabildiği bilgi ögeleriyle tanımlanıyor ve
işleviyle anılıyor: **yapı gösteren**, **sıra gösteren**, **durum gösteren**, **dağıtım gösteren**.
Ölçülen şey ikisi: hangi tür hangi soruyu yanıtlıyor ve o türü güncel tutmak kaça mal oluyor.

#### Türü İşlevi Ayırır

Yapı gösteren diyagram katılımcıları, aralarındaki durağan bağı, bağın yönünü ve çokluğu taşır.
Sıra gösteren diyagram katılımcıları, iletileri, iletilerin sırasını, eşzamansızlığı ve koşullu
dalları taşır. Durum gösteren diyagram durumları, geçişleri, tetikleyicileri ve koruma koşullarını
taşır. Dağıtım gösteren diyagram düğümleri, yapı eserlerini, yerleşimi ve iletişim yollarını taşır.

Listelerin ilginç yanı örtüşmenin azlığıdır: dört liste toplam on yedi yer tutuyor ama on altı ayrı
bilgi ögesi içeriyor — yalnız katılımcı iki türde birden bulunuyor. Türler farklı çizim
alışkanlıkları değil, **farklı bilgi kümeleridir**; bu yüzden birbirinin yerine geçemezler.

Model üç parçadan oluşuyor. Türlerin taşıdığı öge listeleri elle yazılmıştır (**GR11**). Ağ için
çizilmiş yedi diyagram ve her birinin bağlı olduğu kod ögeleri de öyle (**GR12**); dağıtım gösteren
diyagram koda değil kap ve düğüme bağlı olduğu için bağlı öge sayısı düşük tutulmuştur. Soru kümesi
on sekiz sorudur (**GR13**) ve bir tür bir soruyu ancak gereken ögelerin hepsini taşıyorsa yanıtlar
(**GR14**).

```js
// gorunum/tur.mjs — MODEL bolgesel kutuphane agi: diyagram turleri islevleriyle
// tanimlanir, soru kumesine eslenir ve bakim maliyeti sayilir. Gercek bir gosterim
// ya da urun degildir; oge listeleri ve sorular elle secilmistir.

// TUR — her diyagram turunun tasiyabildigi bilgi ogeleri (GR11)
const TUR = {
  yapi: ["katilimci", "statik-bag", "yon", "cokluk"],
  sira: ["katilimci", "ileti", "sira", "es-zamansizlik", "kosul-dali"],
  durum: ["durum", "gecis", "tetikleyici", "koruma-kosulu"],
  dagitim: ["dugum", "yapi-eseri", "yerlesim", "iletisim-yolu"],
};
const TURLER = Object.keys(TUR);

// DIYAGRAM — cizilmis diyagramlar ve her birinin bagli oldugu kod ogeleri (GR12).
// Dagitim gosteren diyagram koda degil kap ve dugume baglidir; bu yuzden bagli
// oge sayisi dusuktur.
const DIYAGRAM = {
  "yapi/odunc-servisi": { tur: "yapi", bagli: ["odunc-ac", "iade-al", "uzatma", "sure-hesabi", "limit-denetimi", "gecikme-cezasi", "ayirtma-sirasi", "ayirtma-bildirimi"] },
  "yapi/veri-semasi": { tur: "yapi", bagli: ["odunc-tablosu", "ceza-tablosu", "kopya-tablosu"] },
  "sira/odunc-acma": { tur: "sira", bagli: ["odunc-ac", "sure-hesabi", "limit-denetimi", "kopya-durumu", "odunc-tablosu", "konu-tanimi"] },
  "sira/ayirtma-bildirimi": { tur: "sira", bagli: ["ayirtma-sirasi", "ayirtma-bildirimi", "kuyruga-yaz", "kuyruktan-oku", "ileti-govdesi"] },
  "durum/kopya": { tur: "durum", bagli: ["kopya-durumu", "odunc-ac", "iade-al", "ayirtma-sirasi"] },
  "durum/ceza": { tur: "durum", bagli: ["ceza-birikimi", "gecikme-cezasi", "ceza-tablosu"] },
  "dagitim/ag": { tur: "dagitim", bagli: ["esitleme-isi", "abonelik"] },
};

// SORU — gerek: yaniti icin gereken bilgi ogeleri (GR13). Bir tur soruyu ancak
// gereken ogelerin hepsini tasiyorsa yanitlar (GR14).
const SORU = [
  { id: "U01", metin: "odunc cekirdegi hangi ogelere bagli", gerek: ["katilimci", "statik-bag"] },
  { id: "U02", metin: "bagimlilik hangi yone gidiyor", gerek: ["statik-bag", "yon"] },
  { id: "U03", metin: "bir uyenin kac odunc kaydi olabilir", gerek: ["katilimci", "cokluk"] },
  { id: "U04", metin: "odunc acma akisinda ilk cagri hangisi", gerek: ["ileti", "sira"] },
  { id: "U05", metin: "bildirim cagrisi es zamansiz mi", gerek: ["ileti", "es-zamansizlik"] },
  { id: "U06", metin: "limit asilirsa akis nereye sapar", gerek: ["sira", "kosul-dali"] },
  { id: "U07", metin: "kopya hangi durumlardan gecer", gerek: ["durum", "gecis"] },
  { id: "U08", metin: "iade hangi gecisi tetikler", gerek: ["gecis", "tetikleyici"] },
  { id: "U09", metin: "ayirtmali kopya dogrudan odunc verilebilir mi", gerek: ["gecis", "koruma-kosulu"] },
  { id: "U10", metin: "odunc servisi hangi dugumde kosuyor", gerek: ["yapi-eseri", "yerlesim"] },
  { id: "U11", metin: "sube ile merkez arasindaki yol hangi tur", gerek: ["dugum", "iletisim-yolu"] },
  { id: "U12", metin: "kac ayri dugum var", gerek: ["dugum"] },
  { id: "U13", metin: "odunc akisinda hangi katilimcilar var", gerek: ["katilimci"] },
  { id: "U14", metin: "ceza birikimi hangi kosulda durur", gerek: ["gecis", "koruma-kosulu"] },
  { id: "U15", metin: "gecikme cezasini hangi ileti hangi durumda tetikler", gerek: ["ileti", "sira", "durum"] },
  { id: "U16", metin: "hangi dugumdeki katilimci hangi iletiyi gonderiyor", gerek: ["dugum", "katilimci", "ileti"] },
  { id: "U17", metin: "ayirtma bildirimi hangi sirayla gonderilir", gerek: ["ileti", "sira"] },
  { id: "U18", metin: "hangi yapi eseri hangi dugume kopyalanir", gerek: ["yapi-eseri", "yerlesim", "dugum"] },
];

const sut = (s, n) => String(s).padEnd(n);
const eksik = (t, s) => s.gerek.filter((o) => !TUR[t].includes(o));
const dogruTur = (s) => TURLER.filter((t) => eksik(t, s).length === 0);

// ---- tur tablosu ----
console.log(sut("tur", 10) + sut("bilgi ogesi", 14) + sut("diyagram", 10) + "bagli oge");
console.log("-".repeat(46));
for (const t of TURLER) {
  const d = Object.values(DIYAGRAM).filter((x) => x.tur === t);
  console.log(sut(t, 10) + sut(TUR[t].length, 14) + sut(d.length, 10) + d.reduce((a, x) => a + x.bagli.length, 0));
}

// ---- soru -> tur eslemesi ve yanlis turun biraktigi eksik bilgi ----
console.log("\nsoru  gereken oge                    dogru tur      en yakin yanlis tur");
console.log("-".repeat(78));
let tek = 0, coklu = 0, yok = 0, eksikToplam = 0;
for (const s of SORU) {
  const d = dogruTur(s);
  d.length === 1 ? tek++ : d.length > 1 ? coklu++ : yok++;
  const yanlis = TURLER.filter((t) => !d.includes(t)).map((t) => [t, eksik(t, s).length])
    .sort((a, b) => a[1] - b[1] || TURLER.indexOf(a[0]) - TURLER.indexOf(b[0]))[0];
  eksikToplam += yanlis[1];
  console.log(sut(s.id, 6) + sut(s.gerek.join("+"), 31) + sut(d.length ? d.join(",") : "- yok -", 15) + `${yanlis[0]} (${yanlis[1]} oge eksik)`);
}
console.log("-".repeat(78));
console.log(`${SORU.length} soru: tek dogru tur ${tek}, birden cok tur ${coklu}, hicbir tur ${yok}`);
console.log(`dort tur birlikte ${SORU.length - yok}/${SORU.length} soruyu yanitliyor`);
console.log(`en yakin yanlis tur secilseydi toplam ${eksikToplam} bilgi ogesi eksik kalirdi (soru basina ${(eksikToplam / SORU.length).toFixed(2)})`);
for (const s of SORU.filter((x) => dogruTur(x).length === 0))
  console.log(`  ${s.id}: gereken ${s.gerek.join("+")} — ` + TURLER.map((t) => `${t} ${eksik(t, s).join("/")} tasimaz`).join("; "));

// ---- her turun tek basina yanitladigi soru ----
console.log("");
for (const t of TURLER) {
  const y = SORU.filter((s) => dogruTur(s).includes(t));
  console.log(`${sut("yalniz " + t, 18)}yanit ${sut(`${y.length}/${SORU.length}`, 8)}(${y.map((s) => s.id).join(" ")})`);
}

// ---- bakim: 150 kod degisikligi ----
// AGIRLIK — kod ogesinin degisme sikligi (GR15); elle secilmistir
const AGIRLIK = {
  "odunc-ac": 9, "iade-al": 7, uzatma: 5, "sure-hesabi": 8, "limit-denetimi": 6, "gecikme-cezasi": 8,
  "ayirtma-sirasi": 5, "ayirtma-bildirimi": 4, "kopya-durumu": 6, "esitleme-isi": 3,
  "kuyruga-yaz": 3, "kuyruktan-oku": 3, "ileti-govdesi": 4, "ceza-birikimi": 5,
  "konu-tanimi": 2, abonelik: 2, "odunc-tablosu": 4, "ceza-tablosu": 3, "kopya-tablosu": 3,
  // hicbir diyagrama bagli olmayan kod ogeleri
  "raf-yeri": 4, "dis-sorgu": 5, "kayit-donusturme": 4, "uye-olustur": 4, "onbellek-anahtari": 3,
};
const OGELER = Object.keys(AGIRLIK);
function uretec(tohum) { let s = tohum >>> 0; return () => (s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32; }
const rast = uretec(20903); // tohum gorunur
const toplamAgirlik = OGELER.reduce((t, o) => t + AGIRLIK[o], 0);
const secOge = () => { let r = rast() * toplamAgirlik; for (const o of OGELER) if ((r -= AGIRLIK[o]) < 0) return o; return OGELER.at(-1); };

const N = 150;
const guncelleme = Object.fromEntries(TURLER.map((t) => [t, 0]));
let dokunulmayan = 0;
for (let i = 0; i < N; i++) {
  const o = secOge();
  let dokundu = false;
  for (const d of Object.values(DIYAGRAM)) if (d.bagli.includes(o)) { guncelleme[d.tur]++; dokundu = true; }
  if (!dokundu) dokunulmayan++;
}
console.log(`\n${N} kod degisikligi (tohum 20903); ${dokunulmayan} degisiklik hicbir diyagrama dokunmuyor`);
console.log(sut("tur", 10) + sut("guncellenen oge", 17) + sut("yanit", 8) + "yanit basina bakim");
console.log("-".repeat(56));
for (const t of TURLER) {
  const y = SORU.filter((s) => dogruTur(s).includes(t)).length;
  console.log(sut(t, 10) + sut(guncelleme[t], 17) + sut(y, 8) + (guncelleme[t] / y).toFixed(1));
}
const toplamGuncelleme = TURLER.reduce((a, t) => a + guncelleme[t], 0);
console.log("-".repeat(56));
console.log(sut("toplam", 10) + sut(toplamGuncelleme, 17) + sut(SORU.length - yok, 8) + (toplamGuncelleme / (SORU.length - yok)).toFixed(1));
```

```
tur       bilgi ogesi   diyagram  bagli oge
----------------------------------------------
yapi      4             2         11
sira      5             2         11
durum     4             2         7
dagitim   4             1         2

soru  gereken oge                    dogru tur      en yakin yanlis tur
------------------------------------------------------------------------------
U01   katilimci+statik-bag           yapi           sira (1 oge eksik)
U02   statik-bag+yon                 yapi           sira (2 oge eksik)
U03   katilimci+cokluk               yapi           sira (1 oge eksik)
U04   ileti+sira                     sira           yapi (2 oge eksik)
U05   ileti+es-zamansizlik           sira           yapi (2 oge eksik)
U06   sira+kosul-dali                sira           yapi (2 oge eksik)
U07   durum+gecis                    durum          yapi (2 oge eksik)
U08   gecis+tetikleyici              durum          yapi (2 oge eksik)
U09   gecis+koruma-kosulu            durum          yapi (2 oge eksik)
U10   yapi-eseri+yerlesim            dagitim        yapi (2 oge eksik)
U11   dugum+iletisim-yolu            dagitim        yapi (2 oge eksik)
U12   dugum                          dagitim        yapi (1 oge eksik)
U13   katilimci                      yapi,sira      durum (1 oge eksik)
U14   gecis+koruma-kosulu            durum          yapi (2 oge eksik)
U15   ileti+sira+durum               - yok -        sira (1 oge eksik)
U16   dugum+katilimci+ileti          - yok -        sira (1 oge eksik)
U17   ileti+sira                     sira           yapi (2 oge eksik)
U18   yapi-eseri+yerlesim+dugum      dagitim        yapi (3 oge eksik)
------------------------------------------------------------------------------
18 soru: tek dogru tur 15, birden cok tur 1, hicbir tur 2
dort tur birlikte 16/18 soruyu yanitliyor
en yakin yanlis tur secilseydi toplam 31 bilgi ogesi eksik kalirdi (soru basina 1.72)
  U15: gereken ileti+sira+durum — yapi ileti/sira/durum tasimaz; sira durum tasimaz; durum ileti/sira tasimaz; dagitim ileti/sira/durum tasimaz
  U16: gereken dugum+katilimci+ileti — yapi dugum/ileti tasimaz; sira dugum tasimaz; durum dugum/katilimci/ileti tasimaz; dagitim katilimci/ileti tasimaz

yalniz yapi       yanit 4/18    (U01 U02 U03 U13)
yalniz sira       yanit 5/18    (U04 U05 U06 U13 U17)
yalniz durum      yanit 4/18    (U07 U08 U09 U14)
yalniz dagitim    yanit 4/18    (U10 U11 U12 U18)

150 kod degisikligi (tohum 20903); 24 degisiklik hicbir diyagrama dokunmuyor
tur       guncellenen oge  yanit   yanit basina bakim
--------------------------------------------------------
yapi      97               4       24.3
sira      74               5       14.8
durum     59               4       14.8
dagitim   11               4       2.8
--------------------------------------------------------
toplam    241              16      15.1
```

#### Soru Başına Doğru Tür

On sekiz sorunun **on beşinde tek bir doğru tür** var. Bir soru iki türle birden yanıtlanıyor:
ödünç akışında hangi katılımcıların bulunduğunu soran U13, hem yapı hem sıra gösteren diyagramla
yanıtlanabiliyor, çünkü katılımcı iki türde birden bulunan tek ögedir. İki soru hiçbir türle
yanıtlanmıyor.

Tür seçiminin bir tercih olmadığı buradan okunur. Ödünç açma akışında ilk çağrının hangisi olduğu
sorulduğunda sıra gösteren diyagram bir seçenek değil, **tek seçenektir**; sıra ögesini taşıyan
başka tür yok. Aynı biçimde çokluğu yalnız yapı gösteren, koruma koşulunu yalnız durum gösteren,
iletişim yolunu yalnız dağıtım gösteren diyagram taşır. Bir soru sorulduğunda hangi diyagramın
çizileceği tartışma konusu değildir; sorunun gerektirdiği bilgi ögeleri türü belirler.

Tek türle yetinen bir belge dört ile beş soru arasında yanıt verir. Sıra gösteren tür en yüksek
paya sahip (5/18), çünkü öge listesi en uzun olan tür odur. Dördü birlikte on altı soruyu
yanıtlıyor.

#### Yanlış Türün Bıraktığı Eksik

Asıl ölçü sağdaki sütunda. Her soru için, doğru tür yerine seçilebilecek **en yakın yanlış tür** ve
o türün taşımadığı öge sayısı yazılı. Toplam 31 bilgi ögesi, soru başına 1,72.

Sayı iki nedenle önemli. Birincisi, yanlış tür soruyu yanıtsız bırakmaz — **yarı yanıtlı** bırakır.
Ödünç akışının ilk çağrısı sorulduğunda yapı gösteren diyagram katılımcıları listeler; okuyucu bir
şey görür, hatta ilgili bir şey görür, ama sıra bilgisi orada yoktur. Bulunan yanıtın eksik olduğu
belli değildir; eksik olan öge, olmadığı için görünmez.

İkincisi, en yakın yanlış tür bazen çok yakındır. U15 gecikme cezasını hangi iletinin hangi durumda
tetiklediğini soruyor; sıra gösteren diyagram üç gerekli ögeden ikisini taşıyor, yalnız durum
ögesini taşımıyor. Tek bir öge eksik, ama eksik olan öge sorunun yarısıdır. U16 aynı biçimde sıra
gösteren diyagrama bir öge uzaklıkta: düğüm.

Bu iki soru hiçbir türle yanıtlanmıyor ve nedeni önceki dersinkiyle aynı: **soru iki türün
ögelerini birleştirmeyi istiyor.** Çalışma anındaki bir iletiyi bir duruma bağlamak, ya da bir
iletiyi gönderen katılımcıyı bir düğüme bağlamak, tek bir türün taşıyabileceğinden fazlasıdır.
Türler çoğaltılarak kapatılabilecek bir boşluk değildir; her yeni tür kendi bilgi kümesiyle gelir
ve kümeler arasında yine boşluk kalır.

#### Bakım Maliyeti

Alt tablo diyagramları koda bağlayarak ölçüyor. Yüz elli kod değişikliği üretiliyor; öge seçimi
elle yazılmış bir ağırlık tablosundan geliyor (**GR15**) ve tohum 20903 görünür. Bir değişiklik,
değişen kod ögesine bağlı her diyagramda bir öge güncellemesi gerektiriyor.

Yüz elli değişikliğin **24'ü hiçbir diyagrama dokunmuyor** — kodun bir kısmı hiçbir diyagramda
görünmüyor. Kalan değişiklikler toplam 241 diyagram ögesi güncellemesi doğuruyor.

Türler arasındaki fark büyük. Yapı gösteren tür 97 güncelleme istiyor ve dört soru yanıtlıyor:
yanıt başına 24,3. Dağıtım gösteren tür 11 güncelleme istiyor ve yine dört soru yanıtlıyor: yanıt
başına 2,8. Aradaki dokuz katlık fark, çizim ustalığından değil **bağlanma sıklığından** geliyor.
Yapı gösteren diyagram doğrudan kod ögelerine bağlıdır ve kod en sık değişen şeydir; dağıtım
gösteren diyagram düğüme ve kaba bağlıdır, onlar seyrek değişir.

Buradan çıkan kural şudur: bir diyagramın değeri yanıtladığı soru sayısıysa, maliyeti kod
değişikliği başına güncellenen öge sayısıdır ve ikisinin oranı türden türe dokuz kat değişir. Bir
belge kümesi kısılacaksa kesilecek yer yanıt başına bakımı en yüksek olan yerdir — bu modelde yapı
gösteren diyagramlar. Sayılar modelin girdilerine bağlıdır; bağlı öge listeleri daraltılırsa
maliyet düşer, ama yanıtlanan soru da değişmez, çünkü yanıt öge listesinden değil türün bilgi
kümesinden gelir.

#### Özet

- Diyagram türleri gösterim adıyla değil taşıdıkları bilgi ögesiyle ayrılır: yapı gösteren tür
  durağan bağı ve çokluğu, sıra gösteren tür iletiyi ve sırayı, durum gösteren tür geçişi ve koruma
  koşulunu, dağıtım gösteren tür düğümü ve yerleşimi taşır; on altı ayrı ögeden yalnız katılımcı
  iki türde birden bulunur.
- On sekiz sorunun 15'inde tek bir doğru tür var, biri iki türle yanıtlanıyor, ikisi hiçbir türle
  yanıtlanmıyor; dört tür birlikte 16/18 soruyu yanıtlıyor, tek tür 4 ile 5 arasında.
- Yanlış tür soruyu yanıtsız değil yarı yanıtlı bırakır: en yakın yanlış tür seçilseydi toplam 31
  bilgi ögesi eksik kalırdı, soru başına 1,72. U15'te sıra gösteren tür üç ögeden ikisini taşıyor,
  eksik olan tek öge sorunun yarısıdır.
- Hiçbir türle yanıtlanamayan iki soru, iki türün ögelerini birleştirmeyi istiyor; tür eklemek bu
  boşluğu kapatmaz, yeni kümeler arasında yenisini açar.
- Tohumu 20903 olan üreteçle 150 kod değişikliğinin 24'ü hiçbir diyagrama dokunmuyor; kalanlar 241
  öge güncellemesi doğuruyor. Yanıt başına bakım yapı gösteren türde 24,3, sıra ve durum gösteren
  türde 14,8, dağıtım gösteren türde 2,8 — dokuz katlık fark bağlanma sıklığından gelir.

#### Sonraki Adım

Üç ders de aynı biçimde soru sordu: bir şeyin neye bağlı olduğu, hangi sırayla çağrıldığı, nerede
koştuğu. Hepsinin yanıtı sistemin **yapısında** duruyor ve bakılacak yer bilindiğinde kesin bir
yanıt çıkıyor.

Sonradan gelen birinin sorularının bir kısmı bu türden değil. "Ağ yoğun saatte yeterince hızlı mı",
"bir şube kopunca ödünç verme durur mu", "bu sistem yeni bir şube eklemeye ne kadar açık" soruları
hiçbir diyagramda düğüm ya da kenar olarak durmaz. Bunlar sistemin yapısı hakkında değil
**davranışının niteliği** hakkındadır ve bu hâlleriyle sınanamazlar: "yeterince hızlı" bir ölçü
değildir. Sonraki ders bu soruları uyaran, ortam, yanıt ve yanıt ölçüsü alanları olan senaryolara
çevirir ve şunu ölçer: alanları dolu senaryo sayısı, ölçüsü yazılmadığı için sınanamayan senaryo
sayısı.

### Kalite Nitelik Senaryoları

Source: https://academia.sh/tr/kurslar/mimari-dokumantasyon/kalite-nitelik-senaryolari

Kalite gereksiniminin sınanabilir yazılması: aynı on iki gereksinimin serbest cümle ve alanlı senaryo biçiminde yazılıp kaçının sınanabildiğinin sayılması, boş kalan alanların soruya çevrilmesi ve eşiği yazılmamış gereksinimin teslimde doğurduğu anlaşmazlığın ölçülmesi.

Önceki dersler görünümleri ve diyagram türlerini soruya göre ayırdı: hangi resmin hangi soruyu
yanıtladığı artık belli. Ama sonradan gelen birinin sorduğu soruların bir bölümü resimle
yanıtlanamaz. "Bu sistem yeterince hızlı mı", "bu şube kopukken ne olacak", "bu yavaşlık kabul
edilir mi" türünden sorular bir kutu-ok resmine bakılarak kapanmaz; bunların yanıtı bir sayıdır.
Bu ders o sayının nereye yazıldığını ve yazılmadığında ne olduğunu ölçer.

Kalite niteliğinin ne olduğu ve nitelik senaryosunun hangi alanlardan oluştuğu Kalite ve Test
Temelleri kursunda tanımlandı; burada o tanım tekrarlanmaz. Buradaki soru dardır: elde bir
gereksinim listesi varken, o listenin kaç maddesi **sınanabilir**. Sınanabilirlik burada bir
görüş değil bir ölçüttür — bir gereksinimden sınama yordamı yazılabiliyorsa sınanabilirdir ve
bir yordam için tetiklenecek bir uyaran, kurulacak bir ortam, gözlenecek bir yanıt ve
karşılaştırılacak bir eşik gerekir. Hangi kalite niteliklerinin bir sistem için önemli olduğu
ayrı bir konudur ve bu kursun sonraki kursunda ele alınır; burada nitelik listesi çıkarılmaz.

#### Ölçülecek Gereksinim Kümesi

Aşağıdaki blok on iki kalite gereksinimini iki biçimde **modeller**: serbest cümle ve alanlı
senaryo. Alanlı biçim, serbest cümlenin söylediği kadarını taşır — cümlede olmayan hiçbir bilgi
alana yazılmaz. Model diske de yazılır; sonraki iki blok aynı kümeyi okur.

```js
// kume.mjs — gereksinim kumesini iki bicimde modeller ve sinanabilirligi olcer
import { mkdirSync, writeFileSync } from "node:fs";

// GR16: bolge kutuphane agindan toplanan on iki kalite gereksinimi. Alanli bicim,
// serbest cumlenin soyledigi kadarini tasir; hicbir alan uydurulmaz.
const GEREKSINIM = [
  ["Katalog aramasi hizli yanit vermeli.",
    "uye katalogda arama yapar", "", "sonuc listesi doner", "", ""],
  ["Sube baglantisi koptugunda odunc verme durmamali.",
    "sube ile merkez baglantisi kopar", "", "odunc verme surer", "", ""],
  ["Yogun saatte sistem ayakta kalmali.",
    "", "yogun saat", "sistem istek almayi surdurur", "", ""],
  ["Odunc kaydi kaybolmamali.",
    "", "", "kayit okunabilir kalir", "", ""],
  ["Gecikme bildirimi zamaninda gitmeli.",
    "odunc suresi dolar", "", "bildirim gonderilir", "", ""],
  ["Katalog toplu yuklemesi gece penceresine sigmali.",
    "toplu yukleme baslar", "gece bakim penceresi", "yukleme tamamlanir", "", ""],
  ["Uye kendi odunc gecmisini beklemeden gorebilmeli.",
    "uye gecmis sayfasini acar", "", "gecmis listelenir", "", ""],
  ["Odunc alma islemi, 500 eszamanli uye altinda isteklerin %95'inde 400 ms icinde tamamlanmali.",
    "uye odunc istegi gonderir", "500 eszamanli uye", "odunc kaydi olusur",
    "isteklerin %95'i 400 ms altinda", ""],
  ["Merkez veritabani dugumu kaybedildiginde odunc servisi 60 saniye icinde yedege gecmeli.",
    "birincil veritabani dugumu yanit vermez", "is saati yuku", "servis yedek dugumle calisir",
    "gecis 60 saniye icinde", ""],
  ["Yeni sube tanimi uretime tek dagitimla 1 saat icinde alinmali.",
    "sube tanimi eklenir", "tek dagitim penceresi", "sube uretimde gorunur",
    "eklemeden yayina 1 saat", "sube acilis takvimi: ayda iki sube"],
  ["Sistem yedeklenebilir olmali.",
    "", "", "", "", ""],
  ["Rapor ekrani makul surede acilmali.",
    "yonetici rapor ekranini acar", "", "rapor goruntulenir", "", ""],
];

const ALAN = ["uyaran", "ortam", "yanit", "olcu", "kaynak"];
const KUME = GEREKSINIM.map(([serbest, ...deger], i) => ({
  no: i + 1, serbest, alan: Object.fromEntries(ALAN.map((a, j) => [a, deger[j]])),
}));

mkdirSync("kume", { recursive: true });
writeFileSync("kume/kume.mjs",
  `export const ALAN = ${JSON.stringify(ALAN)};\n` +
  `export const KUME = ${JSON.stringify(KUME, null, 0)};\n`);

// Sinanabilirlik olcutu: bir sinama yordami yazilabilmesi icin tetiklenecek uyaran,
// kurulacak ortam, gozlenecek yanit ve karsilastirilacak esik gerekir. Esik, birim
// tasiyan bir sayidir; iki bicimde de ayni duzenli ifadeyle aranir.
const ESIK = /(\d+\s*(ms|saniye|dakika|saat|gun)|%\s*\d+|%\d+)/;
const KOSUL = /(koptugunda|kaybedildiginde|altinda|penceresi|penceresine|dagitimla|eszamanli|yogun|yuku)/;
const DORT = ["uyaran", "ortam", "yanit", "olcu"];

const serbestSinanabilir = (g) => ESIK.test(g.serbest) && KOSUL.test(g.serbest);
const alanliSinanabilir = (g) =>
  DORT.every((a) => g.alan[a] !== "") && ESIK.test(g.alan.olcu);

console.log(`${"no".padStart(2)}  ${"serbest bicim".padEnd(14)}${"alanli bicim (dolu alan)".padEnd(26)}bos alan`);
for (const g of KUME) {
  const dolu = DORT.filter((a) => g.alan[a] !== "");
  console.log(`${String(g.no).padStart(2)}  ` +
    `${(serbestSinanabilir(g) ? "sinanabilir" : "-").padEnd(14)}` +
    `${(dolu.join(",") || "-").padEnd(26)}` +
    `${4 - dolu.length}`);
}

const bosDort = KUME.reduce((s, g) => s + DORT.filter((a) => g.alan[a] === "").length, 0);
const bosBes = KUME.reduce((s, g) => s + ALAN.filter((a) => g.alan[a] === "").length, 0);
const yaziSerbest = KUME.reduce((s, g) => s + g.serbest.length, 0);
const yaziAlanli = KUME.reduce((s, g) =>
  s + ALAN.reduce((t, a) => t + a.length + 2 + g.alan[a].length, 0), 0);

console.log(`\nsinanabilir: serbest bicimde ${KUME.filter(serbestSinanabilir).length}/${KUME.length}, ` +
  `alanli bicimde ${KUME.filter(alanliSinanabilir).length}/${KUME.length}`);
console.log(`bos alan: dort alanda ${bosDort}, besinci alan (kaynak) dahil ${bosBes}`);
console.log(`yazim maliyeti (karakter): serbest ${yaziSerbest}, alanli ${yaziAlanli} ` +
  `(${(yaziAlanli / yaziSerbest).toFixed(2)} kat)`);
```

```
no  serbest bicim alanli bicim (dolu alan)  bos alan
 1  -             uyaran,yanit              2
 2  -             uyaran,yanit              2
 3  -             ortam,yanit               2
 4  -             yanit                     3
 5  -             uyaran,yanit              2
 6  -             uyaran,ortam,yanit        1
 7  -             uyaran,yanit              2
 8  sinanabilir   uyaran,ortam,yanit,olcu   0
 9  sinanabilir   uyaran,ortam,yanit,olcu   0
10  sinanabilir   uyaran,ortam,yanit,olcu   0
11  -             -                         4
12  -             uyaran,yanit              2

sinanabilir: serbest bicimde 3/12, alanli bicimde 3/12
bos alan: dort alanda 20, besinci alan (kaynak) dahil 31
yazim maliyeti (karakter): serbest 584, alanli 1079 (1.85 kat)
```

#### Alanlı Biçim Neyi Değiştirir

İki biçimde de sınanabilir gereksinim sayısı üçtür. Bu sonuç, alanlı senaryoya yüklenen en yaygın
beklentiyi yıkar: **alan açmak bilgi üretmez.** Cümlede eşik yoksa alanda da yoktur; biçim
değiştirmek bir gereksinimi sınanabilir kılmıyor.

Değişen şey başka: serbest biçim, sınanamayan dokuz gereksinim için tek bir bilgi veriyor — sayı
yok. Alanlı biçim her biri için **hangi alanın** boş olduğunu veriyor. Dördüncü gereksinim üç
alan eksik, on birincisi dört alanın dördünde de boş, altıncısında yalnız ölçü eksik. Bu, kusurun
büyüklüğünü ve türünü ayıran bir ölçüdür; serbest cümlede ikisi de görünmez.

Boş alan sayısı yirmidir ve bu sayı doğrudan bir soru listesidir. Alanlı biçimin yazım maliyeti
serbest biçimin 1,85 katı: 584 karaktere karşı 1.079 karakter. Bedel bu, karşılığı da şu — yirmi
sorunun tek tek nereye sorulacağı belli.

Beşinci alan burada ayrı durur. `kaynak`, eşiğin nereden geldiğini tutar ve on iki gereksinimin
yalnız birinde dolu. Sınanabilirlik ölçütü ilk dört alana bakar, çünkü sınama yordamı eşiğin
kaynağını bilmeden de yazılabilir. Beşinci alanın neden ayrıca gerektiği bu dersin son bölümünde
ölçülür.

#### Yazılmamış Eşiğin Bedeli

Sınanamayan bir gereksinim hemen bir bedel ödetmez. Bedeli teslim anında çıkar. Eşik yazılmadığı
zaman ortadan kalkmaz; iki tarafın kafasında iki ayrı eşik olarak durur. İsteyen taraf "hızlı"
derken bir sayı düşünür, geliştiren taraf başka bir sayı. Aşağıdaki blok bu durumu modelliyor:
her gereksinime küçüğün iyi olduğu bir ölçek, iki tarafa birer eşik ve teslim edilen sisteme
ölçülmüş bir değer veriliyor.

```js
// anlasmazlik.mjs — kume.mjs'in yazdigi kume/kume.mjs'i okur; sinanamayan gereksinimin
// teslimde dogurdugu anlasmazligi olcer
import { KUME } from "./kume/kume.mjs";

// GR17: her gereksinimin kucugun iyi oldugu bir olcegi vardir; sinanamayan gereksinimde
// isteyen ile gelistiren yazili olmayan iki ayri esik tasir. Sinanabilir uc gereksinimde
// esik yazilidir, iki taraf da ayni sayiyi kullanir.
const ESIK = [                     // [olcek, isteyenin esigi, gelistirenin esigi, teslim araligi]
  ["arama yanit suresi (ms)", 300, 1200, [200, 1500]],
  ["odunc durma suresi (dk)", 5, 120, [0, 180]],
  ["reddedilen istek (binde)", 1, 20, [0, 40]],
  ["yillik kayip kayit", 0, 3, [0, 6]],
  ["bildirim gecikmesi (saat)", 1, 24, [0, 36]],
  ["toplu yukleme suresi (dk)", 60, 240, [30, 300]],
  ["gecmis sayfasi (ms)", 500, 2500, [300, 3000]],
  ["odunc islemi p95 (ms)", 400, 400, [250, 900]],
  ["yedege gecis (saniye)", 60, 60, [20, 180]],
  ["yayina alma (dakika)", 60, 60, [20, 240]],
  ["yedekten donus (saat)", 2, 24, [1, 48]],
  ["rapor ekrani (saniye)", 3, 20, [1, 30]],
];

// GR18: teslim olcumu, her gereksinim icin kendi araligindan duzgun dagilimla cekilir.
// Uretec kendi yazilmistir; tohum gorunurdur.
const uretec = (tohum) => () => {
  tohum = (tohum + 0x6d2b79f5) | 0;
  let t = Math.imul(tohum ^ (tohum >>> 15), 1 | tohum);
  t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
  return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
const TOHUM = 20802;
const TESLIM = 200;
const rastgele = uretec(TOHUM);

const sonuc = ESIK.map(() => ({ kabul: 0, anlasmazlik: 0, red: 0 }));
let teslimBasinaAnlasmazlik = 0;
for (let t = 0; t < TESLIM; t += 1) {
  let n = 0;
  ESIK.forEach(([, isteyen, gelistiren, [alt, ust]], i) => {
    const olculen = alt + rastgele() * (ust - alt);
    if (olculen <= Math.min(isteyen, gelistiren)) sonuc[i].kabul += 1;
    else if (olculen > Math.max(isteyen, gelistiren)) sonuc[i].red += 1;
    else { sonuc[i].anlasmazlik += 1; n += 1; }
  });
  teslimBasinaAnlasmazlik += n;
}

console.log(`tohum ${TOHUM}, ${TESLIM} teslim; her satir bir gereksinim (teslim sayisi)`);
console.log(`${"no".padStart(2)}  ${"olcek".padEnd(26)}${"esik".padStart(12)}` +
  `${"kabul".padStart(8)}${"anlasmazlik".padStart(13)}${"red".padStart(7)}`);
ESIK.forEach(([olcek, isteyen, gelistiren], i) => {
  console.log(`${String(i + 1).padStart(2)}  ${olcek.padEnd(26)}` +
    `${(isteyen === gelistiren ? `${isteyen}` : `${isteyen}/${gelistiren}`).padStart(12)}` +
    `${String(sonuc[i].kabul).padStart(8)}${String(sonuc[i].anlasmazlik).padStart(13)}` +
    `${String(sonuc[i].red).padStart(7)}`);
});

const yazili = ESIK.map(([, a, b], i) => (a === b ? i : -1)).filter((i) => i >= 0);
const yazisiz = ESIK.map((_, i) => i).filter((i) => !yazili.includes(i));
const topla = (dizi) => dizi.reduce((s, i) => s + sonuc[i].anlasmazlik, 0);
console.log(`\nesigi yazili ${yazili.length} gereksinim: ${topla(yazili)} anlasmazlik`);
console.log(`esigi yazilmamis ${yazisiz.length} gereksinim: ${topla(yazisiz)} anlasmazlik`);
console.log(`teslim basina ortalama anlasmazlik: ` +
  `${(teslimBasinaAnlasmazlik / TESLIM).toFixed(2)} (${KUME.length} gereksinimin uzerinden)`);
```

```
tohum 20802, 200 teslim; her satir bir gereksinim (teslim sayisi)
no  olcek                             esik   kabul  anlasmazlik    red
 1  arama yanit suresi (ms)       300/1200      12          142     46
 2  odunc durma suresi (dk)          5/120      10          112     78
 3  reddedilen istek (binde)          1/20       3           99     98
 4  yillik kayip kayit                 0/3       0           90    110
 5  bildirim gecikmesi (saat)         1/24       5          117     78
 6  toplu yukleme suresi (dk)       60/240      13          148     39
 7  gecmis sayfasi (ms)           500/2500       8          158     34
 8  odunc islemi p95 (ms)              400      45            0    155
 9  yedege gecis (saniye)               60      56            0    144
10  yayina alma (dakika)                60      35            0    165
11  yedekten donus (saat)             2/24       5           92    103
12  rapor ekrani (saniye)             3/20       9          104     87

esigi yazili 3 gereksinim: 0 anlasmazlik
esigi yazilmamis 9 gereksinim: 1062 anlasmazlik
teslim basina ortalama anlasmazlik: 5.31 (12 gereksinimin uzerinden)
```

Eşiği yazılı üç gereksinimde anlaşmazlık sıfırdır ve bu sıfır modelin yapısal sonucudur: tek eşik
varsa iki taraf aynı ölçüme bakıp aynı sonuca varır. Sekizinci gereksinim iki yüz teslimin
155'inde reddedilmiş; bu bir başarısızlıktır ama tartışmasızdır. Red bir anlaşmazlık değil bir
karardır.

Eşiği yazılmamış dokuz gereksinim iki yüz teslimde toplam 1.062 anlaşmazlık üretiyor — teslim
başına 5,31. Yani her teslimde, on iki gereksinimin beşinden fazlası "sağlandı mı sağlanmadı mı"
sorusuna iki farklı yanıt alıyor. En yüksek oran yedinci gereksinimde: iki yüz teslimin 158'i
anlaşmazlıkla bitiyor. Nedeni ölçekte görünüyor — iki eşik arasındaki aralık (500 ile 2.500 ms)
teslim aralığının büyük bölümünü kaplıyor. **Anlaşmazlık olasılığı, iki tarafın örtük eşikleri
arasındaki açıklıkla büyür.** Cümle ne kadar belirsizse açıklık o kadar geniştir.

Bu, dersin maliyet karşılaştırmasını verir. Kümeyi alanlı biçimde yazmanın bedeli 495 fazla
karakter ve yirmi sorunun sorulmasıdır; bu bedel bir kez ödenir. Sınanamayan bırakmanın bedeli
teslim başına 5,31 anlaşmazlıktır ve her teslimde yeniden ödenir.

#### Soru Kümesi ve Beşinci Alan

Bu kursun ölçüsü, bir belgenin sonradan gelen birinin sorduğu soruların kaçını yanıtladığıdır.
Kalite gereksinimlerine sorulan sorular beş tanedir ve her biri bir alana düşer.

```js
// soru.mjs — kume.mjs'in yazdigi kume/kume.mjs'i okur; soru kumesini kumeye uygular
import { KUME } from "./kume/kume.mjs";

// Sonradan gelen birinin bir kalite gereksinimine sordugu bes soru; her soru bir alana duser.
const SORU = [
  ["Bu gereksinim ne zaman saglanmis sayilir?", "olcu"],
  ["Hangi kosulda olculecek?", "ortam"],
  ["Neyi tetikleyerek sinanacak?", "uyaran"],
  ["Sistemden ne gozlenecek?", "yanit"],
  ["Bu sayi nereden geldi?", "kaynak"],
];

console.log(`${"soru".padEnd(42)}${"yanitlanan".padStart(11)}${"yanitlanamayan".padStart(16)}`);
let yanitlanan = 0;
for (const [soru, alan] of SORU) {
  const d = KUME.filter((g) => g.alan[alan] !== "").length;
  yanitlanan += d;
  console.log(`${soru.padEnd(42)}${`${d}/${KUME.length}`.padStart(11)}` +
    `${String(KUME.length - d).padStart(16)}`);
}
const toplam = SORU.length * KUME.length;
console.log(`toplam ${toplam} soru: ${yanitlanan} yanitlanan, ${toplam - yanitlanan} yanitlanamayan`);

// Bir gereksinimi sinanabilir kilmak icin sorulacak soru sayisi (besinci alan disarida:
// sinanabilirlik dort alana bakar).
const DORT = ["olcu", "ortam", "uyaran", "yanit"];
const eksik = KUME.map((g) => [g.no, DORT.filter((a) => g.alan[a] === "").length,
  g.serbest.slice(0, 34)]).sort((a, b) => a[1] - b[1] || a[0] - b[0]);
console.log(`\n${"no".padStart(2)}  ${"sorulacak soru".padStart(14)}  gereksinim`);
for (const [no, n, c] of eksik) console.log(`${String(no).padStart(2)}  ${String(n).padStart(14)}  ${c}`);
console.log(`kumeyi sinanabilir kilmak: ${eksik.reduce((s, [, n]) => s + n, 0)} soru; ` +
  `tek soruyla kapanan ${eksik.filter(([, n]) => n === 1).length} gereksinim, ` +
  `uc ya da daha fazla soru gereken ${eksik.filter(([, n]) => n >= 3).length} gereksinim`);
```

```
soru                                       yanitlanan  yanitlanamayan
Bu gereksinim ne zaman saglanmis sayilir?        3/12               9
Hangi kosulda olculecek?                         5/12               7
Neyi tetikleyerek sinanacak?                     9/12               3
Sistemden ne gozlenecek?                        11/12               1
Bu sayi nereden geldi?                           1/12              11
toplam 60 soru: 29 yanitlanan, 31 yanitlanamayan

no  sorulacak soru  gereksinim
 8               0  Odunc alma islemi, 500 eszamanli u
 9               0  Merkez veritabani dugumu kaybedild
10               0  Yeni sube tanimi uretime tek dagit
 6               1  Katalog toplu yuklemesi gece pence
 1               2  Katalog aramasi hizli yanit vermel
 2               2  Sube baglantisi koptugunda odunc v
 3               2  Yogun saatte sistem ayakta kalmali
 5               2  Gecikme bildirimi zamaninda gitmel
 7               2  Uye kendi odunc gecmisini beklemed
12               2  Rapor ekrani makul surede acilmali
 4               3  Odunc kaydi kaybolmamali.
11               4  Sistem yedeklenebilir olmali.
kumeyi sinanabilir kilmak: 20 soru; tek soruyla kapanan 1 gereksinim, uc ya da daha fazla soru gereken 2 gereksinim
```

Altmış sorunun 29'u yanıtlanıyor, 31'i yanıtlanamıyor. Soruların yanıtlanma oranı düzenli biçimde
düşüyor: sistemden ne gözleneceği on iki gereksinimin on birinde belli, neyin tetikleneceği
dokuzunda, hangi koşulda ölçüleceği beşinde, ne zaman sağlanmış sayılacağı yalnız üçünde. Yani
gereksinim yazan kişi **ne istediğini** anlatabiliyor, **ne zaman yeteceğini** anlatamıyor.

En düşük satır beşinci alandır: eşiğin kaynağı on iki gereksinimin yalnız birinde yazılı. Eşiği
yazılı üç gereksinimden ikisinde bile sayının nereden geldiği kayıtlı değil. Bu ayrım küçük
görünür ama sonucu ağırdır: 400 ms eşiği sınanabilir bir eşiktir, ancak altı ay sonra "bu eşik
yükseltilebilir mi" sorusu geldiğinde yanıtlanamaz. Kaynağı yazılı olan tek gereksinimde
(şube açılış takvimi, ayda iki şube) bu soru yanıtlanır: takvim seyrekleşirse eşik gevşetilebilir.
**Ölçü, gereksinimin sınanmasını sağlar; eşiğin kaynağı, gereksinimin değiştirilmesini sağlar.**

Son tablo yirmi sorunun nasıl dağıldığını gösterir: üçü zaten sınanabilir, biri tek soruyla
kapanıyor, altısı ikişer soru istiyor, ikisi üç ve dört soru istiyor. On birinci gereksinim
aslında bir gereksinim değil bir başlıktır; dört alanının dördü de boş olduğu için tek soruyla
değil ayrı bir çalışmayla doldurulur.

#### Özet

- Sınanabilirlik bir ölçüttür: bir gereksinimden sınama yordamı yazılabiliyorsa sınanabilirdir;
  yordam için uyaran, ortam, yanıt ve birim taşıyan bir eşik gerekir.
- Aynı on iki gereksinim iki biçimde de 3/12 sınanabilir çıkıyor — alanlı biçim bilgi üretmez,
  eksik bilgiyi görünür kılar: 20 boş alan, yani 20 tek tek sorulabilir soru.
- Alanlı biçimin yazım maliyeti 1,85 kat (584'e karşı 1.079 karakter) ve bir kez ödenir; eşiği
  yazılmamış dokuz gereksinim 200 teslimde 1.062 anlaşmazlık, teslim başına 5,31 anlaşmazlık üretiyor.
- Anlaşmazlık olasılığı iki tarafın örtük eşikleri arasındaki açıklıkla büyür; eşiği yazılı üç
  gereksinimde anlaşmazlık sıfırdır, red bile tartışmasızdır.
- Beş sorunun 60 uygulamasından 31'i yanıtsız kalıyor; en yanıtsız soru "bu sayı nereden geldi"
  (11/12 yanıtsız) — ölçü sınamayı, eşiğin kaynağı değiştirmeyi mümkün kılar.

#### Sonraki Adım

Gereksinim kümesi ölçülebilir hale getirildiğinde ortaya bir belge çıkar: alanları dolu senaryolar,
eşikler, kaynaklar. Bu belge yazıldığı gün kodla uyumludur. Sonraki ders o günden sonrasını ölçer.
Kod değişmeye devam eder, belge yerinde durur ve aradaki fark büyür. Bu fark tahmin edilerek değil
sayılarak bulunur: bir modül grafiğinden çıkarılan gerçek ile belgedeki iddia karşılaştırılır, bir
dizi değişiklik uygulandıkça sapmanın nasıl arttığı izlenir ve iki güncel tutma düzeni — elle
güncelleme ile belgeden üretilen denetim — yakaladıkları sapma, ödettikleri bakım maliyeti ve
ürettikleri yanlış alarm üzerinden karşılaştırılır.

### Belgenin Güncel Kalması

Source: https://academia.sh/tr/kurslar/mimari-dokumantasyon/belgenin-guncel-kalmasi

Belge sapmasının gerçekten ölçülmesi: bir modül ithal grafiğinden çıkarılan gerçek ile belgedeki iddianın karşılaştırılması, sekiz değişiklik boyunca sapmanın büyümesi ve üç güncel tutma düzeninin yakaladığı sapma, ödettiği bakım maliyeti ve ürettiği yanlış alarm üzerinden karşılaştırılması.

Önceki ders kalite gereksinimlerini ölçülebilir hale getirdi ve ortaya bir belge çıktı; o belge
yazıldığı gün kodla uyumluydu. Bu ders sonrasını ölçer: kod değişmeye devam eder, belge yerinde
durur, aradaki fark büyür. Bu farkın adı **belge sapması** (documentation drift) ve tahmin
edilerek değil sayılarak bulunur.

Ölçüm yöntemi ödünç alınmıştır. Servis Mimarileri kursunun sınır çizme dersinde ithal kenarları
kaynak dosyalardan çıkarılıp sınır ihlali sayılmıştı. Aynı çıkarım burada koddan okunan grafiği
belgenin iddiasıyla karşılaştırmak için kullanılır; ölçülen nicelik farklıdır.

#### Kod, Belge ve Denetim

Aşağıdaki blok üç şeyi tanımlar: bölgesel kütüphane ağı sisteminin modül haritasını, on dört
iddiadan oluşan belgeyi ve iddiaları koda karşı sınayan denetimi. Modül haritası bir **modeldir**;
her modül kaynak dosya olarak diske yazılır ve grafik o dosyalardan çıkarılır — ölçüm gerçek
dosyalar üzerinde yapılır.

İddiaların üçe ayrılması dersin eksenidir: **kural** iddiası mimari niyeti söyler, kod ona
uymazsa sapma vardır; **anlık** iddia kodun o günkü hâlinin sayımıdır, kod değişince bayatlar;
**belirsiz** iddia koddan denetlenemez.

```js
// ortam.mjs — kod tabanini, belgeyi ve denetimi tanimlar; dogrudan calistirildiginda
// baslangic durumunu raporlar. Sonraki iki blok bu dosyayi ithal eder.
import { mkdirSync, rmSync, writeFileSync, readdirSync, readFileSync } from "node:fs";

// GR19: bolgesel kutuphane agi sisteminin alti alan modulu. Her modul kaynaginda hangi
// modulleri ithal ettigini, hangi tablolara yazdigini ve hangi tablonun sahibi oldugunu tasir.
export const BASLANGIC = {
  sube:     { ithal: [],                   yazar: ["sube"],          sahip: ["sube"] },
  katalog:  { ithal: [],                   yazar: ["kitap", "nusha"], sahip: ["kitap", "nusha"] },
  uyelik:   { ithal: [],                   yazar: ["uye"],           sahip: ["uye"] },
  odunc:    { ithal: ["katalog", "uyelik"], yazar: ["odunc"],        sahip: ["odunc"] },
  bildirim: { ithal: ["uyelik", "odunc"],  yazar: ["bildirim"],      sahip: ["bildirim"] },
  ucret:    { ithal: ["odunc"],            yazar: ["ucret"],         sahip: ["ucret"] },
};

export const yaz = (m) => {              // kod/ dizini her yazimda bastan olusturulur
  rmSync("kod", { recursive: true, force: true });
  mkdirSync("kod", { recursive: true });
  for (const [ad, x] of Object.entries(m)) {
    writeFileSync(`kod/${ad}.mjs`,
      `// sahip: ${x.sahip.join(", ")}\n` +
      x.ithal.map((i) => `import { ${i}Islem } from "./${i}.mjs";\n`).join("") +
      `export const yaz = (t, v) => ({ t, v });\n` +
      `export function ${ad}Islem(v) {\n` +
      x.yazar.map((t) => `  yaz("${t}", v);\n`).join("") + "}\n");
  }
};

// Ithal grafigi kaynak dosyalardan cikarilir. Yontem Servis Mimarileri kursunun sinir cizme
// dersinden odunc alinmistir; orada sinir ihlali olculmustu, burada belge sapmasi olculuyor.
export const grafik = () => Object.fromEntries(readdirSync("kod").map((d) => {
  const t = readFileSync(`kod/${d}`, "utf8");
  return [d.replace(".mjs", ""), {
    ithal: [...t.matchAll(/from "\.\/(\w+)\.mjs"/g)].map((x) => x[1]),
    yazar: [...t.matchAll(/yaz\("(\w+)"/g)].map((x) => x[1]),
    sahip: t.match(/^\/\/ sahip: (.+)$/m)[1].split(", "),
  }];
}));

const ithalEden = (g, a) => Object.entries(g).filter(([, m]) => m.ithal.includes(a)).map(([x]) => x);
const yazan = (g, t) => Object.entries(g).filter(([, m]) => m.yazar.includes(t)).map(([x]) => x);

// Belge uc tur iddia tasir. KURAL: mimari niyet, kod uymazsa sapma vardir. ANLIK: kodun o
// gunku halinin sayimi, kod degisince bayatlar. BELIRSIZ: koddan denetlenemez.
export const KURAL = [
  ["K1", "uyelik", "Uyelik hicbir alan modulunu ithal etmez.", (g) => g.uyelik.ithal.length === 0],
  ["K2", "katalog", "Katalogu yalniz odunc ithal edebilir.",
    (g) => ithalEden(g, "katalog").every((a) => a === "odunc")],
  ["K3", "bildirim", "Bildirime hicbir modul baglanamaz.", (g) => ithalEden(g, "bildirim").length === 0],
  ["K4", "ucret", "Ucret yalniz odunc modulunu ithal edebilir.",
    (g) => g.ucret.ithal.every((a) => a === "odunc")],
  ["K5", "sube", "Sube hicbir alan modulunu ithal etmez.", (g) => g.sube.ithal.length === 0],
  ["K6", "odunc", "Odunc tablosuna yalniz odunc yazabilir.",
    (g) => yazan(g, "odunc").every((a) => a === "odunc")],
];

export const ANLIK = [
  ["A1", "sistem", "Sistem alti alan modulunden olusur.", (g) => Object.keys(g).length],
  ["A2", "odunc", "Odunc, katalog ve uyelik modullerini ithal eder.", (g) => g.odunc.ithal],
  ["A3", "sistem", "Grafikte bes ithal kenari vardir.",
    (g) => Object.values(g).reduce((s, m) => s + m.ithal.length, 0)],
  ["A4", "bildirim", "Bildirim, uyelik ve odunc modullerini ithal eder.", (g) => g.bildirim.ithal],
  ["A5", "ucret", "Ucret yalniz ucret tablosuna yazar.", (g) => g.ucret.yazar],
  ["A6", "katalog", "Katalog kitap ve nusha tablolarinin sahibidir.", (g) => g.katalog.sahip],
];

export const BELIRSIZ = [
  ["D1", "katalog", "Katalog sistemi disaridan alinmistir."],
  ["D2", "sistem", "Gece bakim penceresi 02.00-04.00 arasidir."],
];

// GR20: sonraki iki ceyrekte uygulanan sekiz degisiklik. Niyet etiketi, degisikligin mimari
// kurala uyup uymadigini soyler; etiket iddia edilmez, denetim sonucu ile karsilastirilir.
export const DEGISIM = [
  ["rezervasyon modulu eklenir", "kurala uygun", "rezervasyon",
    (m) => { m.rezervasyon = { ithal: ["uyelik"], yazar: ["rezervasyon"], sahip: ["rezervasyon"] }; }],
  ["odunc bildirim ithal eder", "kural disi", "odunc", (m) => m.odunc.ithal.push("bildirim")],
  ["ucret uyelik ithal eder", "kural disi", "ucret", (m) => m.ucret.ithal.push("uyelik")],
  ["bildirim uyelik ithalini birakir", "kurala uygun", "bildirim",
    (m) => { m.bildirim.ithal = m.bildirim.ithal.filter((a) => a !== "uyelik"); }],
  ["rezervasyon odunc tablosuna yazar", "kural disi", "rezervasyon", (m) => m.rezervasyon.yazar.push("odunc")],
  ["katalog etiket tablosunu devralir", "kurala uygun", "katalog",
    (m) => { m.katalog.sahip.push("etiket"); m.katalog.yazar.push("etiket"); }],
  ["ucret ceza tablosuna yazar", "kurala uygun", "ucret", (m) => m.ucret.yazar.push("ceza")],
  ["sube uyelik ithal eder", "kural disi", "sube", (m) => m.sube.ithal.push("uyelik")],
];

if (process.argv[1]?.endsWith("ortam.mjs")) {
  yaz(structuredClone(BASLANGIC));
  const g = grafik();
  const satir = [
    ...KURAL.map(([ad, ozne, metin, sina]) => [ad, "kural", ozne, sina(g) ? "dogru" : "yanlis", metin]),
    ...ANLIK.map(([ad, ozne, metin, oku]) => [ad, "anlik", ozne, String(oku(g)), metin]),
    ...BELIRSIZ.map(([ad, ozne, metin]) => [ad, "belirsiz", ozne, "-", metin]),
  ];
  console.log(`${"iddia".padEnd(6)}${"tur".padEnd(10)}${"ozne".padEnd(10)}${"t=0".padEnd(16)}metin`);
  for (const [a, b, c, d, e] of satir) {
    console.log(`${a.padEnd(6)}${b.padEnd(10)}${c.padEnd(10)}${d.padEnd(16)}${e}`);
  }
  console.log(`\n${Object.keys(g).length} modul, ` +
    `${Object.values(g).reduce((s, x) => s + x.ithal.length, 0)} ithal kenari; belge ${satir.length} ` +
    `iddia tasiyor (${KURAL.length} kural, ${ANLIK.length} anlik, ${BELIRSIZ.length} belirsiz)`);
  console.log(`t=0 sapmasi: 0 yanlis kural iddiasi, 0 bayat anlik iddia ` +
    `(belge kodla ayni gun yazildi)`);
}
```

```
iddia tur       ozne      t=0             metin
K1    kural     uyelik    dogru           Uyelik hicbir alan modulunu ithal etmez.
K2    kural     katalog   dogru           Katalogu yalniz odunc ithal edebilir.
K3    kural     bildirim  dogru           Bildirime hicbir modul baglanamaz.
K4    kural     ucret     dogru           Ucret yalniz odunc modulunu ithal edebilir.
K5    kural     sube      dogru           Sube hicbir alan modulunu ithal etmez.
K6    kural     odunc     dogru           Odunc tablosuna yalniz odunc yazabilir.
A1    anlik     sistem    6               Sistem alti alan modulunden olusur.
A2    anlik     odunc     katalog,uyelik  Odunc, katalog ve uyelik modullerini ithal eder.
A3    anlik     sistem    5               Grafikte bes ithal kenari vardir.
A4    anlik     bildirim  uyelik,odunc    Bildirim, uyelik ve odunc modullerini ithal eder.
A5    anlik     ucret     ucret           Ucret yalniz ucret tablosuna yazar.
A6    anlik     katalog   kitap,nusha     Katalog kitap ve nusha tablolarinin sahibidir.
D1    belirsiz  katalog   -               Katalog sistemi disaridan alinmistir.
D2    belirsiz  sistem    -               Gece bakim penceresi 02.00-04.00 arasidir.

6 modul, 5 ithal kenari; belge 14 iddia tasiyor (6 kural, 6 anlik, 2 belirsiz)
t=0 sapmasi: 0 yanlis kural iddiasi, 0 bayat anlik iddia (belge kodla ayni gun yazildi)
```

#### Sapma Nasıl Büyür

Belge sıfır sapmayla başlıyor. Sonraki blok sekiz değişikliği sırayla uygular, her adımdan sonra
on dört iddiayı yeniden sınar ve belgeye hiç dokunmaz.

```js
// sapma.mjs — ortam.mjs'i ithal eder; guncellenmeyen belgenin sapmasini adim adim olcer
import { BASLANGIC, DEGISIM, KURAL, ANLIK, BELIRSIZ, yaz, grafik } from "./ortam.mjs";

const m = structuredClone(BASLANGIC);
yaz(m);
const ilk = grafik();
const beklenen = Object.fromEntries(ANLIK.map(([ad, , , oku]) => [ad, JSON.stringify(oku(ilk))]));

const sapmaOlc = (g) => ({
  kural: KURAL.filter(([, , , sina]) => !sina(g)).map(([ad]) => ad),
  anlik: ANLIK.filter(([ad, , , oku]) => JSON.stringify(oku(g)) !== beklenen[ad]).map(([ad]) => ad),
});

console.log(`${"t".padStart(2)}  ${"degisiklik".padEnd(35)}${"niyet".padEnd(14)}` +
  `${"dusen iddia".padEnd(14)}${"yanlis kural".padStart(13)}${"bayat anlik".padStart(13)}` +
  `${"sapma".padStart(7)}`);
let onceki = [];
DEGISIM.forEach(([ad, niyet, , uygula], t) => {
  uygula(m);
  yaz(m);
  const s = sapmaOlc(grafik());
  const hepsi = [...s.kural, ...s.anlik];
  const yeni = hepsi.filter((a) => !onceki.includes(a));
  onceki = hepsi;
  console.log(`${String(t + 1).padStart(2)}  ${ad.padEnd(35)}${niyet.padEnd(14)}` +
    `${(yeni.join(",") || "-").padEnd(14)}${String(s.kural.length).padStart(13)}` +
    `${String(s.anlik.length).padStart(13)}${String(hepsi.length).padStart(7)}`);
});

const son = sapmaOlc(grafik());
const toplam = KURAL.length + ANLIK.length + BELIRSIZ.length;
console.log(`\nsekiz degisiklik sonunda ${toplam} iddianin ` +
  `${son.kural.length + son.anlik.length} tanesi yanlis, ${BELIRSIZ.length} tanesi denetlenemez, ` +
  `${toplam - son.kural.length - son.anlik.length - BELIRSIZ.length} tanesi dogru ` +
  `(dogru kalanlar: ${KURAL.filter(([a]) => !son.kural.includes(a)).map(([a]) => a).join(", ")})`);
console.log(`kurala uygun ${DEGISIM.filter(([, n]) => n === "kurala uygun").length} degisiklik ` +
  `hicbir kural iddiasini dusurmedi; kural disi ` +
  `${DEGISIM.filter(([, n]) => n === "kural disi").length} degisiklik ` +
  `${son.kural.length} kural iddiasini dusurdu`);
```

```
 t  degisiklik                         niyet         dusen iddia    yanlis kural  bayat anlik  sapma
 1  rezervasyon modulu eklenir         kurala uygun  A1,A3                     0            2      2
 2  odunc bildirim ithal eder          kural disi    K3,A2                     1            3      4
 3  ucret uyelik ithal eder            kural disi    K4                        2            3      5
 4  bildirim uyelik ithalini birakir   kurala uygun  A4                        2            4      6
 5  rezervasyon odunc tablosuna yazar  kural disi    K6                        3            4      7
 6  katalog etiket tablosunu devralir  kurala uygun  A6                        3            5      8
 7  ucret ceza tablosuna yazar         kurala uygun  A5                        3            6      9
 8  sube uyelik ithal eder             kural disi    K5                        4            6     10

sekiz degisiklik sonunda 14 iddianin 10 tanesi yanlis, 2 tanesi denetlenemez, 2 tanesi dogru (dogru kalanlar: K1, K2)
kurala uygun 4 degisiklik hicbir kural iddiasini dusurmedi; kural disi 4 degisiklik 4 kural iddiasini dusurdu
```

Sapma ilk değişiklikte başlıyor ve tek yönde büyüyor: 2, 4, 5, 6, 7, 8, 9, 10. Sonunda on dört
iddianın onu yanlış, ikisi denetlenemez; doğru kalan iki kural iddiasıdır. Belge konuşmaya devam
ediyor ama çoğunun kodda karşılığı yok.

İki tür iddianın davranışı ayrı. Kurala uygun dört değişikliğin hiçbiri bir kural iddiasını
düşürmedi; hepsi yalnız anlık iddiaları bayattı. Kural dışı dört değişikliğin dördü de bir kural
iddiasını düşürdü. **Kural iddiasının yanlışlığı bir kod kusurudur; anlık iddianın bayatlığı bir
belge kusurudur.** İkisi aynı tabloda görünse de biri kodda, öteki belgede düzeltilir.

#### Üç Güncel Tutma Düzeni

Aynı sekiz değişiklik şimdi üç düzende koşturulur. **Elle güncelleme**: değişikliği yapan, yalnız
dokunduğu modülün iddialarını gözden geçirir. **Üretilen denetim**: her değişiklikten sonra bütün
iddialar koda karşı sınanır. **Yalnız kural belgesi**: belge anlık iddia hiç taşımaz.

```js
// duzen.mjs — ortam.mjs'i ithal eder; ayni sekiz degisiklikte uc guncel tutma duzenini karsilastirir
import { BASLANGIC, DEGISIM, KURAL, ANLIK, BELIRSIZ, yaz, grafik } from "./ortam.mjs";

// elle:    degisikligi yapan, dokundugu modulun iddialarini gozden gecirir.
// denetim: her degisiklikten sonra butun iddialar koda karsi otomatik sinanir.
// kural:   belge yalniz kural iddialarini tasir; anlik iddia hic yazilmaz.
const kosu = (duzen) => {
  const m = structuredClone(BASLANGIC);
  yaz(m);
  const belge = duzen === "kural" ? [] :
    ANLIK.map(([ad, ozne, , oku]) => ({ ad, ozne, oku, deger: JSON.stringify(oku(grafik())) }));
  const farkedilen = new Set();
  let guncelleme = 0, uyari = 0, yanlisAlarm = 0;

  for (const [, niyet, dokunulan, uygula] of DEGISIM) {
    uygula(m);
    yaz(m);
    const g = grafik();
    const ihlal = KURAL.filter(([, , , sina]) => !sina(g));
    const bayat = belge.filter((i) => JSON.stringify(i.oku(g)) !== i.deger);
    if (duzen === "elle") {
      for (const [ad, ozne] of ihlal) if (ozne === dokunulan) farkedilen.add(ad);
      for (const i of bayat) if (i.ozne === dokunulan) { i.deger = JSON.stringify(i.oku(g)); guncelleme += 1; }
    } else {
      for (const [ad] of ihlal) farkedilen.add(ad);
      uyari += bayat.length;
      if (niyet === "kurala uygun") yanlisAlarm += bayat.length;
      for (const i of bayat) { i.deger = JSON.stringify(i.oku(g)); guncelleme += 1; }
    }
  }
  const g = grafik();
  const gercek = KURAL.filter(([, , , sina]) => !sina(g)).map(([ad]) => ad);
  return { duzen, belge, gercek, farkedilen, guncelleme, uyari, yanlisAlarm,
    bayat: belge.filter((i) => JSON.stringify(i.oku(g)) !== i.deger).map((i) => i.ad) };
};

const SONUC = ["elle", "denetim", "kural"].map(kosu);
console.log(`${"duzen".padEnd(9)}${"yakalanan".padStart(10)}${"kacan".padStart(18)}` +
  `${"guncelleme".padStart(12)}${"anlik uyari".padStart(13)}${"yanlis alarm".padStart(14)}` +
  `${"bayat kalan".padStart(13)}`);
for (const s of SONUC) {
  const kacan = s.gercek.filter((a) => !s.farkedilen.has(a));
  console.log(`${s.duzen.padEnd(9)}${`${s.farkedilen.size}/${s.gercek.length}`.padStart(10)}` +
    `${(kacan.join(",") || "-").padStart(18)}${String(s.guncelleme).padStart(12)}` +
    `${String(s.uyari).padStart(13)}${String(s.yanlisAlarm).padStart(14)}` +
    `${(s.bayat.join(",") || "-").padStart(13)}`);
}

// Sonradan gelen birinin belgeye sordugu yedi soru; her soru bir iddiaya duser.
const SORU = [
  ["Sistem kac alan modulunden olusur?", "A1"],
  ["Odunc modulu hangi modulleri kullanir?", "A2"],
  ["Grafikte kac ithal kenari var?", "A3"],
  ["Bildirim moduluna kim baglanabilir?", "K3"],
  ["Odunc tablosuna kim yazabilir?", "K6"],
  ["Katalog hangi tablolarin sahibi?", "A6"],
  ["Gece bakim penceresi ne zaman?", "D2"],
];
const g = grafik();
const yanit = (s, iddia) => {
  if (KURAL.some(([a]) => a === iddia)) return "dogru";          // kural, niyeti soyler
  if (BELIRSIZ.some(([a]) => a === iddia)) return s.duzen === "kural" ? "yanitsiz" : "denetlenemez";
  const i = s.belge.find((x) => x.ad === iddia);
  if (!i) return "yanitsiz";
  return JSON.stringify(i.oku(g)) === i.deger ? "dogru" : "yanlis";
};

console.log(`\n${"soru".padEnd(40)}${"iddia".padEnd(7)}${"elle".padEnd(14)}${"denetim".padEnd(14)}kural`);
for (const [soru, iddia] of SORU) {
  console.log(`${soru.padEnd(40)}${iddia.padEnd(7)}` +
    SONUC.map((s) => yanit(s, iddia).padEnd(14)).join("").trimEnd());
}
for (const s of SONUC) {
  const v = SORU.map(([, i]) => yanit(s, i));
  console.log(`${s.duzen.padEnd(9)}${v.filter((x) => x === "dogru").length} dogru, ` +
    `${v.filter((x) => x === "yanlis").length} yanlis, ` +
    `${v.filter((x) => x === "yanitsiz").length} yanitsiz, ` +
    `${v.filter((x) => x === "denetlenemez").length} denetlenemez`);
}
```

```
duzen     yakalanan             kacan  guncelleme  anlik uyari  yanlis alarm  bayat kalan
elle            3/4                K6           4            0             0        A1,A3
denetim         4/4                 -          10           10             6            -
kural           4/4                 -           0            0             0            -

soru                                    iddia  elle          denetim       kural
Sistem kac alan modulunden olusur?      A1     yanlis        dogru         yanitsiz
Odunc modulu hangi modulleri kullanir?  A2     dogru         dogru         yanitsiz
Grafikte kac ithal kenari var?          A3     yanlis        dogru         yanitsiz
Bildirim moduluna kim baglanabilir?     K3     dogru         dogru         dogru
Odunc tablosuna kim yazabilir?          K6     dogru         dogru         dogru
Katalog hangi tablolarin sahibi?        A6     dogru         dogru         yanitsiz
Gece bakim penceresi ne zaman?          D2     denetlenemez  denetlenemez  yanitsiz
elle     4 dogru, 2 yanlis, 0 yanitsiz, 1 denetlenemez
denetim  6 dogru, 0 yanlis, 0 yanitsiz, 1 denetlenemez
kural    2 dogru, 0 yanlis, 5 yanitsiz, 0 denetlenemez
```

Elle güncelleme dört gerçek sapmanın üçünü yakalıyor, dördüncüsünü kaçırıyor. Kaçan sapma K6: ödünç
tablosuna yazan yeni modül rezervasyondur, ama iddianın öznesi ödünçtür ve rezervasyona dokunan
ödüncün iddiasına bakmaz. **Elle güncellemenin kör noktası budur — dokunulan modül ile iddianın
öznesi farklı olduğunda sapma görünmez.** Bakım maliyeti ise en düşük: dört iddia güncellemesi.

Üretilen denetim dört sapmanın dördünü de yakalıyor ve hiç bayat iddia bırakmıyor. Bedeli on iddia
güncellemesi ve on uyarıdır. Uyarıların altısı kurala uygun değişikliklerden, yani kod kusursuzken
gelmiştir: **yanlış alarm**. Kaynağı bellidir — anlık iddialar her meşru değişiklikte düşer ve
denetim onları gerçek sapmadan ayırmaz.

Yalnız kural belgesi iki kalemi de sıfırlıyor: dört sapma yakalanıyor, sıfır güncelleme, sıfır
yanlış alarm. Bedeli soru tablosunda — yedi sorunun beşi yanıtsız. Bu düzen ucuzdur çünkü az şey
söyler.

Soru tablosu üç düzeni bu kursun kendi ölçüsüyle ayırır. Elle güncellenen belge yedi sorunun
dördünü doğru, **ikisini yanlış** yanıtlıyor: kaç modül ve kaç ithal kenarı olduğunu güvenle
söylüyor, ikisi de yanlış. Yanıtsız soru okuyucuyu koda gönderir, yanlış yanıtlanan soru yanlış
yere gönderir. Kural iddiaları üç düzende de doğru kalır: koda uymayan bir kural iddiası belgeyi
değil kodu yanlışlar.

#### Özet

- Belge sapması ölçülebilir bir niceliktir: koddan çıkarılan ithal grafiği belgedeki iddiayla
  karşılaştırılır; yöntem Servis Mimarileri kursunun sınır ihlali ölçümünden ödünç alınmıştır.
- İddialar üç türdür ve ayrı davranır: kurala uygun dört değişiklik hiçbir kural iddiasını
  düşürmedi, yalnız anlık iddiaları bayattı; kural dışı dördü dört kural iddiasını düşürdü.
  Güncellenmeyen belgede sapma 2'den 10'a büyür, 14 iddianın yalnız 2'si doğru kalır.
- Elle güncelleme 4 sapmanın 3'ünü yakalar, 4 güncelleme ödetir; kör noktası dokunulan modül ile
  iddianın öznesinin farklı olduğu durumdur. Üretilen denetim 4'ünü de yakalar, 10 güncelleme ve
  10 uyarı ödetir; uyarıların 6'sı yanlış alarmdır ve kaynağı anlık iddialardır.
- Yedi sorunun yanıtları düzenlere göre ayrışır: elle güncellemede 4 doğru ve 2 yanlış, üretilen
  denetimde 6 doğru, yalnız kural belgesinde 2 doğru ve 5 yanıtsız.

#### Sonraki Adım

Buraya kadar ölçülen her şey geçmişe ya da bugüne bakıyordu: verilen karar, çizilen sınır, koşan
kod. Mimarın yazılı verdiği bir şey daha var ve o geleceğe bakıyor — bu iş ne kadar
sürer. Sonraki ders tahmini ölçer: aynı iş kümesi tek sayı ve belirsizlik bandı olarak tahmin
edilip gerçekleşmeyle karşılaştırılır. Sapma nasıl dağılıyor, bant gerçekleşmelerin kaçını
kapsıyor ve tahmini parçalara bölmek sapmayı küçültüyor mu.

### Tahmin ve Değerlendirme

Source: https://academia.sh/tr/kurslar/mimari-dokumantasyon/tahmin-ve-degerlendirme

İş büyüklüğü tahmininin ölçülmesi: aynı iş kümesinin tek sayı ve belirsizlik bandı olarak tahmin edilip gerçekleşmeyle karşılaştırılması, sapma dağılımının yüzdelikleri, bandın gerçek kapsama oranı ve tahmini parçalara bölmenin hem sapmayı hem yayılımı nasıl değiştirdiği.

Önceki ders belgedeki iddianın kodla eşitlenmesini ölçtü. Buraya kadar ölçülen her şeyin ortak
yanı, olmuş bir şeyi anlatmasıydı: verilen karar, çizilen sınır, koşan kod. Mimarın yazılı olarak
verdiği bir şey daha var ve tek başına geleceğe bakıyor — bu iş ne kadar sürer.

Tahmin de bir belgedir ve bu kursun ölçüsü ona da uygulanır: hangi soruyu yanıtlıyor, hangi soruyu
yanıtlayamıyor, sapması ne. Kabaca büyüklük hesabı Sistem Tasarımına Giriş kursunda kurulmuştu;
orada hesaplanan büyüklük veri ve istek hacmiydi. Buradaki büyüklük iş büyüklüğüdür, birimi
adam-gün ve doğrulaması hacim hesabından farklıdır — hacim hesabı yaklaşık doğrulanır, iş
tahmini gerçekleşmeyle tek tek karşılaştırılır.

#### İki Biçimde Tahmin

Aşağıdaki blok bir çeyreğin iş listesini, tahminlerin band biçimini ve gerçekleşme modelini
**modelleyerek** tanımlar, sonra tek bir gerçekleşme koşturur. Gerçekleşme modeli iki sayıdan
oluşur ve ikisi de görünürdür: sistematik iyimserlik ile kaleme özgü değişkenlik.

```js
// tahmin.mjs — is kalemlerini, band kuralini ve gerceklesme modelini tanimlar; dogrudan
// calistirildiginda tek bir gerceklesmeyi olcer. Sonraki iki blok bu dosyayi ithal eder.

// GR21: bolgesel kutuphane agi sisteminin sonraki ceyrek is listesi; tahminler adam-gun.
export const IS = [
  ["sube tanim akisi", 5], ["katalog sarmalayici ucu", 8], ["odunc kayit gecisi", 13],
  ["uyelik dogrulama", 5], ["bildirim kanali", 3], ["ucret hesap kurallari", 8],
  ["rezervasyon sirasi", 13], ["rapor ekrani", 5], ["yedege gecis provasi", 8],
  ["toplu yukleme penceresi", 3], ["denetim betigi", 2], ["gorunum belgesi", 5],
];
// Band tek sayidan turetilir: alt sinir dortte uc, ust sinir bir buçuk kat. Tahmini veren,
// bandin gerceklesmelerin onda dokuzunu kapsayacagini soyluyor.
export const band = (t) => [Math.round(t * 0.75), Math.round(t * 1.5)];

// GR22: gerceklesme modeli. Gerceklesme = tahmin x 1.25 x exp(N(0, 0.45)). 1.25 sistematik
// iyimserligi (tahminde gorunmeyen is), exp(N(0, 0.45)) kaleme ozgu bagimsiz degiskenligi
// temsil eder. Iki sayi da modelin girdisidir; asagidaki butun sonuclar bunlara baglidir.
export const IYIMSERLIK = 1.25;
export const YAYILIM = 0.45;

export const uretec = (tohum) => () => {          // uretec kendi yazilmistir, tohum gorunur
  tohum = (tohum + 0x6d2b79f5) | 0;
  let t = Math.imul(tohum ^ (tohum >>> 15), 1 | tohum);
  t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
  return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
export const normal = (r) => {                    // Box-Muller: iki duzgun sayidan bir normal
  const u = Math.max(r(), 1e-12);
  return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * r());
};
export const carpan = (r) => IYIMSERLIK * Math.exp(YAYILIM * normal(r));

if (process.argv[1]?.endsWith("tahmin.mjs")) {
  const TOHUM = 60406;
  const r = uretec(TOHUM);
  console.log(`tohum ${TOHUM}; iyimserlik ${IYIMSERLIK}, yayilim ${YAYILIM}`);
  console.log(`${"is kalemi".padEnd(26)}${"tahmin".padStart(7)}${"band".padStart(10)}` +
    `${"gerceklesme".padStart(12)}${"oran".padStart(7)}${"band ici".padStart(10)}`);
  let toplamT = 0, toplamG = 0, ici = 0;
  for (const [ad, t] of IS) {
    const [alt, ust] = band(t);
    const g = Math.round(t * carpan(r) * 10) / 10;
    const icinde = g >= alt && g <= ust;
    toplamT += t; toplamG += g; ici += icinde ? 1 : 0;
    console.log(`${ad.padEnd(26)}${String(t).padStart(7)}${`${alt}-${ust}`.padStart(10)}` +
      `${g.toFixed(1).padStart(12)}${(g / t).toFixed(2).padStart(7)}` +
      `${(icinde ? "evet" : "hayir").padStart(10)}`);
  }
  const [pAlt, pUst] = band(toplamT);
  console.log(`toplam tahmin ${toplamT} gun, gerceklesme ${toplamG.toFixed(1)} gun, ` +
    `oran ${(toplamG / toplamT).toFixed(2)}`);
  console.log(`band ${IS.length} kalemin ${ici} tanesini kapsadi; proje toplami icin band ` +
    `${pAlt}-${pUst} gun ve gerceklesme ${toplamG >= pAlt && toplamG <= pUst ? "band ici" : "band disi"}`);
}
```

```
tohum 60406; iyimserlik 1.25, yayilim 0.45
is kalemi                  tahmin      band gerceklesme   oran  band ici
sube tanim akisi                5       4-8         8.6   1.72     hayir
katalog sarmalayici ucu         8      6-12         8.0   1.00      evet
odunc kayit gecisi             13     10-20        12.2   0.94      evet
uyelik dogrulama                5       4-8         5.6   1.12      evet
bildirim kanali                 3       2-5         1.5   0.50     hayir
ucret hesap kurallari           8      6-12         8.3   1.04      evet
rezervasyon sirasi             13     10-20        17.6   1.35      evet
rapor ekrani                    5       4-8        10.1   2.02     hayir
yedege gecis provasi            8      6-12         5.0   0.63     hayir
toplu yukleme penceresi         3       2-5         2.0   0.67      evet
denetim betigi                  2       2-3         3.9   1.95     hayir
gorunum belgesi                 5       4-8         1.2   0.24     hayir
toplam tahmin 78 gun, gerceklesme 84.0 gun, oran 1.08
band 12 kalemin 6 tanesini kapsadi; proje toplami icin band 59-117 gun ve gerceklesme band ici
```

Tek koşumda proje toplamı 78 güne karşı 84 gün: yüzde sekiz sapma, band içinde. Kalem düzeyinde
aynı tahmin çok daha kötü — on iki kalemin altısı bandın dışında ve iki kalemde oran 1,95 ile
2,02. Toplamdaki iyi görüntü kalem sapmalarının birbirini götürmesinden geliyor. Tek koşum kanıt
değildir; aynı liste çok kez koşturulmalıdır.

#### Sapma Nasıl Dağılır

```js
// dagilim.mjs — tahmin.mjs'i ithal eder; sapma dagilimini ve bandin kapsama oranini olcer
import { IS, band, uretec, carpan } from "./tahmin.mjs";

const TOHUM = 31517;
const KOSUM = 500;
const r = uretec(TOHUM);
const toplamT = IS.reduce((s, [, t]) => s + t, 0);

const kalemOran = [], projeOran = [];
let kalemIci = 0, projeIci = 0, altinda = 0;
for (let k = 0; k < KOSUM; k += 1) {
  let toplamG = 0;
  for (const [, t] of IS) {
    const g = t * carpan(r);
    const [alt, ust] = band(t);
    kalemOran.push(g / t);
    if (g >= alt && g <= ust) kalemIci += 1;
    if (g < t) altinda += 1;
    toplamG += g;
  }
  projeOran.push(toplamG / toplamT);
  const [pAlt, pUst] = band(toplamT);
  if (toplamG >= pAlt && toplamG <= pUst) projeIci += 1;
}

const yuzdelik = (d, p) => [...d].sort((a, b) => a - b)[Math.floor((d.length - 1) * p)];
const satir = (ad, d) => `${ad.padEnd(16)}` +
  [0.1, 0.25, 0.5, 0.75, 0.9].map((p) => yuzdelik(d, p).toFixed(2).padStart(8)).join("") +
  `${(d.reduce((s, x) => s + x, 0) / d.length).toFixed(2).padStart(10)}`;

console.log(`tohum ${TOHUM}, ${KOSUM} kosum; ${kalemOran.length} kalem gerceklesmesi`);
console.log(`${"gerceklesme/tahmin".padEnd(16)}${"p10".padStart(8)}${"p25".padStart(8)}` +
  `${"medyan".padStart(8)}${"p75".padStart(8)}${"p90".padStart(8)}${"ortalama".padStart(10)}`);
console.log(satir("kalem basina", kalemOran));
console.log(satir("proje toplami", projeOran));
console.log(`\nband kapsamasi: kalem basina ${((kalemIci / kalemOran.length) * 100).toFixed(0)}%, ` +
  `proje toplaminda ${((projeIci / KOSUM) * 100).toFixed(0)}% ` +
  `(tahmini veren onda dokuz, yani 90% demisti)`);
console.log(`tahminin altinda kalan kalem: ${((altinda / kalemOran.length) * 100).toFixed(0)}%; ` +
  `ustunde kalan: ${(100 - (altinda / kalemOran.length) * 100).toFixed(0)}%`);
```

```
tohum 31517, 500 kosum; 6000 kalem gerceklesmesi
gerceklesme/tahmin     p10     p25  medyan     p75     p90  ortalama
kalem basina        0.71    0.92    1.25    1.68    2.23      1.38
proje toplami       1.13    1.23    1.36    1.51    1.67      1.38

band kapsamasi: kalem basina 54%, proje toplaminda 72% (tahmini veren onda dokuz, yani 90% demisti)
tahminin altinda kalan kalem: 31%; ustunde kalan: 69%
```

Üç sonuç ayrı ayrı okunur. Birincisi, **sapma dağılımı geniştir ve simetrik değildir**: kalem
başına p10 ile p90 arasında 0,71'den 2,23'e uzanan bir aralık var, yani onda birlik uçlar arasında
üç kattan fazla fark. Kalemlerin yüzde 69'u tahmini aşıyor.

İkincisi, **kalem medyanı ile proje toplamının merkezi aynı sayı değildir**. Kalem başına medyan
1,25 iken proje toplamının medyanı 1,36, ortalaması 1,38. Neden farklı olduğu dağılımın biçiminden
gelir: çarpan sağa çarpık olduğu için ortalaması medyanının üstündedir ve on iki kalem
toplandığında toplam ortalamaya yakınsar. **Toplama, medyanı değil ortalamayı görünür kılar.** Tek
kaleme bakarak "genelde tahmini tutuyor" diyen biri, proje toplamında sistematik bir aşımla
karşılaşır.

Üçüncüsü, **bandın kapsama oranı, iddia edilenin çok altındadır**. Tahmini veren bandın onda dokuz
kapsayacağını söylemişti; ölçülen kapsama kalem başına yüzde 54, proje toplamında yüzde 72. Band
yanlış değil, iddiası yanlış: bir aralık vermek onu bir güven düzeyine bağlamaz. Bandın kapsaması
ancak gerçekleşmeyle karşılaştırılarak bilinir; bu yapılmadıkça band tek sayıdan yalnızca biçimce
farklıdır.

#### Tahmini Bölmek

```js
// bolme.mjs — tahmin.mjs'i ithal eder; tahmini parcalara bolmenin sapmaya etkisini olcer
import { IS, band, uretec, normal, IYIMSERLIK, YAYILIM } from "./tahmin.mjs";

// GR23: bir kalem k parcaya bolununce iki sey degisir. Bir, gorunmeyen isin (1 - 1/k) payi
// gorunur hale gelir ve tahmine eklenir. Iki, her parcanin degiskenligi bagimsiz cekilir,
// yani k parcanin gerceklesmesi ortalamada birbirini dengeler. Gercek is her iki durumda ayni
// modelden gelir: tahmin x 1.25 x degiskenlik.
const bolunmusTahmin = (t, k) => t * (1 + (IYIMSERLIK - 1) * (1 - 1 / k));
const bolunmusGerceklesme = (t, k, r) => {
  let toplam = 0;
  for (let i = 0; i < k; i += 1) toplam += (t * IYIMSERLIK / k) * Math.exp(YAYILIM * normal(r));
  return toplam;
};

const TOHUM = 74113;
const KOSUM = 500;
const toplamT = IS.reduce((s, [, t]) => s + t, 0);
const yuzdelik = (d, p) => [...d].sort((a, b) => a - b)[Math.floor((d.length - 1) * p)];

console.log(`tohum ${TOHUM}, ${KOSUM} kosum, ${IS.length} kalem`);
console.log(`${"parca".padStart(6)}${"kalem tahmini".padStart(14)}${"p10".padStart(7)}` +
  `${"medyan".padStart(8)}${"p90".padStart(7)}${"p90/p10".padStart(9)}` +
  `${"band ici".padStart(10)}${"proje medyani".padStart(15)}`);
for (const k of [1, 2, 4, 8]) {
  const r = uretec(TOHUM);
  const oran = [], projeOran = [];
  let ici = 0;
  for (let n = 0; n < KOSUM; n += 1) {
    let toplamG = 0, toplamE = 0;
    for (const [, t] of IS) {
      const e = bolunmusTahmin(t, k);
      const g = bolunmusGerceklesme(t, k, r);
      const [alt, ust] = band(e);
      oran.push(g / e);
      if (g >= alt && g <= ust) ici += 1;
      toplamG += g; toplamE += e;
    }
    projeOran.push(toplamG / toplamE);
  }
  const p10 = yuzdelik(oran, 0.1), p90 = yuzdelik(oran, 0.9);
  console.log(`${String(k).padStart(6)}` +
    `${(bolunmusTahmin(toplamT, k) / toplamT).toFixed(3).padStart(14)}` +
    `${p10.toFixed(2).padStart(7)}${yuzdelik(oran, 0.5).toFixed(2).padStart(8)}` +
    `${p90.toFixed(2).padStart(7)}${(p90 / p10).toFixed(2).padStart(9)}` +
    `${`${((ici / oran.length) * 100).toFixed(0)}%`.padStart(10)}` +
    `${yuzdelik(projeOran, 0.5).toFixed(2).padStart(15)}`);
}
console.log(`\nbolme iki sayiyi ayri yonde oynatir: gorunur hale gelen is tahmini`);
console.log(`buyutur (kalem tahmini sutunu), bagimsiz parcalarin ortalamasi yayilimi`);
console.log(`daraltir (p90/p10 sutunu).`);
```

```
tohum 74113, 500 kosum, 12 kalem
 parca kalem tahmini    p10  medyan    p90  p90/p10  band ici  proje medyani
     1         1.000   0.70    1.24   2.19     3.14       54%           1.35
     2         1.125   0.77    1.16   1.76     2.29       65%           1.21
     4         1.188   0.83    1.13   1.52     1.82       84%           1.16
     8         1.219   0.90    1.12   1.38     1.52       93%           1.13

bolme iki sayiyi ayri yonde oynatir: gorunur hale gelen is tahmini
buyutur (kalem tahmini sutunu), bagimsiz parcalarin ortalamasi yayilimi
daraltir (p90/p10 sutunu).
```

Bölmenin etkisi iki ayrı kanaldan geliyor ve ikisi de ölçülebiliyor. Birinci kanal tahmini
büyütüyor: sekiz parçaya bölünen bir kalemin tahmini bölünmemiş hâlinin 1,219 katı, çünkü parça
parça bakınca daha önce görünmeyen işin bir bölümü listeye giriyor. İkinci kanal yayılımı
daraltıyor: bağımsız parçaların gerçekleşmeleri ortalamada birbirini dengeliyor, p90/p10 oranı
3,14'ten 1,52'ye iniyor.

İki kanal birlikte medyan sapmayı 1,24'ten 1,12'ye, proje medyanını 1,35'ten 1,13'e çekiyor ve
bandın kapsamasını yüzde 54'ten yüzde 93'e çıkarıyor. Kazancın büyük bölümü ilk bölmelerde:
bir parçadan dörde geçmek kapsamayı 30 puan artırıyor, dörtten sekize geçmek 9 puan. **Bölmenin
getirisi azalarak artar** ve karşılığında her parça ayrıca tahmin edilmek zorundadır.

Bu ders üç soruluk bir kümeye yanıt arıyordu. "Bu iş ne kadar sürer" sorusunu tek sayı yanıtlıyor.
"Ne kadar emin" sorusunu yalnız band yanıtlıyor, ama ancak kapsama oranı ölçülmüşse — ölçülmemiş
bir band bu soruyu yanıtlıyor gibi görünüp yanıtlamaz. Üçüncü soru olan "bu tahmin neye dayanıyor"
ikisinde de yanıtsız kalıyor. Bu, dördüncü dersteki eşiğin kaynağı boşluğunun aynısıdır: sayı
yazılıyor, sayının nereden geldiği yazılmıyor.

#### Özet

- İş büyüklüğü tahmininin sapması gerçekleşmeyle tek tek karşılaştırılarak ölçülür; modelde kalem
  başına p10–p90 aralığı 0,71–2,23 ve kalemlerin yüzde 69'u tahmini aşıyor.
- Kalem medyanı ile proje toplamının merkezi ayrı sayılardır: kalem medyanı 1,25 iken proje
  toplamının medyanı 1,36 ve ortalaması 1,38 — toplama, medyanı değil ortalamayı görünür kılar.
- Bandın kapsama oranı iddia edilenden bağımsızdır: onda dokuz kapsayacağı söylenen band kalem
  başına yüzde 54, proje toplamında yüzde 72 kapsıyor; ölçülmemiş band tek sayıdan biçimce farklıdır.
- Bölme iki kanaldan çalışır: görünmeyen işin bir bölümünü tahmine ekleyerek tahmini 1,219 katına
  çıkarır, bağımsız parçaları ortalayarak p90/p10 oranını 3,14'ten 1,52'ye indirir.
- Sekize bölme medyan sapmayı 1,24'ten 1,12'ye, proje medyanını 1,35'ten 1,13'e çeker ve kapsamayı
  yüzde 93'e taşır; getiri azalarak artar, bire dörtten sonraki kazanç 9 puandır.

#### Kurs Kapanışı

Bu kurs bir kuralla yürüdü: **bir belgenin değeri, sonradan gelen birinin sorduğu soruların kaçını
yanıtladığıyla ölçülür.** On bir dersin her biri kendi soru kümesini kurdu, o kümeyi bir kayıt,
görünüm, diyagram, senaryo ya da tahmin üzerinde koşturdu ve yanıtlananla birlikte
**yanıtlanamayanı** da saydı.

| Ders | Soru kümesi | Yanıtlanan / yanıtlanamayan | Bakım maliyeti ya da sapma |
|---|---|---|---|
| Mimari Karar Kayıtları | 4 karara sorulan 39 soru | kayıtsız 18/39, yapılandırılmış kayıtla 39/39 | 4 kayıt 101 satır, 170 dakika; sürenin %15'i hiçbir soruya yanıt vermeyen bölüme gitti |
| Öneri ve Değerlendirme Süreci | 8 karara sorulan küme | doğru yanıt 13, 32 ve 40; sözlü onayda 12 yanlış ve 15 eksik yanıt | çevrilen karar başına 440 ve 420 kişi-dakika; tek turlu düzen 900 kişi-dakikayı yön değiştirmeden harcadı |
| Ödünleşim Çözümlemesi | 5 çözümlemeye sorulan küme | kayıttaki hâliyle 2/5 yeniden üretilebildi, eksikler giderilince 5/5; doğrulanabilen 1 | tamamlama 110 dakika, kazanılan çözümleme başına 36,7 dakika |
| Risk Kaydı | gerçekleşen 2.348 risk | kayıtta hiç olmayan 808 risk (%34,4); doğru yanıt 16'dan 40,9'a, yanlış yanıt 6'dan 0,4'e | yıllık gözden geçirme dönem başına 1,26 risk; aylığa çıkınca yakalanan risk başına 602 kişi-dakika |
| Teknik Borç Yönetimi | 9 borç kalemi ve yıllık değişiklik kümesi | bilinçsiz 5 kalem 6.820, bilinçli 4 kalem 3.640 dakika; 48 tekrar eşiğinde bir yılda hiç fark edilmiyor | borçlu modülde aynı iş %63,7 fazla sürüyor; fark edilme gecikmesi 2,5 aydan 7,4 aya |
| Mimari Görünümler | 24 soru | 18 yanıtlanan / 6 yanıtlanamayan | 200 değişiklikte 279 yazım, 79'u yineleme; yanıt başına 15,50 yazım |
| Katmanlı Diyagram Yaklaşımı | 14 soru | kendi düzeyinde 14 yanıt / bağlam düzeyinde D03 ve ilk üç düzeyde D11 yanıtsız | 112 düğüm bakımda; 62 düğüm okuma yerine tek düzeyde 278, fark 4,24 turda geri ödeniyor |
| Diyagram Türünün Soruya Göre Seçilmesi | 18 soru | 16 yanıtlanan / 2 yanıtlanamayan | 150 değişiklikte 241 öge güncellemesi; yanıt başına bakım 2,8 ile 24,3 arasında |
| Kalite Nitelik Senaryoları | 12 gereksinime 5 soru, toplam 60 | 29 yanıtlanan / 31 yanıtlanamayan | yazım 1,85 kat; eşiği yazılmamış 9 gereksinim teslim başına 5,31 anlaşmazlık |
| Belgenin Güncel Kalması | 7 soru | üretilen denetimde 6 doğru / elle güncellemede 4 doğru ve 2 yanlış | sapma 2'den 10'a; denetim 10 güncelleme ve 6 yanlış alarm ödetiyor |
| Tahmin ve Değerlendirme | 3 soru | 2 yanıtlanan / 1 yanıtlanamayan ("bu tahmin neye dayanıyor") | kalem medyanı 1,25 sapma; band kapsaması %54, bölmeyle %93 |

Tablo bir örüntü gösteriyor. Hiçbir ders soru kümesini tamamen kapatamadı ve bu bir kusur değil:
yanıtlanamayan sorular her seferinde aynı iki sınıftan çıktı — ya iki ayrı görünümün bilgisini
birleştirmeyi istediler, ya da bir sayının nereden geldiğini sordular. İkinci sınıf
kursun başından sonuna kadar sürdü; karar kaydında "bu sayı nereden geldi", nitelik senaryosunda
"eşiğin kaynağı", tahminde "bu tahmin neye dayanıyor" aynı boşluğun üç adıdır.

İkinci örüntü bakım maliyetinde: her ders bir belge biçiminin bedelini saydı, hiçbirinde bedel
sıfır çıkmadı. Bir belgeyi güncel tutmanın maliyeti yazım anında değil, ondan sonraki her
değişiklikte ödenir; bu yüzden bir belgenin değeri yalnız yanıtladığı soru sayısıyla değil, o
sayının bakım maliyetine oranıyla anlamlıdır.

Bütün bu kayıtlar, görünümler, senaryolar ve tahminler tek bir sistemin içinde kaldı: bölgesel
kütüphane ağının yazılım sistemi, kendi modülleri, kendi sınırları ve kendi belgesiyle. Oysa hiçbir
sistem yalnız durmaz. Sistem bir kurumun içinde duruyor, yanında başka sistemler var ve onlarla
veri alışverişi yapıyor. Sonraki kurs — **Kurumsal Bağlam ve Tümleştirme** — bu soruyla açılır: bir
sistemin sınırı, o sistemin dışında kalanlarla kurduğu bağlar hesaba katıldığında nereden geçer.

