İçeriğe geç
academia.sh

Ders 01 / 10

Yaşam Döngüsü Modelleri

Şelale, V, spiral ve artımlı yaşam döngülerinin aynı on sekiz iş kalemi üzerinde koşturulması: el değiştirme, kapıda bekleme, geri bildirim gecikmesi ve yeniden iş sayılır, her modelin kazandığı yerde neyi kaybettiği yazılır.

İçindekiler

Önceki kurs mimari kuralı bir nesneye çevirdi: kural yazıldı, denetime dönüştürüldü, yakaladığı ve kaçırdığı sayıldı. O ölçümlerin tamamı bir şeyi sessizce dışarıda bıraktı. Kuralı yazan, denetimi koşturan, yanlış alarm karşısında kuralı devre dışı bırakan ve bir sonraki değişikliği yapan insanlardır; bu kişilerin hangi sırayla çalıştığı, işi kime devrettiği ve bir hatayı ne zaman öğrendiği mimariye doğrudan yazılır.

Bu kursun ölçü birimi iş kalemidir: bir değişiklik isteğinin doğduğu andan işletime geçtiği ana kadarki yol. Her ders üçünü sayar — kalemin kaç sınır geçtiği ve her geçişte kaç tur beklediği, kaç kalemin geri döndüğü ve neden, ekip sınırı ile modül sınırının örtüşmediği yerde bunların ne kadar büyüdüğü. Through-line bölgesel kütüphane ağıdır: şube sistemleri, dışarıdan alınmış katalog, içeride yazılmış ödünç ve ücretlendirme servisleri, ayrı bir üyelik sistemi ve belediyedeki kimlik servisi. Kurgudur.

Ölçülecek İş Kalemi Kümesi

Aşağıdaki blok bir modeldir, ölçüm değil: aynı iş kalemi kümesini dört yaşam döngüsünde koşturulabilir hâle getiren bir veri yapısıdır.

SM1 — on sekiz iş kalemi beş adımdan geçer ve tek bir ekip çalışır. Her kalem her adımda 3 iş birimi tüketir, ekip tur başına 9 iş birimi işler. Gerekçe: modeller ancak aynı iş yükü ve aynı kapasite altında karşılaştırılabilir; farkı yaratan şey işin miktarı değil, partileme olmalıdır.

SM2 — on kalemde bir kusur doğar, doğduğu adım ile türü girdi olarak bilinmektedir. Türler ayrılır: eksik bilgi (bir ayrıntı hiç sorulmamıştır), yanlış sınır (iki kalem aynı veriyi farklı yerde tutar, hata ancak ikincisi yazılınca görünür), geç öğrenilen kısıt (kısıt yalnız işletimde çıkar). Geri bildirim gecikmesi ancak bilinen bir kusur kümesine karşı sayılabilir.

SM3 — partileme kuralı modelin kendisidir. Şelale ve V tek parti koşar, spiral risk sırasına göre üç döngüye, artımlı bağımlılık sırasına göre altı artıma böler.

SM4 — kusurun bulunduğu adım modele göre değişir. Eksik bilgi ile yanlış sınır sınamada, geç öğrenilen kısıt işletimde görünür. V modelinde sınama tasarımı üretim adımıyla birlikte yapıldığı için eksik bilgi doğduğu adımda göze çarpar. Bir kısıt işletimde öğrenildikten sonra sonraki partilerde aynı kısıtı taşıyan kalem kusurlu doğmaz; aynı partidekiler çoktan yazılmıştır.

SM5 — sabit maliyetler modele bağlıdır. V modelinde ilk üç adım kalem başına 1 iş birimi daha tüketir. Her partinin işletime alınması, kalem sayısından bağımsız 6 iş birimi tutar.

// yasam-dongusu.mjs — ayni 18 is kalemini dort yasam dongusunde kosturur (model)
// kusur kodu: e eksik bilgi, s yanlis sinir, k gec ogrenilen kisit; rakam kusurun dogdugu adim
import { writeFileSync } from "node:fs";

const ADIM = ["cozumleme", "tasarim", "gerceklestirme", "sinama", "isletim"];
const BIRIM = 3;      // bir is kaleminin bir adimda tukettigi is birimi
const KAPASITE = 9;   // tur basina is birimi (tek ekip)
const YAYIM = 6;      // her partinin isletime alma yuku, kalem sayisindan bagimsiz

const KALEM = [
  "uye dogrulama ucu    |uyelik |2|e0",
  "katalog arama ucu    |katalog|1|-",
  "odunc kaydi olusturma|odunc  |2|s1 S1",
  "raf durumu sorgusu   |katalog|0|-",
  "gecikme ucreti hesabi|ucret  |2|k0 K1",
  "sube stok devri      |sube   |1|e2",
  "uyelik dondurma      |uyelik |1|s1 S1",
  "ceza bildirimi       |ucret  |0|-",
  "sube raf etiketi     |sube   |0|-",
  "ayirtma kuyrugu      |odunc  |2|s1 S2",
  "kimlik eslestirme    |uyelik |2|k0 K1",
  "katalog ice aktarma  |katalog|1|-",
  "ucret iadesi         |ucret  |1|e2",
  "subeler arasi tasima |sube   |2|s1 S2",
  "odunc uzatma         |odunc  |0|-",
  "gecikme raporu       |ucret  |0|-",
  "uye bildirimi        |uyelik |0|k0 K2",
  "sube gunluk kapanis  |sube   |1|-",
].map((satir, i) => {
  const [ad, birim, risk, kusur] = satir.split("|").map((x) => x.trim());
  const [kod, anahtar = null] = kusur.split(" ");
  const TUR = { e: "eksik bilgi", s: "yanlis sinir", k: "gec kisit" };
  return { ad, birim, risk: +risk, artim: (i / 3) | 0, anahtar,
           tur: kod === "-" ? null : TUR[kod[0]], dogum: kod === "-" ? -1 : +kod[1] };
});

// SM3: partileme kurali modelin kendisidir; selale ve V tek parti, spiral risk sirali uc dongu,
// artimli bagimlilik sirali alti artim.
const riskli = [...KALEM].sort((a, b) => b.risk - a.risk);
const PARTI = {
  selale:  [KALEM],
  V:       [KALEM],
  spiral:  [0, 1, 2].map((c) => riskli.slice(c * 6, c * 6 + 6)),
  artimli: [0, 1, 2, 3, 4, 5].map((a) => KALEM.filter((k) => k.artim === a)),
};
const maliyet = (model, k, s) => (k.yayim ? YAYIM : BIRIM + (model === "V" && s <= 2 ? 1 : 0));
// V'de sinama tasarimi uretim adimiyla birlikte yapilir: eksik bilgi dogdugu adimda goze carpar.
const bulusAdimi = (model, k) =>
  (model === "V" && k.tur === "eksik bilgi") ? k.dogum : (k.tur === "gec kisit" ? 4 : 3);

function kostur(model) {
  const partiler = PARTI[model];
  const durum = new Map(KALEM.map((k) => [k.ad, { adimBitis: new Map(), parti: 0 }]));
  partiler.forEach((p, i) => p.forEach((k) => { durum.get(k.ad).parti = i; }));
  const ogrenilen = new Set(), cozulen = new Set(), bulgu = [];
  let tur = 0, bekleme = 0, elDegistirme = 0, yeniden = 0, toplam = 0;

  const segment = (liste, birimBasi) => {                 // her segment yeni bir turda baslar
    tur += 1;
    let kap = KAPASITE;
    const bitis = new Map();
    for (const k of liste) {
      let kalan = birimBasi(k);
      toplam += kalan;
      while (kalan > 0) {
        if (kap === 0) { tur += 1; kap = KAPASITE; }
        const al = Math.min(kap, kalan); kap -= al; kalan -= al;
      }
      bitis.set(k.ad, tur);
    }
    return bitis;
  };

  partiler.forEach((parti, p) => {
    for (let s = 0; s < ADIM.length; s++) {
      const liste = s === 4 ? [...parti, { ad: "__yayim", yayim: true }] : parti;
      const bitis = segment(liste, (k) => maliyet(model, k, s));
      for (const k of parti) {
        bekleme += tur - bitis.get(k.ad);
        durum.get(k.ad).adimBitis.set(s, bitis.get(k.ad));
      }
      elDegistirme += parti.length;

      const bulunan = KALEM.filter((k) => {               // bu adimda ortaya cikan kusurlar
        if (k.dogum < 0) return false;
        if (k.tur === "eksik bilgi") return parti.includes(k) && s === bulusAdimi(model, k);
        if (k.tur === "gec kisit") return s === 4 && parti.includes(k) && !ogrenilen.has(k.anahtar);
        const grup = KALEM.filter((x) => x.anahtar === k.anahtar);   // yanlis sinir: es kalem
        return s === 3 && !cozulen.has(k.anahtar) &&
          grup.every((x) => durum.get(x.ad).parti <= p) && grup.some((x) => durum.get(x.ad).parti === p);
      });
      for (const k of bulunan) {
        const adimlar = [...Array(s - k.dogum + 1)].map((_, i) => k.dogum + i);
        const bedel = adimlar.reduce((a, x) => a + maliyet(model, k, x), 0);
        const bulusTur = s === k.dogum ? bitis.get(k.ad) : tur;   // ayni adimda bulundu ise kapida degil
        bulgu.push({ model, kalem: k.ad, tur: k.tur, dogum: k.dogum, bulus: s, bedel,
          gecikmeAdim: (p - durum.get(k.ad).parti) * ADIM.length + s - k.dogum,
          gecikmeTur: bulusTur - durum.get(k.ad).adimBitis.get(k.dogum),
          ustune: KALEM.filter((x) => durum.get(x.ad).adimBitis.get(k.dogum) <= bulusTur).length });
        yeniden += bedel;
        elDegistirme += adimlar.length;
        if (k.tur === "yanlis sinir") cozulen.add(k.anahtar);
      }
      if (bulunan.length) segment(bulunan, (k) =>
        [...Array(s - k.dogum + 1)].reduce((a, _, i) => a + maliyet(model, k, k.dogum + i), 0));
      if (s === 4) for (const k of parti) if (k.tur === "gec kisit") ogrenilen.add(k.anahtar);
    }
  });
  return { model, tur, bekleme, elDegistirme, yeniden, toplam, bulgu };
}

const SONUC = ["selale", "V", "spiral", "artimli"].map(kostur);
writeFileSync("bulgu.json", JSON.stringify(SONUC.flatMap((s) => s.bulgu)));

const TUR3 = ["eksik bilgi", "yanlis sinir", "gec kisit"];
const tablo = (basliklar, satirlar, ust) => {             // negatif genislik sola yaslar
  const yaz = (h) => console.log(h.map((c, i) =>
    basliklar[i][1] < 0 ? String(c).padEnd(-basliklar[i][1]) : String(c).padStart(basliklar[i][1])).join(""));
  console.log();
  if (ust) console.log(ust);
  yaz(basliklar.map((b) => b[0]));
  satirlar.forEach(yaz);
};
const ort = (g, f) => (g.length ? (g.reduce((a, b) => a + f(b), 0) / g.length) : null);

console.log(`is kalemi: ${KALEM.length}, adim: ${ADIM.length}, kusurlu kalem: ` +
  `${KALEM.filter((k) => k.dogum >= 0).length} ` +
  `(${TUR3.map((t) => `${KALEM.filter((k) => k.tur === t).length} ${t}`).join(", ")})`);

tablo([["model", -9], ["parti", 7], ["tur", 6], ["bekleme", 9], ["el degistirme", 15], ["toplam is birimi", 18]],
  SONUC.map((s) => [s.model, PARTI[s.model].length, s.tur, s.bekleme, s.elDegistirme, s.toplam]));

tablo([["model", -9], ["bulunan kusur", 14], ["ort. gecikme (adim)", 21], ["ort. gecikme (tur)", 20],
       ["ort. ustune kurulan", 21], ["yeniden is", 12]],
  SONUC.map((s) => [s.model, s.bulgu.length, ort(s.bulgu, (b) => b.gecikmeAdim).toFixed(2),
    ort(s.bulgu, (b) => b.gecikmeTur).toFixed(2), ort(s.bulgu, (b) => b.ustune).toFixed(2), s.yeniden]));

tablo([["model", -9], ["eksik bilgi", 13], ["yanlis sinir", 14], ["gec kisit", 11]],
  SONUC.map((s) => [s.model, ...TUR3.map((t) => {
    const o = ort(s.bulgu.filter((b) => b.tur === t), (b) => b.gecikmeTur);
    return o === null ? "-" : o.toFixed(1);
  })]), "kusur turune gore ortalama gecikme (tur):");
is kalemi: 18, adim: 5, kusurlu kalem: 10 (3 eksik bilgi, 4 yanlis sinir, 3 gec kisit)

model      parti   tur  bekleme  el degistirme  toplam is birimi
selale         1    43      243            125               381
V              1    50      288            120               440
spiral         3    47       63            125               393
artimli        6    48       18            120               396

model     bulunan kusur  ort. gecikme (adim)  ort. gecikme (tur)  ort. ustune kurulan  yeniden is
selale               10                 2.50               20.10                18.00         105
V                    10                 2.00               19.00                14.70         110
spiral               10                 3.00                8.50                 9.60         105
artimli               9                 4.00                5.22                10.67          90

kusur turune gore ortalama gecikme (tur):
model      eksik bilgi  yanlis sinir  gec kisit
selale            13.3          14.8       34.0
V                  0.0          18.5       38.7
spiral             4.0           9.3       12.0
artimli            1.7           7.8        5.5

Dört Modelin Okunması

İlk tablo akışı, ikincisi öğrenmeyi ölçer ve ikisi ters yönde hareket eder.

Şelale en az turda biter (43) çünkü sabit yükü bir kez öder. Bedeli ikinci sütundadır: 243 tur bekleme birikir, çünkü kalem kendi adımını bitirince partinin son kalemini bekler ve her kapıda yeniden bekler. Öğrenme tarafı daha serttir — gecikme 20,1 tur ve bir kusur bulunduğunda on sekiz kalemin on sekizi o yanlış varsayımın üstündedir. Geç öğrenilen kısıtta gecikme 34 tura çıkar, çünkü işletime alma sonda tek seferdir.

V modeli tek bir kusur türünde kesin kazanç sağlar: eksik bilginin gecikmesi 13,3 turdan 0 tura iner, soru cevabın gerekli olduğu adımda sorulur. Bedeli iki yerdedir. Toplam iş gücü 381’den 440 birime çıkar (270 temel iş, 54 birim sınama tasarımı eki, 6 birim yayım, 110 birim yeniden iş) ve akış 50 tura uzar. Diğer iki türde ise V hiçbir şey yapmaz — yanlış sınır 14,8’den 18,5 tura, geç öğrenilen kısıt 34’ten 38,7 tura çıkar, çünkü koşumun tamamı uzamıştır.

Spiral partiyi risk sırasına böler; en riskli altı kalem ilk döngüde işletime kadar gittiği için geç öğrenilen kısıt 34 turdan 12 tura, ortalama gecikme 8,5 tura, bekleme 63’e iner. Bedeli üç kez ödenen yayım yükü (18 birim) ve kaçırılan bir öğrenmedir: risk sırası aynı kısıtı taşıyan iki kalemi (gecikme ucreti hesabi ve kimlik eslestirme) aynı döngüye koyduğu için kısıt ikisi de yazıldıktan sonra öğrenilir ve ikisi de yeniden işe girer.

Artımlı model bağımlılık sırasına göre altı artıma böler. Bekleme 18 tura, gecikme 5,22 tura iner ve bulunan kusur ona değil dokuza düşer: birinci artımda öğrenilen kısıt üçüncü artımdaki eş kalemi kusursuz doğurur, yeniden iş 90 birime iner. Bedeli turlardadır — altı yayım yükü akışı dört modelin en uzunu olan 48 tura çıkarır — ve adım cinsinden gecikme en yüksek buradadır (4,00): artım sınırını kesen yanlış sınır ancak ikinci artımda görünür, birincinin teslim edilmiş kalemi geri açılır.

Gecikme Yeniden İşe Nasıl Dönüşür

SM6 — yeniden işin bedeli iki çarpandan gelir: yeniden yapılan adım sayısı ve aynı varsayımın üstüne kurulmuş kalem sayısı. Aşağıdaki blok dört koşumun otuz dokuz bulgusunu tek havuzda toplar ve bu iki çarpanı gecikmeye karşı ayırır.

// gecikme.mjs — yasam-dongusu.mjs'in yazdigi bulgulari okur; gecikme ile yeniden is baginti
import { readFileSync } from "node:fs";

const BULGU = JSON.parse(readFileSync("bulgu.json", "utf8"));
const KOVA = [[0, 0], [1, 5], [6, 12], [13, 24], [25, 99]];

console.log(`dort modelde toplam ${BULGU.length} kusur bulgusu; kova = geri bildirim gecikmesi (tur)\n`);
console.log(`${"gecikme".padStart(9)}${"bulgu".padStart(7)}${"ort. yeniden is (birim)".padStart(25)}` +
  `${"ort. yeniden adim".padStart(19)}${"ort. ustune kurulan kalem".padStart(27)}`);
for (const [a, b] of KOVA) {
  const g = BULGU.filter((x) => x.gecikmeTur >= a && x.gecikmeTur <= b);
  if (!g.length) continue;
  const o = (f) => (g.reduce((s, x) => s + f(x), 0) / g.length).toFixed(1);
  console.log(`${(a === b ? `${a}` : `${a}-${b}`).padStart(9)}${String(g.length).padStart(7)}` +
    `${o((x) => x.bedel).padStart(25)}${o((x) => x.bulus - x.dogum + 1).padStart(19)}` +
    `${o((x) => x.ustune).padStart(27)}`);
}

// SM6: yeniden isin bedeli iki carpandan gelir; yeniden yapilan adim sayisi ve ayni varsayimin
// ustune kurulmus kalem sayisi. Ikisi de gecikmeyle birlikte buyur.
const ADI = (i) => ["cozumleme", "tasarim", "gerceklestirme", "sinama", "isletim"][i];
const kume = (g, f) => [...new Set(g.map(f))].join(" ve ");
const pahali = [...BULGU].sort((a, b) => b.bedel - a.bedel).slice(0, 5);
console.log(`\nen pahali bes bulgu: tur ${kume(pahali, (x) => x.tur)}, ` +
  `dogum ${kume(pahali, (x) => ADI(x.dogum))}, bulus ${kume(pahali, (x) => ADI(x.bulus))}, ` +
  `ort. ${(pahali.reduce((a, x) => a + x.bedel, 0) / 5).toFixed(1)} birim yeniden is`);
dort modelde toplam 39 kusur bulgusu; kova = geri bildirim gecikmesi (tur)

  gecikme  bulgu  ort. yeniden is (birim)  ort. yeniden adim  ort. ustune kurulan kalem
        0      3                      4.0                1.0                        7.0
      1-5     11                      8.7                2.9                       10.4
     6-12      6                     10.5                3.5                       13.5
    13-24     13                     10.8                3.4                       15.0
    25-99      6                     16.5                5.0                       18.0

en pahali bes bulgu: tur gec kisit, dogum cozumleme, bulus isletim, ort. 16.8 birim yeniden is

Kovalar tek yönde ilerler: gecikme sıfırken yeniden iş 4 birim, yirmi beş turun üstünde 16,5 birimdir. Aradaki fark iki çarpandan gelir. Yeniden yapılan adım 1,0’dan 5,0’a çıkar, çünkü geç bulunan kusur daha çok adımı geçersiz kılar; aynı varsayımın üstüne kurulmuş kalem 7,0’dan 18,0’a çıkar, çünkü bu arada kusurun etrafında iş birikmiştir. En pahalı beş bulgunun beşi de aynı türdendir — çözümlemede doğup işletimde bulunan geç öğrenilen kısıt.

Temel iş dört modelde de 270 birimdir; farkı sabit yük ile yeniden iş yaratır. Bir modelin “daha iyi” olduğunu söyleyen tek bir sayı yoktur: geç öğrenilen kısıt ağır basıyorsa küçük parti, eksik bilgi ağır basıyorsa erken doğrulama, sabit yayım maliyeti yüksekse büyük parti kazanır.

Özet

  • Aynı 18 iş kalemi dört yaşam döngüsünde koşturuldu; 270 birimlik temel iş sabit tutuldu, farkı yalnız partileme kuralı ile sabit yük yarattı.
  • Şelale en kısa akışı verir (43 tur) ama 243 tur bekleme biriktirir, gecikmeyi 20,1 tura çıkarır ve her kusurda 18 kalemin tamamı yanlış varsayımın üstündedir.
  • V modeli eksik bilgi gecikmesini 13,3 turdan 0’a indirir; karşılığında toplam iş gücü 381’den 440 birime çıkar ve diğer iki kusur türünde gecikme artar.
  • Spiral gecikmeyi 8,5 tura indirir ama aynı kısıtın iki kalemini aynı döngüye koyup partiler arası öğrenmeyi kaçırır; artımlı beklemeyi 18 tura, yeniden işi 90 birime indirir ve bir kusuru hiç doğurmaz, bedeli 48 turluk en uzun akıştır.
  • Havuzlanmış 39 bulguda gecikme 25 turun üstüne çıktığında yeniden iş 4 birimden 16,5 birime yükselir; yeniden yapılan adım 1,0’dan 5,0’a, üstüne kurulmuş kalem 7,0’dan 18,0’a çıkar.

Sonraki Adım

Bu dersin en güçlü tek değişkeni parti büyüklüğüydü: on sekiz kalemlik tek parti ile üç kalemlik altı parti arasındaki fark beklemeyi on üç kat, geri bildirim gecikmesini dört kat değiştirdi. Fakat parti büyüklüğü burada elle seçildi — altı artımın neden altı olduğu hiçbir yerde ölçülmedi. Sonraki ders yinelemeli teslimatın çerçevelerini ortak yapısıyla ele alır ve asıl değişkeni tarar: yineleme uzunluğu bir haftadan dört haftaya çıkarıldığında akış süresi, yeniden iş ve törene giden süre nasıl değişir. Kısa yineleme gecikmeyi düşürür, ama her yinelemenin planlama ve gözden geçirme yükü sabittir ve yinelemeye sığmayan kalem yarım kalıp sonrakine devrolur. İki maliyet ters yönde büyüdüğü için taramadan tek bir en iyi uzunluk çıkmaz.

İ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