İçeriğe geç
academia.sh

Ders 24 / 27

Maliyet Tahmini

Altyapı değişikliğinin bütçeye etkisinin uygulanmadan önce sayılması: plan farkından değişiklik başına aylık tahmin üretilmesi, sabit birim fiyatlı kalemler ile kullanıma bağlı kalemlerin ayrılması, gerçekleşenle karşılaştırıldığında hatanın nereden geldiği ve nasıl dağıldığı, toplam doğruluğun tek tek değişikliklerdeki sapmayı nasıl gizlediği ve bir eşik kuralının kaç değişikliği gereksiz durdurup kaç pahalı değişikliği geçirdiği.

İçindekiler

Önceki ders bir doğrulama koşumunun kendi maliyetini ve arkasında bıraktığı artığın sızan maliyetini saydı. İkisi de olan bitince sayıldı — para harcandıktan sonra. Aynı soru uygulamadan önce de sorulabilir, çünkü plan zaten elde: hangi kaynağın ekleneceği, hangisinin büyütüleceği, hangisinin silineceği orada yazılıdır. Bir plan farkı aynı zamanda bir fatura farkıdır.

Sistem ölçeğinde kapasite ve maliyet farkındalığı daha önce ölçüldü; buradaki birim ise değişikliğin kendisidir: bu katkı birleşirse aylık gider ne kadar artar? Bu ders plandan değişiklik başına tahmin üretir, gerçekleşenle karşılaştırır ve üç şeyi sayar: hangi kalem tahmin edilebiliyor, hangi kalem edilemiyor, hata nasıl dağılıyor. Sonra bir eşik kuralı — “tahmin şu kadarı aşarsa dur” — bilinen değişiklik kümesinde koşturulur ve kuralın iki hatası birden sayılır. Through-line bölgesel ölçüm ağıdır; fiyat listesi de tutarlar da kurgudur.

Tahmin Edilebilen ve Edilemeyen Kalem

Bir maliyet kalemi iki türden biridir. Sabit birim fiyatlı kalemin miktarı plandan doğrudan okunur: üç makine, 120 gigabayt disk, 500 gigabayt depo. Bu kalemler için tahmin bir çarpma işlemidir ve ay boyunca değişmez. Kullanıma bağlı kalemin miktarı ise planda yoktur; plan kaynağın var olacağını söyler ama kaç istek alacağını, kaç gigabayt veri çıkaracağını, kaç ileti taşıyacağını söylemez. Bu kalemler için tahmin, kayıtta yazılı beklenen kullanıma dayanır — yani bir tahminin üstüne kurulmuş bir tahmindir.

// tahmin.mjs — plan uzerinden degisiklik basina aylik maliyet tahmini uretir (model)
import { writeFileSync } from "node:fs";

// AD19: fiyat listesi kurgudur, hicbir saglayicinin fiyatiyla ilgisi yoktur; birim "kb" (kurgu birim)
const SABIT = { "makine-orta": 30, "makine-buyuk": 60, "disk-gb": 0.5, "depo-gb": 0.8,
  "arsiv-gb": 0.1 };                       // kalem basina aylik sabit birim fiyat
// kullanima bagli kalem: [aylik birim fiyat, belirsizlik carpani r] — gercek miktar 1/r ile r arasi
const KULLANIM = { "istek-milyon": [4, 4], "cikan-veri-gb": [0.9, 4], "ileti-milyon": [2, 2],
  "okuma-milyon": [3, 2] };

// AD20: on iki degisiklik plandan okunmustur; her kalemin miktari plandaki kaynak farkidir,
// kullanima bagli kalemlerde ise kayitta yazili beklenen kullanimdir (kurgu)
const DEGISIKLIK = [
  ["G01", "uc toplayici makine eklenir", [["makine-orta", 3], ["disk-gb", 120],
    ["cikan-veri-gb", 60]]],
  ["G02", "veri deposu 500 GB buyutulur", [["depo-gb", 500], ["okuma-milyon", 20]]],
  ["G03", "arsiv akisi eklenir", [["arsiv-gb", 2000], ["okuma-milyon", 30]]],
  ["G04", "olcum kuyrugu eklenir", [["ileti-milyon", 120]]],
  ["G05", "toplayici boyutu buyuk yapilir", [["makine-buyuk", 3], ["makine-orta", -3]]],
  ["G06", "yeni bolge: ag ve iki makine", [["makine-orta", 2], ["disk-gb", 80], ["cikan-veri-gb", 300]]],
  ["G07", "toplayici diski 40'tan 200 GB'a", [["disk-gb", 480], ["okuma-milyon", 10]]],
  ["G08", "yedek penceresi 7 gunden 30 gune", [["depo-gb", 1400]]],
  ["G09", "dis uc nokta acilir", [["istek-milyon", 20], ["cikan-veri-gb", 800]]],
  ["G10", "arsiv sinifi soguga alinir", [["arsiv-gb", -1500]]],
  ["G11", "toplayici sayisi 3'ten 9'a", [["makine-orta", 6], ["disk-gb", 240],
    ["cikan-veri-gb", 120]]],
  ["G12", "toplama sikligi iki katina", [["ileti-milyon", 40], ["okuma-milyon", 60]]],
];

const T = DEGISIKLIK.map(([kod, ne, kalemler]) => {
  let sabit = 0;
  const kullanim = [];
  for (const [kalem, miktar] of kalemler) {
    if (SABIT[kalem] !== undefined) sabit += SABIT[kalem] * miktar;
    else kullanim.push([kalem, KULLANIM[kalem][0] * miktar, KULLANIM[kalem][1]]);
  }
  const k = kullanim.reduce((s, [, t]) => s + t, 0);
  return { kod, ne, sabit, kullanim, tahmin: sabit + k, sabitKalem: kalemler.length - kullanim.length };
});
writeFileSync("tahmin.json", JSON.stringify(T));

console.log(`${"kod".padEnd(5)}${"degisiklik".padEnd(34)}${"sabit kalem".padStart(12)}` +
  `${"kullanim kalemi".padStart(16)}${"sabit kb".padStart(10)}${"kullanim kb".padStart(13)}` +
  `${"tahmin kb".padStart(11)}`);
for (const d of T) {
  const k = d.tahmin - d.sabit;
  console.log(`${d.kod.padEnd(5)}${d.ne.padEnd(34)}${String(d.sabitKalem).padStart(12)}` +
    `${String(d.kullanim.length).padStart(16)}${d.sabit.toFixed(1).padStart(10)}` +
    `${k.toFixed(1).padStart(13)}${d.tahmin.toFixed(1).padStart(11)}`);
}
const sabitToplam = T.reduce((s, d) => s + d.sabit, 0);
const kullanimToplam = T.reduce((s, d) => s + (d.tahmin - d.sabit), 0);
const kalem = T.reduce((s, d) => s + d.sabitKalem + d.kullanim.length, 0);
console.log(`\n${T.length} degisiklik, ${kalem} kalem: ` +
  `${T.reduce((s, d) => s + d.sabitKalem, 0)} sabit birim fiyatli (tahmin edilebilir), ` +
  `${T.reduce((s, d) => s + d.kullanim.length, 0)} kullanima bagli (tahmin edilemez)`);
console.log(`toplam tahmin ${(sabitToplam + kullanimToplam).toFixed(1)} kb; ` +
  `sabit bolum ${sabitToplam.toFixed(1)} kb (%${(100 * sabitToplam / (sabitToplam + kullanimToplam)).toFixed(0)}), ` +
  `kullanim bolumu ${kullanimToplam.toFixed(1)} kb (%${(100 * kullanimToplam / (sabitToplam + kullanimToplam)).toFixed(0)})`);
console.log(`kullanima bagli kalem tasiyan degisiklik: ${T.filter((d) => d.kullanim.length).length}/${T.length}`);
kod  degisiklik                         sabit kalem kullanim kalemi  sabit kb  kullanim kb  tahmin kb
G01  uc toplayici makine eklenir                  2               1     150.0         54.0      204.0
G02  veri deposu 500 GB buyutulur                 1               1     400.0         60.0      460.0
G03  arsiv akisi eklenir                          1               1     200.0         90.0      290.0
G04  olcum kuyrugu eklenir                        0               1       0.0        240.0      240.0
G05  toplayici boyutu buyuk yapilir               2               0      90.0          0.0       90.0
G06  yeni bolge: ag ve iki makine                 2               1     100.0        270.0      370.0
G07  toplayici diski 40'tan 200 GB'a              1               1     240.0         30.0      270.0
G08  yedek penceresi 7 gunden 30 gune             1               0    1120.0          0.0     1120.0
G09  dis uc nokta acilir                          0               2       0.0        800.0      800.0
G10  arsiv sinifi soguga alinir                   1               0    -150.0          0.0     -150.0
G11  toplayici sayisi 3'ten 9'a                   2               1     300.0        108.0      408.0
G12  toplama sikligi iki katina                   0               2       0.0        260.0      260.0

12 degisiklik, 24 kalem: 13 sabit birim fiyatli (tahmin edilebilir), 11 kullanima bagli (tahmin edilemez)
toplam tahmin 4362.0 kb; sabit bolum 2450.0 kb (%56), kullanim bolumu 1912.0 kb (%44)
kullanima bagli kalem tasiyan degisiklik: 9/12

Yirmi dört kalemin on üçü sabit birim fiyatlıdır, on biri kullanıma bağlı. Kalem sayısı bakımından çoğunluk tahmin edilebilir görünür, ama tutar bakımından bölünme farklıdır: toplamın yüzde elli altısı sabit, yüzde kırk dördü kullanıma bağlıdır. Daha önemlisi dağılımdır — on iki değişikliğin dokuzu en az bir kullanıma bağlı kalem taşır. Üç değişiklik (G05, G08, G10) yalnız sabit kalemden oluşur ve onların tahmini bir hesaplama değil, bir kesinliktir.

Gerçekleşenle Karşılaştırma

AD21 — sabit birim fiyatlı kalem ay boyunca değişmez, hatası tam olarak sıfırdır; kullanıma bağlı kalemin gerçek miktarı beklenenin 1/r ile r katı arasındadır. AD22 — on iki değişiklik aynı ay içinde uygulanır ve gerçekleşen tutar ay sonunda okunur (kurgu). Belirsizlik çarpanı kaleme göre değişir: dış istek ve çıkan veri en oynak kalemlerdir (r = 4), kuyruk iletisi ve depo okuması daha durgundur (r = 2).

// gercek.mjs — gerceklesen maliyet, tahmin hatasinin dagilimi ve esik kuralinin sonucu (model)
import { readFileSync } from "node:fs";

// AD21: sabit birim fiyatli kalem ay boyunca degismez, hatasi sifirdir; kullanima bagli kalemin
// gercek miktari beklenenin 1/r ile r kati arasindadir (r kalemin belirsizlik carpani)
const TOHUM = 20260401;
let durum = TOHUM;
const rast = () => (durum = (durum * 48271) % 2147483647) / 2147483647;

const G = JSON.parse(readFileSync("tahmin.json", "utf8")).map((d) => {
  const gercek = d.sabit + d.kullanim.reduce((s, [, tutar, r]) =>
    s + tutar * Math.pow(r, 2 * rast() - 1), 0);
  return { ...d, gercek, hata: gercek - d.tahmin };
});

console.log(`${"kod".padEnd(5)}${"tahmin kb".padStart(11)}${"gerceklesen kb".padStart(16)}` +
  `${"hata kb".padStart(10)}${"hata %".padStart(9)}${"kaynak".padStart(20)}`);
for (const d of G) {
  const yuzde = d.tahmin === 0 ? 0 : (100 * d.hata) / Math.abs(d.tahmin);
  console.log(`${d.kod.padEnd(5)}${d.tahmin.toFixed(1).padStart(11)}${d.gercek.toFixed(1).padStart(16)}` +
    `${d.hata.toFixed(1).padStart(10)}${yuzde.toFixed(0).padStart(9)}` +
    `${(d.kullanim.length ? `${d.kullanim.length} kullanim kalemi` : "yalniz sabit").padStart(20)}`);
}

const hatasiz = G.filter((d) => d.hata === 0);
const sapan = G.filter((d) => d.hata !== 0);
const oran = (d) => d.gercek / d.tahmin;
console.log(`\n${hatasiz.length} degisiklikte hata tam olarak sifir (yalniz sabit birim fiyatli ` +
  `kalem tasiyorlar); ${sapan.length} degisiklikte sapma var`);
console.log(`sapma araligi ${Math.min(...sapan.map(oran)).toFixed(2)}x ile ` +
  `${Math.max(...sapan.map(oran)).toFixed(2)}x arasinda; ` +
  `toplam tahmin ${G.reduce((s, d) => s + d.tahmin, 0).toFixed(1)} kb, ` +
  `gerceklesen ${G.reduce((s, d) => s + d.gercek, 0).toFixed(1)} kb ` +
  `(toplamda %${(100 * (G.reduce((s, d) => s + d.hata, 0) / G.reduce((s, d) => s + d.tahmin, 0))).toFixed(0)})`);

// AD23: esik kurali plandaki tahmine bakar: tahmin esigi asarsa degisiklik durur ve elle onaya gider
console.log(`\n${"esik kb".padStart(8)}${"duran".padStart(7)}${"gereksiz duran".padStart(16)}` +
  `${"gecen pahali".padStart(14)}${"dogru duran".padStart(13)}${"onaya giden butce".padStart(19)}`);
for (const E of [200, 300, 500, 800]) {
  const duran = G.filter((d) => d.tahmin > E);
  const gereksiz = duran.filter((d) => d.gercek <= E);
  const gecen = G.filter((d) => d.tahmin <= E && d.gercek > E);
  console.log(`${String(E).padStart(8)}${String(duran.length).padStart(7)}` +
    `${String(gereksiz.length).padStart(16)}${String(gecen.length).padStart(14)}` +
    `${String(duran.length - gereksiz.length).padStart(13)}` +
    `${duran.reduce((s, d) => s + d.tahmin, 0).toFixed(0).padStart(19)}`);
}
for (const E of [200, 300]) {
  const ad = (f) => G.filter(f).map((d) => d.kod).join(", ") || "yok";
  console.log(`\nesik ${E} kb: duran ${ad((d) => d.tahmin > E)}; ` +
    `gereksiz duran ${ad((d) => d.tahmin > E && d.gercek <= E)}; ` +
    `gecen pahali ${ad((d) => d.tahmin <= E && d.gercek > E)}`);
}
kod    tahmin kb  gerceklesen kb   hata kb   hata %              kaynak
G01        204.0           192.3     -11.7       -6   1 kullanim kalemi
G02        460.0           461.5       1.5        0   1 kullanim kalemi
G03        290.0           362.5      72.5       25   1 kullanim kalemi
G04        240.0           121.6    -118.4      -49   1 kullanim kalemi
G05         90.0            90.0       0.0        0        yalniz sabit
G06        370.0           535.2     165.2       45   1 kullanim kalemi
G07        270.0           288.3      18.3        7   1 kullanim kalemi
G08       1120.0          1120.0       0.0        0        yalniz sabit
G09        800.0           958.4     158.4       20   2 kullanim kalemi
G10       -150.0          -150.0       0.0        0        yalniz sabit
G11        408.0           368.8     -39.2      -10   1 kullanim kalemi
G12        260.0           316.2      56.2       22   2 kullanim kalemi

3 degisiklikte hata tam olarak sifir (yalniz sabit birim fiyatli kalem tasiyorlar); 9 degisiklikte sapma var
sapma araligi 0.51x ile 1.45x arasinda; toplam tahmin 4362.0 kb, gerceklesen 4664.7 kb (toplamda %7)

 esik kb  duran  gereksiz duran  gecen pahali  dogru duran  onaya giden butce
     200     10               2             0            8               4422
     300      5               0             2            5               3158
     500      2               0             1            2               1920
     800      1               0             1            1               1120

esik 200 kb: duran G01, G02, G03, G04, G06, G07, G08, G09, G11, G12; gereksiz duran G01, G04; gecen pahali yok

esik 300 kb: duran G02, G06, G08, G09, G11; gereksiz duran yok; gecen pahali G03, G12

Hata tablosunun ilk okunuşu yanıltıcıdır. Toplamda tahmin 4.362, gerçekleşen 4.664,7 kurgu birim — yüzde yedi sapma, kabul edilebilir görünür. Ama bu yüzde yedi, tek tek değişikliklerdeki sapmaların birbirini götürmesiyle oluşur: G04 yarı yarıya düşük gerçekleşir (0,51x), G06 yüzde kırk beş yüksek (1,45x), G03 yüzde yirmi beş yüksek. Toplam doğruluk, karar veren kişiye hiçbir şey vermez, çünkü kararlar tek tek değişikliklerde verilir. Bir bütçe raporu için yüzde yedi iyi bir sayıdır; bir kapı için sapmanın 0,51x–1,45x aralığı asıl sayıdır.

İkinci okuma hatanın kaynağını gösterir. Üç değişiklikte hata tam olarak sıfırdır ve bu bir şans değil, yapısal bir sonuçtur: o üçü yalnız sabit birim fiyatlı kalem taşır. Kalan dokuzun hepsinde sapma vardır ve sapmanın büyüklüğü kalemin belirsizlik çarpanıyla birlikte artar. Tahmin hatasını düşürmenin yolu daha iyi bir tahmin aracı değildir; kullanıma bağlı kalemin payını bilmektir. Bir değişikliğin tahmini yanında “bu tutarın yüzde kaçı kullanıma bağlı” sayısı yazılmıyorsa, tahmin tek başına ne kadar güvenilir olduğunu söylemez.

Tabloda tek negatif kalem de aynı kurala uyar: arşiv sınıfının soğuğa alınması aylık 150 birim tasarruf olarak tahmin edilir ve tam olarak 150 birim tasarruf eder. Sabit birim fiyatlı bir kalemde tasarruf da gider kadar kesindir; kullanıma bağlı bir kalemde vaat edilen düşüş ise aynı belirsizliği taşır ve gerçekleşmeyebilir.

Eşik Kuralı

Tahmin bir kapıya bağlandığı anda karar üretmeye başlar: tahmin eşiği aşarsa değişiklik durur ve elle onaya gider. Kuralın iki hatası vardır ve ikisi de tabloda sayılıdır. Gereksiz duran, tahmini eşiği aşan ama gerçekte aşmayan değişikliktir; bedeli bekleyen bir katkı ve harcanan onay zamanıdır. Geçen pahalı, tahmini eşiğin altında kalan ama gerçekte üstüne çıkan değişikliktir; bedeli bütçedeki sürprizdir.

İki yüz birimlik eşik on iki değişikliğin onunu durdurur. Bu eşikte hiçbir pahalı değişiklik kaçmaz — ama duranların ikisi (G01 ve G04) gerçekte eşiğin altında kalır ve onaya giden bütçe 4.422 birime, yani neredeyse toplamın tamamına çıkar. Kapı bu noktada bir kapı olmaktan çıkar: her şey durur, onay bir tören hâline gelir. Üç yüz birimlik eşikte duran sayısı beşe iner ve o beşin beşi de gerçekte eşiği aşar — ama bu kez iki değişiklik (G03 ve G12) tahmini altında kaldığı için geçer ve gerçekte üstüne çıkar.

Sekiz yüz birimlik eşikte yalnız bir değişiklik durur ve bir tanesi kaçar. Hiçbir eşik ikisini birden sıfırlamaz, çünkü eşik bir sayıya bakar ve o sayının kendisi belirsizdir. Kuralı iyileştirmenin yolu eşiği daha iyi seçmek değil, eşiğe bakılan sayıyı değiştirmektir: tahminin kendisi yerine tahminin kullanıma bağlı payı da eşiğe girerse, yalnız sabit kalem taşıyan G08 gibi bir değişiklik yüksek tutarına rağmen otomatik geçebilir; çünkü onun tahmini kesindir.

Bir sınır daha var. Bu tahminlerin girdisi plandır ve plan da uygulamayı tam öngörmez: planda hiç görünmeyen bir kalem tahmine de girmez, planda görünüp gerçekleşmeyen bir kalem ise tahmini şişirir. Buradaki bütün sayılar planın doğru olduğunu varsayar; kayıt ile gerçek arasındaki fark, tahmin hatasının üstüne binen ikinci bir hatadır.

Özet

  • On iki değişikliğin tahmini plan farkından üretildi: 24 kalemin 13’ü sabit birim fiyatlı (tahmin edilebilir), 11’i kullanıma bağlıdır (tahmin edilemez); tutarın %56’sı sabit, %44’ü kullanıma bağlıdır ve 12 değişikliğin 9’u en az bir kullanıma bağlı kalem taşır.
  • Yalnız sabit kalem taşıyan 3 değişiklikte hata tam olarak sıfırdır; kalan 9’unda sapma 0,51x ile 1,45x arasındadır.
  • Toplam sapma %7’dir ve bu sayı aldatıcıdır: tek tek sapmalar birbirini götürür, oysa kararlar değişiklik başına verilir.
  • Eşik kuralının iki hatası ters yönde çalışır: 200 birimlik eşik 12 değişikliğin 10’unu durdurur (2’si gereksiz, onaya giden bütçe 4.422 birim) ve hiçbir pahalı değişikliği kaçırmaz; 300 birimde 5 durur ve 2 pahalı değişiklik geçer; 800 birimde 1 durur ve 1 pahalı değişiklik geçer.
  • Tahmin hatasını düşürmenin yolu daha iyi bir araç değil, tahminin kullanıma bağlı payını raporlamaktır; eşik o paya da bakarsa kesin tahminli yüksek tutarlı değişiklik gereksiz yere durmaz.

Sonraki Adım

Bu konunun buraya kadarki dersleri kaydı denetledi, politikadan geçirdi, sınadı ve fiyatladı. Dördü de aynı şeye baktı: kaynağın kendisine — makine, disk, ağ, kuyruk, depo. Ama bir makine yaratıldığında içi boştur; üzerinde hangi paketin kurulu olacağı, hangi dosyanın hangi izinle duracağı, hangi servisin açık kalacağı sağlama katmanının kaydında yazmaz. Sonraki ders bu sınırın öte yanını ele alır: makine yaratıldıktan sonra devreye giren yapılandırma yönetimi katmanı, ajanlı ve ajansız yaklaşımların farkı ve bu katmanın kendi kaydının sağlama kaydıyla nerede örtüşüp nerede ayrıldığı.

İ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