Ders 02 / 12
Doğrulama ve Geçerleme
Ürünün belirtime uygunluğunu sınayan doğrulama ile belirtimin gerçek ihtiyaca uygunluğunu sınayan geçerlemenin ayrılması ve her denetimi geçtiği hâlde yanlış ücret hesaplayan bir örnek.
İçindekiler
Önceki ders denetimleri belirtimden türetti: yazılı kural neyse, denetim onu sordu. Denetimlerin tamamı geçtiğinde varılan sonuç şudur — kitaplık, yazılı kurala uyuyor. Bu sonuç sanıldığı kadar geniş değildir. Yazılı kuralın kendisi kütüphanenin gerçekten uyguladığı kural olmayabilir. O durumda program kusursuz biçimde yanlış bir şey yapar.
Bu ders o iki soruyu ayırır. Birincisi ürünün belirtime uygunluğunu, ikincisi belirtimin ihtiyaca uygunluğunu sorar. Aynı kitaplığa yeni bir kural ekleyerek ikisinin bağımsız olduğu gösterilecek: gecikme ücreti hesabı.
İki Ayrı Soru
Doğrulama (verification) şunu sorar: ürünü doğru mu inşa ediyoruz? Ölçüt üründen bir adım geridedir — belirtim, tasarım kararı, kabul ölçütü. Yanıtı üretmek için ürünle o yazılı ölçüt karşılaştırılır.
Geçerleme (validation) şunu sorar: doğru şeyi mi inşa ediyoruz? Ölçüt belirtimin kendisinden bir adım geridedir — kullanıcının işi, kurumun kuralı, hizmetin amacı. Yanıtı üretmek için belirtim gerçek kullanımla karşılaştırılır.
İki soru arasındaki fark ölçütün nereden geldiğidir. Doğrulama ölçütü belgedir; geçerleme ölçütü belgenin dışındaki dünyadır. Bu yüzden yalnız doğrulama yapan bir çalışma kendi içine kapalıdır: belge ne diyorsa onu onaylar, belgenin yanlış olma ihtimalini hiç gündeme getirmez.
Belirtime Uyan Yanlış Program
Kütüphaneye gecikme ücreti kuralı eklenir. Çözümleme sırasında yazılan madde şudur:
- K7 — Gecikme ücreti gün başına 2 birimdir.
- K8 — Negatif gecikme reddedilir.
Uygulaması maddeyi birebir izler.
// ucret.mjs — belirtimdeki K7 kuralinin birebir uygulanmasi export const GUNLUK_UCRET = 2; export function gecikmeUcreti(gecikmeGun) { if (gecikmeGun < 0) throw new Error('gecikme gun sayisi negatif olamaz'); return gecikmeGun * GUNLUK_UCRET; }
Denetimler de aynı maddeden türetilir. Önceki dersteki düzenin aynısı kullanılır: her denetim bir kural numarasına bağlıdır.
// dogrulama.mjs — yazili belirtimden turetilmis denetimler import assert from 'node:assert/strict'; import { gecikmeUcreti } from './ucret.mjs'; const DENETIMLER = { 'K7 gun basina 2 birim': () => assert.equal(gecikmeUcreti(5), 10), 'K7 gecikme yoksa ucret yok': () => assert.equal(gecikmeUcreti(0), 0), 'K7 ucret gecikmeyle dogru orantili': () => { assert.equal(gecikmeUcreti(10), 2 * gecikmeUcreti(5)); }, 'K8 negatif gecikme reddedilir': () => assert.throws(() => gecikmeUcreti(-1)), }; let gecen = 0; for (const [ad, denetim] of Object.entries(DENETIMLER)) { try { denetim(); gecen += 1; } catch (hata) { console.log(`dustu: ${ad} — ${hata.message.split('\n')[0]}`); } } console.log(`dogrulama: ${gecen}/${Object.keys(DENETIMLER).length} denetim gecti`);
dogrulama: 4/4 denetim gecti
Doğrulama tamdır. Kural iki maddeden ibaretti, iki madde de denetlendi, ikisi de geçti. İzlenebilirlik ölçümü de kusursuz çıkardı: yazılı her kuralın bir denetimi var. Bu noktada elde, belirtimine tam uyduğu kanıtlanmış bir program vardır.
Gerçeğe Karşı Sınamak
Kütüphanenin gişesinde yıllardır ücret tahsil ediliyordu. Geçerlemenin ölçütü budur: belge değil, gerçekten yapılmış işlemler. Program o işlemleri yeniden hesaplar ve tahsil edilmiş tutarla karşılaştırır.
// gecerleme.mjs — gisede gercekten tahsil edilmis ucretlerle karsilastirma import { gecikmeUcreti } from './ucret.mjs'; const GISE_KAYITLARI = [ { islem: 'A-101', gecikmeGun: 0, tahsil: 0 }, { islem: 'A-102', gecikmeGun: 2, tahsil: 0 }, { islem: 'A-103', gecikmeGun: 3, tahsil: 0 }, { islem: 'A-104', gecikmeGun: 4, tahsil: 2 }, { islem: 'A-105', gecikmeGun: 10, tahsil: 14 }, { islem: 'A-106', gecikmeGun: 40, tahsil: 20 }, ]; let uyusan = 0; for (const k of GISE_KAYITLARI) { const hesap = gecikmeUcreti(k.gecikmeGun); if (hesap === k.tahsil) uyusan += 1; else console.log(`${k.islem}: ${k.gecikmeGun} gun — program ${hesap}, gise ${k.tahsil}`); } console.log(`gecerleme: ${uyusan}/${GISE_KAYITLARI.length} kayit uyustu`);
A-102: 2 gun — program 4, gise 0 A-103: 3 gun — program 6, gise 0 A-104: 4 gun — program 8, gise 2 A-105: 10 gun — program 20, gise 14 A-106: 40 gun — program 80, gise 20 gecerleme: 1/6 kayit uyustu
Altı kayıttan biri uyuşuyor. Uyuşan kayıt gecikmesi olmayan işlemdir; ücret hesabının hiç çalışmadığı durumdur. Uyuşmayan beş kaydın örüntüsü iki eksik kuralı ele verir: ilk üç gün ücretsizdir ve ücret bir üst sınırda durur. Bu iki kural gişede uygulanıyordu ama belirtime girmemişti.
Kusur kodda değildir. Kod, kendisinden istenen şeyi doğru yapmıştır. Kusur çözümleme aşamasındadır ve doğrulamanın hiçbir miktarı onu bulamaz — çünkü doğrulama, ölçütü olarak tam da kusurlu belgeyi kullanır.
Düzeltilen Belirtim
Geçerlemenin çıktısı bir kod düzeltmesi değil, bir belirtim düzeltmesidir. K7 üç maddeye ayrılır: ücretsiz gün payı (grace period) üç gündür, sonraki her gün 2 birimdir, ücret 20 birimi aşmaz.
// ucret2.mjs — gecerlemeden sonra duzeltilen belirtimin uygulanmasi export const UCRETSIZ_GUN = 3; export const GUNLUK_UCRET = 2; export const UST_SINIR = 20; export function gecikmeUcreti(gecikmeGun) { if (gecikmeGun < 0) throw new Error('gecikme gun sayisi negatif olamaz'); const ucretli = Math.max(gecikmeGun - UCRETSIZ_GUN, 0); return Math.min(ucretli * GUNLUK_UCRET, UST_SINIR); }
Artık iki sınav da aynı anda uygulanabilir. Doğrulama yeni maddelerden türetilir, geçerleme aynı gişe kayıtlarını kullanır.
// iki-sinav.mjs — duzeltilmis belirtim hem dogrulanir hem gecerlenir import assert from 'node:assert/strict'; import { gecikmeUcreti } from './ucret2.mjs'; const DENETIMLER = { 'K7a ilk 3 gun ucretsiz': () => assert.equal(gecikmeUcreti(3), 0), 'K7b sonraki her gun 2 birim': () => assert.equal(gecikmeUcreti(6), 6), 'K7c ucret 20 birimi asmaz': () => assert.equal(gecikmeUcreti(100), 20), 'K8 negatif gecikme reddedilir': () => assert.throws(() => gecikmeUcreti(-1)), }; let gecen = 0; for (const [ad, denetim] of Object.entries(DENETIMLER)) { try { denetim(); gecen += 1; } catch { console.log(`dustu: ${ad}`); } } const GISE_KAYITLARI = [ { gecikmeGun: 0, tahsil: 0 }, { gecikmeGun: 2, tahsil: 0 }, { gecikmeGun: 3, tahsil: 0 }, { gecikmeGun: 4, tahsil: 2 }, { gecikmeGun: 10, tahsil: 14 }, { gecikmeGun: 40, tahsil: 20 }, ]; const uyusan = GISE_KAYITLARI.filter((k) => gecikmeUcreti(k.gecikmeGun) === k.tahsil).length; console.log(`dogrulama : ${gecen}/${Object.keys(DENETIMLER).length} denetim gecti`); console.log(`gecerleme : ${uyusan}/${GISE_KAYITLARI.length} kayit uyustu`);
dogrulama : 4/4 denetim gecti gecerleme : 6/6 kayit uyustu
İlk sürümde birinci satır da doluydu. Ayıran şey ikinci satırdır. Bir kalite iddiası kurulurken hangi satırın gösterildiği sorulmalıdır.
Her Aşamanın Kendi Sınavı
Doğrulama tek bir etkinlik değildir; her ara ürünün kendi doğrulaması vardır. Tasarım belirtime karşı, kod tasarıma karşı, yapılandırma koda karşı doğrulanır. Bu eşleşme geliştirme aşamalarıyla sınama aşamalarını karşılıklı dizen V modeli (V-model) olarak bilinir: soldaki her ayrıştırma adımının sağda bir birleştirme ve sınama adımı vardır. Model bir süreç önerisi olarak tartışmalıdır, ama kurduğu eşleştirme kalıcıdır — her karar düzeyinin kendi ölçütü ve kendi sınavı olur.
Doğrulamanın yöntemleri programı çalıştırmayı gerektirmez. Gözden geçirme (review), statik çözümleme (static analysis) ve tür denetimi de üründe belirtimden sapma arar. Test bunların programı çalıştıran türüdür.
Geçerlemenin yöntemleri ise ürünü kullanacak kişiyi ya da onun işini gerektirir: kabul testi, gerçek işlem kayıtlarıyla karşılaştırma, kullanıcı denemesi, örnekle belirtim (specification by example) — kuralın soyut cümlesi yerine üzerinde anlaşılmış somut örneklerin yazılması. Yukarıdaki gişe kayıtları bu son yöntemin ham hâlidir.
Geçerlemenin geç kalmasının bedeli doğrulamanınkinden büyüktür. Yanlış kod düzeltilir; yanlış belirtim üzerine kurulmuş tasarım, veri şeması, arayüz ve eğitim malzemesinin tamamı yeniden yapılır. Ücret hesabı örneğinde tek bir işlev değişti, ama tahsil edilmiş tutarlar yanlış olsaydı geriye dönük düzeltme de gerekirdi. Bu yüzden geçerleme sorusu belirtim yazılırken sorulur — birkaç somut örnek üzerinde anlaşmak, sonradan yapılacak düzeltmenin küçük bir kesridir.
Özet
- Doğrulama ürünün belirtime, geçerleme belirtimin gerçek ihtiyaca uygunluğunu sorar; ikisinin ölçütü ayrı yerlerden gelir.
- Yalnız doğrulama yapan bir çalışma kendi içine kapalıdır: belgeyi ölçüt aldığı için belgenin yanlış olma ihtimalini gündeme getirmez.
- Örnekte dört denetimin dördü geçti, ama altı gişe kaydından yalnız biri uyuştu; kusur kodda değil çözümleme aşamasındaydı.
- Geçerlemenin çıktısı çoğu zaman bir kod düzeltmesi değil, bir belirtim düzeltmesidir.
- Doğrulama gözden geçirme ve statik çözümlemeyle de yapılabilir; geçerleme ürünü kullanacak kişinin işini gerektirir.
- Yanlış belirtimin bedeli yanlış kodunkinden büyüktür, çünkü üzerine kurulan her şeyi birlikte götürür.
Sonraki Adım
Bu derste ücret hesabının doğru sonucu vermesi tartışıldı. Doğru sonuç, beklentilerin yalnız bir bölümüdür. Kitaplık doğru ücreti hesaplasa bile yanıt vermesi bir dakika sürüyorsa, on bin kayıtta belleğe sığmıyorsa ya da başka bir üyenin borcunu gösteriyorsa kullanılamaz. Bu beklentiler belirtimde çoğu zaman yazılı değildir ve yazılı olduklarında da “hızlı olsun” gibi ölçülemez cümlelerle geçer. Sonraki ders bu beklentileri adlandırır ve ölçülebilir ölçütlere çevirir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.