İçeriğe geç
academia.sh

Ders 11 / 11

Tahmin ve Değerlendirme

İş 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.

İçindekiler

Ö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.

// 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

// 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

// 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.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat