Ders 03 / 24
Kullanıcı Hikâyeleri ve Görevler
İhtiyacın hikâyeye, hikâyenin göreve çevrilmesi; çözümün hikâyeye sızmasının denetlenmesi ve görev envanterinin sıklık, başarısızlık oranı ile kayıp deneme ölçütlerine göre sıralanması.
İçindekiler
Önceki ders üç kullanıcı profili üretti: bilinen kaydı arayan, konu tarayan ve dar ekrandan bakan. Profiller kimin geldiğini söylüyor, ne yaptığını söylemiyor. “Konu tarayan” profilinin oturumu yirmi bir buçuk dakika sürüyor; bu sürede hangi adımlar var, hangi adım hangi adımı gerektiriyor ve tasarımın hangisine öncelik vermesi gerekiyor?
Bu ders ihtiyacı eyleme çevirir. Önce ihtiyaç, hikâye ve görev ayrımını kurar; sonra bir hikâyenin içine çözümün nasıl sızdığını denetler; en sonda gözlem verisinden çıkan görev envanterini üç ayrı ölçütle sıralar ve bu sıralamaların birbirini tutmadığını gösterir.
İhtiyaç, Hikâye ve Görev
Üç kavram sık sık birbirinin yerine kullanılır ve karıştıkları yerde tasarım kararı bulanıklaşır.
İhtiyaç (need) arayüzden bağımsızdır. “Bir konuda ne yazılmış olduğunu bilmek” bir ihtiyaçtır; katalog arayüzü olmadan da vardır, kütüphaneci sorularak da karşılanabilir.
Kullanıcı hikâyesi (user story) ihtiyacı bir role ve bir amaca bağlar. Biçimi kısadır ve üç parça taşır: kim, ne, niçin. Hikâye tek başına bir çözüm önermez; çözüm alanını daraltır.
Görev (task) hikâyenin gözlenebilir karşılığıdır: başlangıcı, bitişi ve başarı ölçütü olan bir eylem dizisi. “Konu tara” bir görevdir; başlar, biter ve başarılı sayılıp sayılmadığı ölçülebilir. Görevler alt görevlere ayrıştırılır: konu taramanın içinde “sonucu daralt”, “kaydı aç”, “listeye dön” adımları vardır.
Zincir tek yönlüdür. İhtiyaçtan hikâyeye, hikâyeden göreve, görevden arayüz kararına inilir. Ters yönde yürünürse — önce bir arayüz ögesi düşünülüp sonra onu haklı çıkaracak hikâye yazılırsa — araştırma verisi karar üretmez, kararı süsler.
Hikâyeye Çözüm Sızması
Hikâyenin en sık bozulma biçimi, ihtiyacın yerine bir arayüz ögesinin yazılmasıdır. “Yayın yılı filtresi istiyorum” cümlesi ihtiyaç gibi görünür ama bir çözümdür; ihtiyaç “güncel kaynakları ayırt etmek”tir ve filtre bunun tek karşılığı değildir. Aşağıdaki denetim hikâyeleri biçim, rol ve sözcük düzeyinde tarar.
// hikaye.mjs — kullanici hikayelerinin bicim ve cozum sizintisi denetimi const HIKAYELER = [ "Lisans öğrencisi olarak arama sonuçlarında yayın yılı filtresi istiyorum, böylece güncel kaynakları ayırabilirim", "Konu tarayan biri olarak bir kaydı açtıktan sonra sonuç listeme dönebilmek istiyorum, böylece karşılaştırmayı sürdürebilirim", "Akademisyen olarak açılır menüden dil seçmek istiyorum", "Kütüphane çalışanı olarak bir kaydın rafta olup olmadığını görmek istiyorum, böylece okuyucuya doğru bilgi verebilirim", "Kullanıcı olarak daha iyi bir arama ekranı istiyorum, böylece daha rahat ararım", "Dışarıdan üye olarak ödünç süremi uzatmak istiyorum, böylece kitabı iade etmeden çalışmayı sürdürebilirim", ]; // Bicim: <rol> olarak <ihtiyac> istiyorum, boylece <amac> const BICIM = /^(.+?) olarak (.+?) istiyorum, böylece (.+)$/; // Cozum sozlugu: arayuz ogesi adlandiran sozcukler const COZUM = ["filtre", "düğme", "açılır menü", "sekme", "onay kutusu", "ekran", "sayfa", "form", "kaydırıcı"]; // Rol yerine gecmeyen genel adlar const GENEL_ROL = ["kullanıcı", "kişi", "herkes", "insan"]; console.log("no bicim rol bulgu"); HIKAYELER.forEach((h, i) => { const m = h.match(BICIM); const bulgular = []; if (!m) bulgular.push("bicim eksik (amac yazilmamis)"); const rol = m ? m[1] : (h.split(" olarak ")[0] ?? ""); if (GENEL_ROL.includes(rol.toLocaleLowerCase("tr"))) bulgular.push("rol genel"); const sizan = COZUM.filter((c) => h.toLocaleLowerCase("tr").includes(c)); if (sizan.length) bulgular.push(`cozum sizintisi: ${sizan.join(", ")}`); console.log( `${String(i + 1).padStart(2)} ${(m ? "tam" : "eksik").padEnd(5)} ${rol.slice(0, 14).padEnd(14)} ${bulgular.length ? bulgular.join(" | ") : "-"}` ); }); const temiz = HIKAYELER.filter((h) => { const m = h.match(BICIM); const rol = m ? m[1] : ""; return m && !GENEL_ROL.includes(rol.toLocaleLowerCase("tr")) && !COZUM.some((c) => h.toLocaleLowerCase("tr").includes(c)); }); console.log(`\ndenetimi gecen hikaye: ${temiz.length} / ${HIKAYELER.length}`);
no bicim rol bulgu 1 tam Lisans öğrenci cozum sizintisi: filtre 2 tam Konu tarayan b - 3 eksik Akademisyen bicim eksik (amac yazilmamis) | cozum sizintisi: açılır menü 4 tam Kütüphane çalı - 5 tam Kullanıcı rol genel | cozum sizintisi: ekran 6 tam Dışarıdan üye - denetimi gecen hikaye: 3 / 6
Altı hikâyenin üçü denetimden geçiyor. Denetim bir doğruluk ölçüsü değil, bir gözden geçirme aracıdır: sözcük listesine dayandığı için listede olmayan bir çözümü yakalamaz ve masum bir kullanımı yanlışlıkla işaretleyebilir. İşlevi, tartışmayı üslup düzeyinden yapı düzeyine taşımaktır.
Üç bulgunun her biri ayrı bir kusuru gösteriyor. Birinci hikâyede amaç doğru yazılmış ama ihtiyacın yerini çözüm almış; düzeltilmiş biçimi “yayınları güncellik sırasına göre görebilmek” olur ve filtre, sıralama, tarih aralığı gibi birden çok çözümü açık bırakır. Üçüncü hikâyede amaç hiç yok: dil seçiminin niçin gerektiği yazılmadığı için hikâye sınanamaz. Beşinci hikâyede rol “kullanıcı” — üç profilin hangisi olduğu belirsizdir ve “daha rahat” ölçülemez.
Görev Envanteri ve Öncelik
Hikâyeler görevlere çevrildikten sonra sıra hangisinin önce ele alınacağındadır. Gözlem döneminde her görev için deneme ve başarı sayıları kaydedildi.
// gorev.mjs — gorev envanterinin siklik, basarisizlik orani ve kayip deneme ile siralanmasi // Dort haftalik gozlem doneminde kaydedilen gorev denemeleri (ders icin kurulmus veri) const GOREVLER = [ { ad: "bilinen kaydi bul", deneme: 148, basari: 132 }, { ad: "raf konumunu bul", deneme: 121, basari: 79 }, { ad: "odunc al", deneme: 94, basari: 88 }, { ad: "konu tara", deneme: 86, basari: 51 }, { ad: "odunc suresini uzat", deneme: 37, basari: 19 }, { ad: "rezerve et", deneme: 22, basari: 12 }, { ad: "okuma listesine ekle", deneme: 14, basari: 13 }, ]; for (const g of GOREVLER) { g.kayip = g.deneme - g.basari; g.oran = g.kayip / g.deneme; } console.log("gorev deneme basari kayip basarisizlik orani"); for (const g of GOREVLER) { console.log( `${g.ad.padEnd(22)} ${String(g.deneme).padStart(6)} ${String(g.basari).padStart(7)} ${String(g.kayip).padStart(6)} ${(g.oran * 100).toFixed(1)}%` ); } const siraliAdlar = (anahtar) => [...GOREVLER].sort((a, b) => b[anahtar] - a[anahtar]).map((g) => g.ad); const SIRALAMALAR = { "siklik (deneme)": siraliAdlar("deneme"), "basarisizlik orani": siraliAdlar("oran"), "kayip deneme": siraliAdlar("kayip"), }; console.log("\nsira siklik basarisizlik orani kayip deneme"); for (let i = 0; i < GOREVLER.length; i++) { console.log( `${String(i + 1).padStart(4)} ${SIRALAMALAR["siklik (deneme)"][i].padEnd(23)} ` + `${SIRALAMALAR["basarisizlik orani"][i].padEnd(23)} ${SIRALAMALAR["kayip deneme"][i]}` ); } // Ayni gorevin uc siralamadaki basamagi console.log("\ngorev siklik oran kayip en buyuk basamak farki"); let enBuyuk = { ad: "", fark: -1 }; for (const g of GOREVLER) { const s = Object.values(SIRALAMALAR).map((l) => l.indexOf(g.ad) + 1); const fark = Math.max(...s) - Math.min(...s); if (fark > enBuyuk.fark) enBuyuk = { ad: g.ad, fark }; console.log(`${g.ad.padEnd(22)} ${String(s[0]).padStart(6)} ${String(s[1]).padStart(5)} ${String(s[2]).padStart(6)} ${fark}`); } console.log(`\nen cok yer degistiren gorev: ${enBuyuk.ad} (${enBuyuk.fark} basamak)`); console.log(`toplam kayip deneme: ${GOREVLER.reduce((t, g) => t + g.kayip, 0)}`); const ilkIki = [...GOREVLER].sort((a, b) => b.kayip - a.kayip).slice(0, 2); console.log( `kayip denemelerin ${((ilkIki.reduce((t, g) => t + g.kayip, 0) / GOREVLER.reduce((t, g) => t + g.kayip, 0)) * 100).toFixed(1)}%'i iki gorevde: ` + ilkIki.map((g) => g.ad).join(", ") );
gorev deneme basari kayip basarisizlik orani bilinen kaydi bul 148 132 16 10.8% raf konumunu bul 121 79 42 34.7% odunc al 94 88 6 6.4% konu tara 86 51 35 40.7% odunc suresini uzat 37 19 18 48.6% rezerve et 22 12 10 45.5% okuma listesine ekle 14 13 1 7.1% sira siklik basarisizlik orani kayip deneme 1 bilinen kaydi bul odunc suresini uzat raf konumunu bul 2 raf konumunu bul rezerve et konu tara 3 odunc al konu tara odunc suresini uzat 4 konu tara raf konumunu bul bilinen kaydi bul 5 odunc suresini uzat bilinen kaydi bul rezerve et 6 rezerve et okuma listesine ekle odunc al 7 okuma listesine ekle odunc al okuma listesine ekle gorev siklik oran kayip en buyuk basamak farki bilinen kaydi bul 1 5 4 4 raf konumunu bul 2 4 1 3 odunc al 3 7 6 4 konu tara 4 3 2 2 odunc suresini uzat 5 1 3 4 rezerve et 6 2 5 4 okuma listesine ekle 7 6 7 1 en cok yer degistiren gorev: bilinen kaydi bul (4 basamak) toplam kayip deneme: 128 kayip denemelerin 60.2%'i iki gorevde: raf konumunu bul, konu tara
Üç sıralama üç ayrı listeye çıkıyor ve hiçbir görev üçünde de aynı basamakta değil.
Sıklığa göre birinci sırada “bilinen kaydı bul” var; en çok denenen görev budur. Ama başarısızlık oranı %10.8’dir ve yüz kırk sekiz denemenin yalnızca on altısı kayıptır. Sıklık tek başına tasarım önceliği vermez; çok yapılan ama iyi çalışan bir görev iyileştirme alanı değildir.
Başarısızlık oranına göre birinci sırada “ödünç süresini uzat” var: %48.6. Bu oran sorunun ciddiyetini doğru anlatır ama büyüklüğünü anlatmaz; görev otuz yedi kez denendi ve on sekiz kayıp üretti.
Kayıp denemeye göre birinci sırada “raf konumunu bul” var: kırk iki kayıp. Bu görev sıklıkta ikinci, başarısızlık oranında dördüncü — iki listede de birinci değil, ama toplam zararın en büyük payını üretiyor. Toplam yüz yirmi sekiz kayıp denemenin %60.2’si iki görevde toplanıyor.
Öncelik sıralaması, üçünden biri seçilerek değil, sorunun ne olduğu tanımlanarak kurulur. Amaç toplam zararı azaltmaksa kayıp deneme sıralaması kullanılır. Amaç bir görevin güvenilirliğini eşiğin üstüne çıkarmaksa başarısızlık oranı kullanılır. Sıklık ise tek başına bir öncelik ölçütü değil, diğer ikisinin çarpanıdır: az denenen bir görevdeki yüksek oran, çok denenen bir görevdeki düşük orandan daha az kayıp üretebilir.
Bir uyarı gerekir: bu sayılar yalnızca denenmiş görevleri sayar. Kullanıcının yapmak isteyip yolunu bulamadığı ve hiç denemediği görev envantere girmez. “Okuma listesine ekle” görevinin on dört denemesi, bu görevin ender olduğunu değil, ender bulunduğunu da gösteriyor olabilir. Envanterin körlüğü budur ve ancak görüşmeyle kapatılır.
Görev Verisinin Etik Sınırı
Görev envanteri kişi başına oturum kaydından üretilir; bu, araştırma verisinin en tanımlayıcı biçimidir. Üç kural burada da geçerlidir. Oturum kaydı onay alınmış katılımcılardan toplanır; genel kullanıma açık bir arayüzde herkesin kaydını tutmak araştırma değil izlemedir. Çözümlemeye giren veri toplulaştırılmış hâldir: yukarıdaki tabloda kişi yok, görev var. Kişi başına kayıt, toplulaştırma yapıldıktan sonra saklama sınırı içinde silinir; yalnızca sayılar kalır.
Buna bağlı bir tasarım kuralı da vardır: envanteri zenginleştirmek için toplanan her yeni alan, kimliğe geri gitmenin bir yolunu daha açar. Görevin başarılı olup olmadığını bilmek için kimin denediğini bilmek gerekmez.
Kabul Ölçütü Hikâyeyi Sınanabilir Kılar
Denetimden geçen bir hikâye bile tek başına yeterli değildir; ne zaman karşılandığı yazılı olmalıdır. Kabul ölçütü (acceptance criteria), hikâyenin doğrulanabilir karşılığıdır ve görev üzerinden yazılır.
İkinci hikâye — “bir kaydı açtıktan sonra sonuç listeme dönebilmek” — şu ölçütlere bağlanır: geri dönüşte arama terimi, uygulanan daraltmalar ve listedeki konum korunur; kullanıcının daha önce açtığı kayıt listede işaretli kalır; dönüş, aramanın yeniden çalıştırılmasını gerektirmez. Üçü de gözlenebilir; üçü de sınanabilir.
Ölçüt yazılırken çözüm yine dışarıda tutulur. “Geri düğmesi eklenir” bir ölçüt değil bir karardır; ölçüt, hangi düğmeyle olursa olsun sonucun ne olması gerektiğini söyler.
Özet
- İhtiyaç arayüzden bağımsızdır, hikâye ihtiyacı role ve amaca bağlar, görev hikâyenin başlangıcı, bitişi ve başarı ölçütü olan gözlenebilir karşılığıdır.
- Hikâyeye çözüm sızması en sık bozulmadır; sözcük ve biçim denetimi bir doğruluk ölçüsü değil gözden geçirme aracıdır ve örnek altı hikâyenin üçünü işaretledi.
- Aynı görev envanteri sıklık, başarısızlık oranı ve kayıp deneme ölçütlerine göre üç ayrı sıralama üretir; örnek veride bir görev üç sıralama arasında dört basamak yer değiştirdi.
- Öncelik ölçütü sorunun tanımından türetilir: toplam zarar için kayıp deneme, güvenilirlik için başarısızlık oranı kullanılır; sıklık tek başına öncelik vermez.
- Envanter yalnızca denenmiş görevleri sayar; hiç denenmemiş görev görünmez ve bu körlük ancak görüşmeyle kapatılır.
- Oturum kaydı onaylı katılımcılardan toplanır, çözümlemeye toplulaştırılmış girer ve kişi başına kayıt saklama sınırı içinde silinir.
Sonraki Adım
Görev envanteri her görevi ayrı bir satır olarak gösteriyor, oysa kullanıcı bunları arka arkaya yapıyor: bir kaydı buluyor, raf konumuna bakıyor, rafa gidiyor, bulamıyor ve kataloğa geri dönüyor. Kayıp denemelerin en büyük payını üreten “raf konumunu bul” görevi, ekranda başlayıp ekranın dışında biten bir adımdır. Sonraki ders bu zinciri uçtan uca çıkarır: yolculuk haritasının hangi sütunlardan oluştuğunu, aşamaların süresiyle bırakma noktalarının niçin aynı yerde olmadığını ve ekran dışındaki adımların haritaya nasıl girdiğini gösterir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.