Ders 04 / 11
Kabul Ölçütleri
Tamamlanma tanımının bir veri yapısı olarak yazılması: kabul ölçütünün özne, ölçü ve sınır yuvaları, yuvayı kimin ne zaman doldurduğuna göre değişen geri dönüş sayısı ve keşif aşaması, ürün tarafının dikkatinin sınırlı bir bütçe olması ve kapanışta yazılan ölçütün gözlenen davranışı onaylaması.
İçindekiler
Önceki dersin keşifsel oturumu anlam-sapmasi sınıfında bulgular üretti, ama bu bulguların
kusur olup olmadığı bir ölçüm sorusu değildi: sınayan kusur sayar, geliştiren beklenen davranış
sayar, ürün tarafı ikisine de katılmaz. Böyle bir durumda kaçan hata sayısı bir ölçü değil bir
anlaşmazlıktır.
Anlaşmazlığı ölçüye çeviren şey kabul ölçütüdür (acceptance criteria): bir iş kaleminin ne
zaman bittiğini söyleyen, önceden yazılmış koşul. Kabul ölçütünün otomatik teste çevrilmesi
Birim Testi ve Test Odaklı Geliştirme kursunda kurulmuştu; buradaki soru başkadır — ölçütü
kim yazar ve ne zaman kapanır. Bu ders o iki soruyu bir bütçe dağıtımı olarak kurar;
dağıtılan kaynak ürün tarafının dikkatidir ve ölçüsü geri dönüş, yani yeniden açılan iştir.
(Dayanıklılık ve Güvenilirlik kursundaki geri dönüş bir devralmadan sonra asıl düğüme
dönmektir; burada
kastedilen, kapanmış sayılan bir işin yeniden açılmasıdır.)
Ölçütün Üç Yuvası
Bir kabul ölçütü üç yuva doldurur: özne neyin gözleneceğini, ölçü nasıl gözleneceğini, sınır hangi değerin kabul edildiğini söyler. Bir tamamlanma tanımı (definition of done) bu ölçütlerin tümünün karşılandığı durumdur. Üç yuvanın hepsi her ölçüt için gerekmez ve gerekliliği ölçütün türünden gelir: varlık ölçütü özne ile ölçü ister (“gecikme bildirimi gönderilir”), sayısal ölçüt sınırı da ister (“iade tarihinden beş gün sonra”), anlamsal ölçüt sınırı ister ve o sınırın gözlemden türetilmiş olmamasını ister (“bildirim ödüncü alan üyeye gider”).
Son madde bu dersin en keskin noktasıdır. Bir ölçüt kapanışta, sistemin gözlenen davranışına bakılarak yazılırsa sınır o davranışa eşitlenir; ölçüt hiçbir zaman kırmızı vermez, çünkü sınadığı şey kendi kaynağıdır.
TS15 (varsayım) — 20 iş kalemi, kalem başına 4 kabul ölçütü, toplam 80 ölçüt; tür dağılımı tohum 6060’tan gelir. Kusur kümesinin 60 kusuru kalemlere üçer üçer dağılır. TS16 (varsayım) — ürün tarafının ölçüt yazımına ayırabildiği süre ufuk boyunca 300 dakikadır; bir ölçüte bakması 12 dakika sürer. Bu, dağıtılan kaynaktır ve duyarlılığı taranır. TS17 (varsayım) — bir geri dönüşün bedeli açığa çıktığı aşamaya bağlıdır: yazım anında 5, geliştirme sırasında 20, kabul incelemesinde 60, sürümden sonra 180 dakika.
// kutuphane/kusurlar.mjs — birinci dersin kusur kumesi, aynen (tohum 20260731, 60 kusur, // 14 sinif). Bu ders envanterin bedel sutunlarini kullanmaz, yalniz kusur kumesini kullanir. const AGIRLIK = [["kural-siniri", 9], ["sema-uyusmazligi", 6], ["goc-veri-kaybi", 4], ["sozlesme-alani", 5], ["arayuz-durumu", 8], ["gorsel-sapma", 3], ["veri-buyumesi", 4], ["sql-birlestirme", 3], ["bilinen-acik", 3], ["erisilebilirlik", 4], ["bozulmus-yanit", 3], ["kurtarma-eksigi", 2], ["senaryo-disi", 5], ["anlam-sapmasi", 4]]; export function kusurlar(tohum, n) { let x = tohum; const rast = () => { x = (1103515245 * x + 12345) % 2147483648; return x / 2147483648; }; const toplam = AGIRLIK.reduce((a, [, w]) => a + w, 0); const liste = []; for (let i = 1; i <= n; i += 1) { let p = rast() * toplam, sinif = AGIRLIK[AGIRLIK.length - 1][0]; for (const [s, w] of AGIRLIK) { p -= w; if (p < 0) { sinif = s; break; } } liste.push({ no: i, sinif, olasilik: 1 + Math.floor(rast() * 5), etki: 1 + Math.floor(rast() * 5), duzeltme: 15 + 15 * Math.floor(rast() * 8) }); } return liste; }
Beş Politika
Beş politika ölçütü farklı ellere ve farklı anlara dağıtır. Eksik kalan bir yuva, onu dolduracak tarafın ölçüte ilk baktığı aşamada açığa çıkar: eksik sınırı ürün tarafı, eksik ölçüyü sınayan doldurur.
// kutuphane/olcut.mjs — kabul olcutunu uc yuvali bir veri yapisi olarak kurar ve olcutu // kimin ne zaman yazdiginin geri donusunu olcer. Girdi: kutuphane/kusurlar.mjs import { kusurlar } from "./kusurlar.mjs"; const IS = 20, BASINA = 4, TOHUM = 20260731, N = 60, URUNDK = 12, URUNBUTCE = 300; const BEDEL = [5, 20, 60, 180]; // TS17: geri donusun asama basina dakikasi const ASAMA = ["yazim ani", "gelistirme", "kabul incelemesi", "surumden sonra"]; let KAT = 6; // TS5: kacan hatanin duzeltme kati const g = (x, n) => String(x).padStart(n); // TS15: 20 is kalemi x 4 kabul olcutu = 80 olcut. Olcut turu gereken yuvalari belirler: // varlik (ozne+olcu), sayisal (ozne+olcu+sinir), anlamsal (ucu de; sinir gozlemden turetilemez). let x = 6060; const r = () => { x = (1103515245 * x + 12345) % 2147483648; return x / 2147483648; }; const TUR = ["varlik", "sayisal", "anlamsal"]; const OLCUTLER = Array.from({ length: IS * BASINA }, (_, i) => { const p = r(); return { no: i, kalem: Math.floor(i / BASINA), tur: TUR[p < 0.45 ? 0 : p < 0.8 ? 1 : 2] }; }); const GEREK = { varlik: ["ozne", "olcu"], sayisal: ["ozne", "olcu", "sinir"], anlamsal: ["ozne", "olcu", "sinir"] }; // Kusurlar kalemlere ucer ucer dagilir. Anlam sapmasi kusuru taniminca anlamsal bir olcutun // altinda durur — onu kusur yapan sey koddaki bir kural degil, olcutun ne dedigidir; kalemde // anlamsal olcut yoksa kalemin bir olcutu anlamsal olarak isaretlenir. const KUSUR = kusurlar(TOHUM, N).map((k, i) => ({ ...k, kalem: Math.floor(i / (N / IS)) })); const ANLAM = KUSUR.filter((k) => k.sinif === "anlam-sapmasi"); for (const k of ANLAM) { const kalemin = OLCUTLER.filter((o) => o.kalem === k.kalem); k.olcut = kalemin.find((o) => o.tur === "anlamsal") || Object.assign(kalemin[Math.floor(r() * kalemin.length)], { tur: "anlamsal" }); } // Politika: her olcutte hangi yuvalarin doldugu, sinir yuvasini dolduran urun mu, sinirin // gozlemden turetilip turetilmedigi ve eksigi kapatacak tarafin olcutu ilk okudugu asama. function olc(ad, yuva, urunMu, gozlemden, urunOkuma, sinayanOkuma, yazim) { const durum = OLCUTLER.map((o) => { const dolu = yuva(o); const eksik = GEREK[o.tur].filter((y) => dolu.includes(y) === false); const yargilar = eksik.length === 0 && (o.tur !== "anlamsal" || gozlemden === false); return { ...o, yargilar, sinirli: dolu.includes("sinir"), asama: eksik.length ? urunOkuma : yargilar ? -1 : sinayanOkuma }; }); const acik = durum.filter((o) => o.yargilar === false); const kacan = ANLAM.filter((k) => durum[k.olcut.no].yargilar === false); const urunSaat = urunMu ? URUNDK * durum.filter((o) => o.sinirli).length : 0; return { ad, urunSaat, yazimDk: yazim * OLCUTLER.length + urunSaat, geri: acik.length, kalem: new Set(acik.map((o) => o.kalem)).size, donusDk: acik.reduce((a, o) => a + BEDEL[o.asama], 0), dagilim: [0, 1, 2, 3].map((s) => acik.filter((o) => o.asama === s).length), kacan: kacan.length, risk: kacan.reduce((a, k) => a + k.olasilik * k.etki, 0), duz: KAT * kacan.reduce((a, k) => a + k.duzeltme, 0) }; } const IKI = ["ozne", "olcu"], UC = ["ozne", "olcu", "sinir"]; const P = () => [olc("Y1 gelistiren yazar, urun bakmaz", () => IKI, false, false, 2, 2, 5), olc("Y2 urun her olcute bakar, basta", () => UC, true, false, 0, 1, 5), olc("Y3 sinayan kapanista yazar", () => UC, false, true, 3, 2, 8), olc("Y4 olcut yok, tek satirlik tanim", () => [], false, false, 3, 3, 1), olc("Y5 urun yalniz anlamsal olcute bakar", (o) => (o.tur === "anlamsal" ? UC : IKI), true, false, 2, 2, 5)]; const toplam = (o) => o.yazimDk + o.donusDk + o.duz; const sigan = (t) => { const u = P().map((o, i) => [t[i], o.urunSaat <= URUNBUTCE, i]) .filter(([, s]) => s); return "Y" + (u.sort((a, b) => a[0] - b[0])[0][2] + 1); }; console.log(`${IS} is kalemi x ${BASINA} kabul olcutu = ${OLCUTLER.length} olcut (tohum 6060); tur: ` + TUR.map((t) => `${t} ${OLCUTLER.filter((o) => o.tur === t).length}`).join(", ")); console.log(`kusur kumesi tohum ${TOHUM}, ${N} kusur; ${ANLAM.length} anlam sapmasi, ` + `${ANLAM.reduce((a, k) => a + k.olasilik * k.etki, 0)} risk puani; ` + `${new Set(ANLAM.map((k) => k.kalem)).size} ayri is kaleminde`); console.log(`TS16 urun butcesi ${URUNBUTCE} dk (olcut basina ${URUNDK} dk); ` + `TS17 geri donus: ` + ASAMA.map((a, i) => `${a} ${BEDEL[i]} dk`).join(", ")); console.log("\npolitika urun dk butce geri donus kalem kesif asamasi"); for (const o of P()) console.log(o.ad.padEnd(38) + g(o.urunSaat, 7) + g(o.urunSaat <= URUNBUTCE ? "sigar" : "asiyor", 7) + g(o.geri, 12) + g(o.kalem, 7) + " " + (o.geri ? ASAMA[o.dagilim.indexOf(Math.max(...o.dagilim))] : "-")); console.log("\nayni kusur kumesinde bedel"); console.log("politika yazim dk donus dk kacan kacan risk duzeltme dk toplam dk"); for (const o of P()) console.log(o.ad.padEnd(38) + g(o.yazimDk, 9) + g(o.donusDk, 11) + g(o.kacan, 8) + g(o.risk, 12) + g(o.duz, 14) + g(toplam(o), 12)); console.log("\nurun butcesi TS16'nin duyarliligi — urun once anlamsal, sonra sayisal olcute bakar"); console.log("urun dk bakilan olcut geri donus kacan toplam dk"); const sira = [...OLCUTLER].sort((a, b) => TUR.indexOf(b.tur) - TUR.indexOf(a.tur)); for (const b of [0, 204, 300, 960]) { const kume = new Set(sira.slice(0, Math.floor(b / URUNDK)).map((o) => o.no)); const o = olc("", (t) => (kume.has(t.no) ? UC : IKI), true, false, 2, 2, 5); console.log(g(b, 7) + g(kume.size, 16) + g(o.geri, 13) + g(o.kacan, 8) + g(toplam(o), 12)); } console.log("\nTS5'in duyarliligi: kacan hatanin kati degisirse hangi politika en ucuz"); console.log("kat " + ["Y1", "Y2", "Y3", "Y4", "Y5"].map((a) => g(a, 8)).join("") + " en ucuz butceye sigan en ucuz"); for (const kat of [1, 6, 20]) { KAT = kat; const t = P().map(toplam); console.log(g(kat, 3) + t.map((n) => g(n, 8)).join("") + g("Y" + (t.indexOf(Math.min(...t)) + 1), 10) + g(sigan(t), 22)); }
20 is kalemi x 4 kabul olcutu = 80 olcut (tohum 6060); tur: varlik 34, sayisal 29, anlamsal 17
kusur kumesi tohum 20260731, 60 kusur; 6 anlam sapmasi, 60 risk puani; 5 ayri is kaleminde
TS16 urun butcesi 300 dk (olcut basina 12 dk); TS17 geri donus: yazim ani 5 dk, gelistirme 20 dk, kabul incelemesi 60 dk, surumden sonra 180 dk
politika urun dk butce geri donus kalem kesif asamasi
Y1 gelistiren yazar, urun bakmaz 0 sigar 46 20 kabul incelemesi
Y2 urun her olcute bakar, basta 960 asiyor 0 0 -
Y3 sinayan kapanista yazar 0 sigar 17 14 kabul incelemesi
Y4 olcut yok, tek satirlik tanim 0 sigar 80 20 surumden sonra
Y5 urun yalniz anlamsal olcute bakar 204 sigar 29 19 kabul incelemesi
ayni kusur kumesinde bedel
politika yazim dk donus dk kacan kacan risk duzeltme dk toplam dk
Y1 gelistiren yazar, urun bakmaz 400 2760 6 60 2970 6130
Y2 urun her olcute bakar, basta 1360 0 0 0 0 1360
Y3 sinayan kapanista yazar 640 1020 6 60 2970 4630
Y4 olcut yok, tek satirlik tanim 80 14400 6 60 2970 17450
Y5 urun yalniz anlamsal olcute bakar 604 1740 0 0 0 2344
urun butcesi TS16'nin duyarliligi — urun once anlamsal, sonra sayisal olcute bakar
urun dk bakilan olcut geri donus kacan toplam dk
0 0 46 6 6130
204 17 29 0 2344
300 25 21 0 1960
960 80 0 0 1360
TS5'in duyarliligi: kacan hatanin kati degisirse hangi politika en ucuz
kat Y1 Y2 Y3 Y4 Y5 en ucuz butceye sigan en ucuz
1 3655 1360 2155 14975 2344 Y2 Y3
6 6130 1360 4630 17450 2344 Y2 Y5
20 13060 1360 11560 24380 2344 Y2 Y5
Kâğıt Üzerindeki En Ucuz Politika Bütçede Yok
Y2 her ölçütü ürün tarafıyla birlikte yazar ve tabloda hiçbir geri dönüş üretmez: 1.360 dakikayla beş politikanın en ucuzudur. Ama ürün sütunu 960 dakika der, TS16’nın bütçesi 300 — politika uygulanamaz. Beş satırın en iyisi bir seçenek değildir; bir üst sınırdır.
Bütçeye sığanlar arasında Y5 öne çıkar: ürün tarafı yalnız on yedi anlamsal ölçüte bakar, 204 dakika harcar ve kaçan hatayı sıfıra indirir. Y1 aynı bütçeyi hiç kullanmaz ve altı kusuru sürüme geçirir; toplam bedeli 6.130 dakika, Y5’in 2,6 katı. Aradaki fark 204 dakikalık bir dikkatin nereye konduğudur.
Ürün bütçesi taraması bu dağıtımın biçimini verir. İlk 204 dakika kaçan hatanın tamamını kaldırır (6 → 0), çünkü o dakikalar anlamsal ölçütlere gider. Kalan 756 dakika bir kusur daha kurtarmaz; yalnız geri dönüşü 29’dan 0’a indirir ve toplamı 2.344’ten 1.360’a çeker. Aynı kaynağın ilk parçası ile son parçası farklı şeyler satın alır: ilk parça kaçan hatayı, son parça geri dönüşü.
Kapanışta Yazılan Ölçüt
Y3 tabloda makul görünür: ürün bütçesi harcamaz, yalnız on yedi geri dönüş üretir, 4.630 dakikayla Y1’den ucuzdur. Yine de altı kusuru sürüme geçirir. Nedeni ölçütün eksik olması değil — üç yuvası da doludur. Nedeni sınırın gözlemden türetilmiş olmasıdır: sınayan kapanışta, çalışan sistemi izleyerek ölçüt yazar ve sistemin yaptığını kabul edilebilir olarak kaydeder. Ölçüt kırmızı vermez, çünkü sınadığı davranışın kendisinden türemiştir. Yazılmış ölçüt sayısı bir kalite göstergesi değildir; ölçütün kaynağı göstergedir.
Y4 ölçütü hiç yazmaz ve seksen ölçütün seksenini sürümden sonra tartışmaya bırakır: 14.400 dakikalık geri dönüş, yirmi kalemin yirmisi yeniden açılır. Keşif aşaması sütunu bu politikayı ötekilerden ayıran tek şeydir — sayı değil, an. Aynı anlaşmazlık kabul incelemesinde 60, sürümden sonra 180 dakikaya mal olur.
Kararın Geri Dönüşü
Bu derste kararın geri dönüşü doğrudan ölçülen niceliktir. Y1 yirmi kalemin yirmisini yeniden açar, Y5 on dokuzunu, Y3 on dördünü, Y2 hiçbirini. Yeniden açılan kalem sayısı politikalar arasında az değişir; değişen, açılmanın hangi aşamada olduğu ve kaç kusurun sürüme geçtiğidir.
TS5 taraması kararı bir bağlama bağlar. Kaçan hatanın düzeltmesi yakalanmışla eşitse (kat 1) bütçeye sığan en ucuz politika Y3’tür — kapanışta yazılan ölçüt, kusur ucuzken savunulabilir. Kat 6 ve 20’de Y5 kazanır ve arayı açar. Yani ölçütü kimin yazacağı sorusunun cevabı, kaçan bir kusurun ekibe neye mal olduğuna bağlıdır; o sayı bilinmeden politika seçilemez.
Özet
- Kabul ölçütü üç yuvadır: özne, ölçü, sınır. Ölçütün türü hangi yuvaların gerekli olduğunu belirler; anlamsal ölçüt ayrıca sınırın gözlemden türetilmemiş olmasını ister.
- Ürün tarafının dikkati sınırlı bir kaynaktır (TS16, 300 dakika); her ölçüte bakan politika 960 dakika ister ve bütçeye sığmaz — tablodaki en ucuz satır bir seçenek değil, üst sınırdır.
- Aynı bütçenin ilk 204 dakikası kaçan hatanın tamamını kaldırır, kalan 756 dakika yalnız geri dönüşü azaltır; bir kaynağın ilk ve son parçası farklı şeyler satın alır.
- Kapanışta yazılan ölçüt üç yuvasını doldurur ama sınırı gözlenen davranıştan aldığı için kırmızı vermez ve altı kusuru sürüme geçirir.
- Ölçüt hiç yazılmadığında anlaşmazlık kaybolmaz, yalnız aşaması kayar: aynı geri dönüş kabul incelemesinde 60, sürümden sonra 180 dakikadır.
- Politika seçimi kaçan hatanın katına bağlıdır: kat 1’de kapanışta yazım, kat 6 ve 20’de hedefli ürün dikkati bütçeye sığan en ucuz seçenektir.
Sonraki Adım
Buradaki bütün ölçütleri ekibin içindeki taraflar yazdı: geliştiren, sınayan, ürün. Üçü de sistemi tasarlayan tarafta duruyor ve üçü de aynı sözlüğü kullanıyor. Oysa ölçütün sonunda karşılanması gereken beklenti ekibin dışındadır ve o beklenti hiçbir yuvaya yazılmadı — kütüphane görevlisinin bir ödünç işlemini kaç adımda bitirmeyi beklediği, bir üyenin gecikme bildirimini ne zaman anlamlı bulduğu. Sonraki ders bu doğrulamayı yönetir: kabul kapsamının kullanıcı görevlerine nasıl dağıtılacağı, dönen geri bildirimin kusur ile istek ile yanlış anlama arasında nasıl ayrılacağı ve her sınıfın ekibe neye mal olduğu.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.