Ders 05 / 11
Kullanıcı Kabul Testi
Son kullanıcı doğrulamasının bir dağıtım kararı olarak yönetilmesi: kabul kapsamının kullanıcı görevlerine dört ayrı bölünmesi, dönen geri bildirimin kusur ile istek ile yanlış anlama arasında ayrılması, her sınıfın dakikayla ölçülen bedeli ve geri bildirim hacminin neden bir kalite ölçüsü olmadığı.
İçindekiler
Önceki dersin bütün ölçütlerini ekibin içindeki taraflar yazdı. Üçü de sistemi tasarlayan tarafta duruyor ve aynı sözlüğü kullanıyor; oysa ölçütün sonunda karşılanması gereken beklenti ekibin dışındadır. Kütüphane görevlisinin bir ödünç işlemini kaç adımda bitirmeyi beklediği ya da bir üyenin gecikme bildirimini ne zaman anlamlı bulduğu hiçbir yuvaya yazılmadı.
Kullanıcı kabul testi (user acceptance testing) bu boşluğu son kullanıcıya sordurur. Bu ders onu bir dağıtım kararı olarak kurar: dağıtılan kaynak kullanıcının zamanıdır, dağıtım birimi kullanıcı görevidir ve dönen şey tek türden değildir. Geri bildirimin bir kısmı kusurdur, bir kısmı yeni bir istektir, bir kısmı da sistemin doğru davranışını yanlış okuyan bir yanlış anlamadır — ve üçünün bedeli farklıdır.
Kabul Aşamasına Ne Ulaşır
Kabul testi son kapıdır: ondan önceki kapıların gördüğü kusur buraya gelmez. İkinci dersin bütçeye sığan dokuz kalemi dokuz sınıfı kapatıyordu; kalan beş sınıfın 18 kusuru bu aşamaya ulaşır. Dördüncü dersin Y5 politikası da bir durum bırakmıştı: yirmi dokuz sayısal ölçütün sınırı boştur.
Bu iki miras geri bildirimin sınıfını belirler. Bir kullanıcı bir ölçütü gözlediğinde sonuç üçe ayrılır: sınırı boş bir ölçütte kullanıcı kendi sınırını söyler ve bu bir istektir; tam ve altında kusur olan bir ölçütte bir kusur bildirir; tam, kusursuz ama ölçüsü kullanıcıya görünmeyen bir varlık ölçütünde gördüğünü kendi beklentisiyle karşılaştırır ve bir yanlış anlama üretir.
TS18 (varsayım) — 20 iş kalemi 12 kullanıcı görevine dağılır, her göreve en az bir kalem düşer; kullanım payı tohum 4712’den gelir. TS19 (varsayım) — kabul bütçesi 600 kullanıcı dakikası, bir ölçütü gözlemek 8 dakika; bütçe böylece 75 ölçüt gözlemidir ve seksen ölçütün tamamına yetmez. TS20 (varsayım) — isteklerin %30’u kabul edilip işe dönüşür, gerisi kayıtta kalır. TS21 (varsayım) — bir isteği karara bağlamak 30, kabul edilen isteği yapmak 120, bir yanlış anlamayı gidermek 20 dakikadır; kusurun bedeli kusur kümesindeki düzeltme dakikasıdır.
// 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; }
Dört Kapsam Dağıtımı
Aynı 600 dakika dört kurala göre bölünür: eşit (K1), kullanım payına göre (K2), ölçütü en çok eksik olan göreve (K3) ve yalnız son dört yeni göreve (K4).
// kutuphane/kabul.mjs — kabul kapsamini kullanici gorevlerine dagitir ve donen geri bildirimi // kusur / istek / yanlis anlama diye ayirir. Girdi: kutuphane/kusurlar.mjs import { kusurlar } from "./kusurlar.mjs"; const IS = 20, BASINA = 4, GOREV = 12, TOHUM = 20260731, N = 60, KAT = 6; const BUTCE = 600, OLCUTDK = 8; // TS19: kabul butcesi, olcut basina kullanici dakikasi let KABUL = 0.3; // TS20: istegin ise donusme orani const ISTEKDK = 30, YANLISDK = 20, YENIISDK = 120; // TS21: sinif basina bedel const v = (x, n = 1) => x.toFixed(n).replace(".", ","); const g = (x, n) => String(x).padStart(n); const uret = (t) => { let x = t; return () => { x = (1103515245 * x + 12345) % 2147483648; return x / 2147483648; }; }; // Dorduncu dersin olcut kumesi aynen: 80 olcut, tohum 6060, uc tur. const r = uret(6060); 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] }; }); // Kabul asamasina ulasan kusurlar: ikinci dersin butceye sigan dokuz kaleminin gormedigi // bes sinif, yani o dersin 18 kacan kusuru. Betikli kumenin gordukleri buraya gelmez. const ULASAN = new Set(["erisilebilirlik", "bozulmus-yanit", "kurtarma-eksigi", "senaryo-disi", "anlam-sapmasi"]); const KUSUR = kusurlar(TOHUM, N).map((k, i) => ({ ...k, kalem: Math.floor(i / (N / IS)) })) .filter((k) => ULASAN.has(k.sinif)); for (const k of KUSUR.filter((k) => k.sinif === "anlam-sapmasi")) { const kalemin = OLCUTLER.filter((o) => o.kalem === k.kalem); if (kalemin.some((o) => o.tur === "anlamsal") === false) Object.assign(kalemin[Math.floor(r() * kalemin.length)], { tur: "anlamsal" }); } // Dorduncu dersin Y5 politikasinin biraktigi durum: sayisal olcutlerin siniri bostur. for (const o of OLCUTLER) o.tam = o.tur !== "sayisal"; // TS18: 20 is kalemi 12 kullanici gorevine dagilir (her goreve en az bir kalem); // gorevin kullanim payi ayni tohumdan gelir. const r2 = uret(4712); const PAY = Array.from({ length: GOREV }, () => 1 + Math.floor(r2() * 9)); const PAYTOP = PAY.reduce((a, x) => a + x, 0); const KALEMGOREV = Array.from({ length: IS }, (_, i) => i % GOREV); for (let j = IS - 1; j > 0; j -= 1) { const q = Math.floor(r2() * (j + 1)); [KALEMGOREV[j], KALEMGOREV[q]] = [KALEMGOREV[q], KALEMGOREV[j]]; } const gorevi = (o) => KALEMGOREV[o.kalem]; // Her kusur kaleminden bir olcutun altinda durur; anlam sapmasi anlamsal olcutte. const r3 = uret(9313); for (const k of KUSUR) { const aday = OLCUTLER.filter((o) => o.kalem === k.kalem && (k.sinif === "anlam-sapmasi" ? o.tur === "anlamsal" : true)); k.olcut = aday[Math.floor(r3() * aday.length)]; } const KUSURLU = new Set(KUSUR.map((k) => k.olcut.no)); // Bir gorevde m dakika: m/OLCUTDK olcut gozlenir. Geri bildirim olcutun durumundan cikar: // siniri bos olcut -> istek (kullanici kendi sinirini soyler), kusurlu tam olcut -> kusur, // kusursuz varlik olcutu -> yanlis anlama (olcusu kullaniciya gorunmez, kendi beklentisiyle // karsilastirir), kusursuz anlamsal olcut -> geri bildirim yok. function sina(dagitim) { const bulgu = { kusur: [], istek: 0, "yanlis anlama": 0 }; let gozlenen = 0, an = 0, adet = 0; for (let go = 0; go < GOREV; go += 1) { const liste = OLCUTLER.filter((o) => gorevi(o) === go); const kac = Math.min(liste.length, Math.floor(dagitim[go] / OLCUTDK)); gozlenen += kac; liste.slice(0, kac).forEach((o, i) => { const t = (i + 1) * OLCUTDK; if (o.tam === false) { bulgu.istek += 1; an += t; adet += 1; } else if (KUSURLU.has(o.no)) { const l = KUSUR.filter((k) => k.olcut.no === o.no); bulgu.kusur.push(...l); an += t * l.length; adet += l.length; } else if (o.tur === "varlik") { bulgu["yanlis anlama"] += 1; an += t; adet += 1; } }); } const kacan = KUSUR.filter((k) => bulgu.kusur.includes(k) === false); // Bulunan kusurun duzeltmesi bir kez, kacanin duzeltmesi TS5 katiyla odenir. const o = { gozlenen, an: an / (adet || 1), kusur: bulgu.kusur.length, istek: bulgu.istek, yanlis: bulgu["yanlis anlama"], kacan: kacan.length, kacanRisk: kacan.reduce((a, k) => a + k.olasilik * k.etki, 0), kusurDk: bulgu.kusur.reduce((a, k) => a + k.duzeltme, 0), kacanDk: KAT * kacan.reduce((a, k) => a + k.duzeltme, 0), istekDk: ISTEKDK * bulgu.istek + KABUL * bulgu.istek * YENIISDK, yanlisDk: YANLISDK * bulgu["yanlis anlama"] }; return { ...o, toplam: o.kusurDk + o.kacanDk + o.istekDk + o.yanlisDk }; } const esit = () => Array.from({ length: GOREV }, () => BUTCE / GOREV); const paya = () => PAY.map((p) => (BUTCE * p) / PAYTOP); const eksige = () => { const e = Array.from({ length: GOREV }, (_, go) => OLCUTLER.filter((o) => gorevi(o) === go && o.tam === false).length); const t = e.reduce((a, x) => a + x, 0); return e.map((x) => (BUTCE * x) / t); }; const yeni = () => Array.from({ length: GOREV }, (_, go) => (go >= GOREV - 4 ? BUTCE / 4 : 0)); const D = [["K1 esit dagitim", esit()], ["K2 kullanim payina gore", paya()], ["K3 en cok eksik olcute", eksige()], ["K4 yalniz dort yeni goreve", yeni()]]; console.log(`${OLCUTLER.length} olcut (tohum 6060) ${GOREV} kullanici gorevine dagildi (tohum 4712)`); console.log("gorev basina olcut: " + Array.from({ length: GOREV }, (_, go) => OLCUTLER.filter((o) => gorevi(o) === go).length).join(" ") + "; kullanim payi: " + PAY.map((p) => v((100 * p) / PAYTOP, 0) + "%").join(" ")); console.log(`dorduncu dersin Y5 durumu: ${OLCUTLER.filter((o) => o.tam === false).length} olcutun siniri bos; ` + `${KUSURLU.size} olcutun altinda kusur var`); console.log(`\nkabul butcesi TS19 = ${BUTCE} kullanici dk, olcut basina ${OLCUTDK} dk = ${BUTCE / OLCUTDK} olcut`); console.log("dagitim gozlenen kusur istek yanlis anlama kusur payi bulgu ani"); for (const [ad, d] of D) { const o = sina(d); const t = o.kusur + o.istek + o.yanlis; console.log(ad.padEnd(26) + g(o.gozlenen, 8) + g(o.kusur, 7) + g(o.istek, 7) + g(o.yanlis, 15) + g(t ? v(o.kusur / t, 2) : "-", 12) + g(v(o.an), 11)); } console.log("\nsiniflarin bedeli — istek 30 dk karar + %30'u 120 dk is, yanlis anlama 20 dk (TS21)"); console.log("dagitim kusur dk istek dk yanlis dk kacan kacan risk kacan dk toplam dk"); for (const [ad, d] of D) { const o = sina(d); console.log(ad.padEnd(26) + g(o.kusurDk, 8) + g(v(o.istekDk, 0), 11) + g(o.yanlisDk, 12) + g(o.kacan, 7) + g(o.kacanRisk, 12) + g(o.kacanDk, 11) + g(v(o.toplam, 0), 12)); } console.log("\nTS20'nin duyarliligi: istegin kac katinin ise donustugu"); console.log("kabul orani " + D.map(([ad]) => g(ad.slice(0, 2), 9)).join("") + " en ucuz"); for (const p of [0, 0.3, 0.6]) { KABUL = p; const t = D.map(([, d]) => sina(d).toplam); console.log(g("%" + 100 * p, 11) + t.map((n) => g(v(n, 0), 9)).join("") + g(D[t.indexOf(Math.min(...t))][0].slice(0, 2), 10)); }
80 olcut (tohum 6060) 12 kullanici gorevine dagildi (tohum 4712)
gorev basina olcut: 8 8 8 8 8 8 8 8 4 4 4 4; kullanim payi: 4% 8% 10% 12% 3% 10% 10% 3% 12% 12% 12% 6%
dorduncu dersin Y5 durumu: 29 olcutun siniri bos; 15 olcutun altinda kusur var
kabul butcesi TS19 = 600 kullanici dk, olcut basina 8 dk = 75 olcut
dagitim gozlenen kusur istek yanlis anlama kusur payi bulgu ani
K1 esit dagitim 64 7 21 30 0,12 25,2
K2 kullanim payina gore 54 8 21 22 0,16 26,0
K3 en cok eksik olcute 60 6 23 24 0,11 28,7
K4 yalniz dort yeni goreve 16 6 4 8 0,33 19,6
siniflarin bedeli — istek 30 dk karar + %30'u 120 dk is, yanlis anlama 20 dk (TS21)
dagitim kusur dk istek dk yanlis dk kacan kacan risk kacan dk toplam dk
K1 esit dagitim 675 1386 600 11 88 3510 6171
K2 kullanim payina gore 720 1386 440 10 82 3240 5786
K3 en cok eksik olcute 555 1518 480 12 102 4230 6783
K4 yalniz dort yeni goreve 570 264 160 12 89 4140 5134
TS20'nin duyarliligi: istegin kac katinin ise donustugu
kabul orani K1 K2 K3 K4 en ucuz
%0 5415 5030 5955 4990 K4
%30 6171 5786 6783 5134 K4
%60 6927 6542 7611 5278 K4
Geri Bildirim Hacmi Bir Kalite Ölçüsü Değildir
Dört dağıtımın ürettiği geri bildirimin kusur payı 0,11 ile 0,33 arasındadır. Yani en iyi durumda üç bildirimden biri, en kötü durumda dokuz bildirimden biri gerçek bir kusurdur; gerisi istek ve yanlış anlamadır. K1 elli sekiz bildirim üretir ve bunun yalnız yedisi kusurdur. Bir kabul turunu “elli sekiz bulgu” diye özetlemek, ekibe yedi kusurdan söz ettiğini söylemez.
Sınıflandırma yapılmadan bu ayrım görünmez: üç sınıf aynı listeye düşer ve aynı biçimde yazılır. Ayrımı ölçüte bağlamak onu bir yargı olmaktan çıkarır: sınırı boş bir ölçütte gelen bildirim istektir, çünkü ortada karşılanmayan bir söz yoktur — kullanıcının sınırı ilk kez orada söylenmektedir.
Hangi Dağıtım Ne Satın Alır
K3 en çok isteği getiren dağıtımdır (23). Şaşırtıcı değildir: eksik ölçütü hedeflemek, tanımı gereği sınırı yazılmamış ölçütleri gözletir. Kaçan hatası da en yüksektir (12) ve toplamı en pahalıdır (6.783 dakika). Belirsizliği hedeflemek kusuru değil isteği bulur.
K2 kullanım payına göre dağıtır, en çok kusuru bulur (8) ve en az kusuru kaçırır (10, kaçan risk 82). Kullanıcının çok kullandığı görevde duran kusur, sınandığında bulunur. Buna karşılık K2 gözleyebildiği ölçüt sayısında en düşüktür (54), çünkü bütçenin büyük parçası az sayıda göreve gider.
K4 en ucuz görünür (5.134 dakika) ve en yüksek kusur payına sahiptir (0,33). Nedeni bir üstünlük değildir: yalnız on altı ölçüt gözlediği için gürültü de üretmez. İstek ve yanlış anlama bedeli 424 dakikada kalır, ama on iki kusuru kaçırır. Toplam bedel sütunu tek başına yanıltıcıdır, çünkü geri bildirim üretmemek ucuzdur. İki sütunu birlikte okumak gerekir: K2 kaçan hatada, K4 işlenen bildirimde ucuzdur ve ikisi aynı şeyi ölçmez.
TS20 taraması bunu doğrular. İsteklerin hiçbiri işe dönüşmese bile (kabul oranı %0) sıralama değişmez; %60’a çıkarıldığında K4’ün önü açılır, çünkü işlenecek isteği zaten azdır. Bir kabul turunun bedelini asıl belirleyen, bulunan kusur sayısı değil işlenen bildirim sayısıdır.
Kararın Geri Dönüşü
Kabul testinin kırmızısı tek bir karar değildir, üç ayrı kuyruktur. Kusur sürümü durdurur ve düzeltmeye gider. İstek ürün tarafının kararına gider ve %30’u işe dönüşür; kalanı bir kayıt olarak durur. Yanlış anlama koda değil arayüz metnine ya da belgeye gider ve dördüncü dersin ölçüt yuvalarına geri besleme yapar — bir yanlış anlama, ölçüsü kullanıcıya görünmeyen bir ölçütün işaretidir.
Bulgunun ortalama anı 19,6 ile 28,7 dakika arasındadır, ama bu sayı yanıltıcı biçimde küçüktür: kabul penceresi geliştirme bittikten sonra açılır. İkinci dersin betikli kümesi kusuru ortalama 18,2 dakikada, üçüncü dersin keşifsel oturumu 74,8 dakikada haber veriyordu; kabul testinin bulgusu ise ancak 600 dakikalık pencere açıldığında ortaya çıkar. Aynı kusur zinciri boyunca haber verme anı büyür ve bedeli onunla büyür.
Özet
- Kabul aşamasına yalnız önceki kapıların görmediği kusurlar ulaşır: ikinci dersin beş kapalı olmayan sınıfından 18 kusur.
- Geri bildirim üç sınıftır ve sınıfı ölçütün durumu belirler: sınırı boş ölçüt istek, kusurlu tam ölçüt kusur, ölçüsü kullanıcıya görünmeyen kusursuz ölçüt yanlış anlama üretir.
- Kusur payı 0,11 ile 0,33 arasındadır; bir kabul turunu bulgu sayısıyla özetlemek ekibe kaç kusur bulunduğunu söylemez.
- Belirsizliği hedefleyen dağıtım en çok isteği ve en çok kaçan hatayı üretir (23 istek, 12 kaçan, 6.783 dakika); kullanım payına göre dağıtım en çok kusuru bulur (8) ve en az kaçırır.
- En düşük toplam bedelli dağıtım en az bildirim üretendir (K4, 5.134 dakika, 12 kaçan); geri bildirim üretmemek ucuzdur, bu yüzden toplam bedel tek başına okunmaz.
- Kabul testinin kırmızısı üç ayrı kuyruğa gider: düzeltme, ürün kararı ve arayüz metni; yanlış anlama ölçüt yuvalarına geri besleme yapar.
Sonraki Adım
Bu konu dört karar bıraktı. Plan kapsamı, ölçütü ve sorumluluğu yazdı; risk sırası aynı bütçeyi olasılık ile etkiye göre böldü; keşifsel oturum betikli kümenin göremediği sınıfa genişlik yerine derinlikle ulaştı; kabul ölçütü ve kullanıcı kabul testi tamamlanmanın tanımını ekibin dışına taşıdı. Dördünün ortak yanı şudur: hepsi kâğıt üzerinde bir dağıtımdır. Bir plan yazılıdır ama koşmaz; bir risk sırası bir listedir ama kimse ona bakmadan da değişiklik gönderebilir; bir oturum kapsamı ancak biri oturmaya karar verirse açılır; bir kabul ölçütü ancak biri onu okursa kapanır. Bu dağıtımın her değişiklikte, kimsenin hatırlamasına kalmadan, kendiliğinden işlemesi için bir düzenek gerekir — ve o düzenek hiç kurulmadı. Otomasyon Altyapısı konusu tam buradan başlar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.