İçeriğe geç
academia.sh

Ders 06 / 11

Sürekli Tümleştirmede Test

Dört kursun bıraktığı sınamaların bir hatta yerleştirilmesi: aşama ve kapı düzeninin geri bildirim süresi ile kaçan hata arasında kurduğu takas, erken kesmenin koşum bütçesine etkisi, gecelik ve sürüm kapılarının bekleme bedeli ve karantinanın kapı kararına bağlanması.

İçindekiler

Önceki konu bir dağıtım bıraktı: kapsam yazıldı, risk sırası kuruldu, keşifsel oturumun kapsamı belirlendi, kabul ölçütü tamamlanma tanımına bağlandı. Hepsinin ortak yanı şudur — bu dağıtım kâğıt üzerindedir. Bir plan, her değişiklikte kendiliğinden işleyen bir düzeneğe bağlanmadığı sürece yalnızca bir niyettir; kimin ne zaman hangi testi koşacağı insanın hatırlamasına kalır.

Bu ders o düzeneği kurar. Değişikliği alıp sırayla sınamalardan geçiren zincire hat (pipeline) denir; hattın her parçası bir aşama (stage), aşamanın sonucunun bir kararı zorladığı nokta ise bir kapıdır (gate). Kabuk Programlama kursundaki boru hattı bir sürecin çıktısını diğerinin girdisine bağlar; buradaki hat ise bir değişikliğin geçmesi gereken sıralı sınama kümesidir, veri değil karar taşır. Sorulan şey tek bir sayıyla yazılabilir: hangi sınama hangi aşamada koşarsa geri bildirim süresi ile kaçan hata (escaped defect, önceki kursların kaçan gerçek hata kavramıyla aynı şey) arasındaki takas nereye düşer.

Sınama Envanteri

Dört kurs otuz ders boyunca sınama yazdı ve her dersin kapanış tablosu o sınamanın koşumdan bağımsız bedelini bıraktı. O sayılar burada dokuz sınama takımına toplanır. Süreç sayısı, ağ isteği, veri erişimi ve elle inceleme adımı doğrudan kapanış tablolarından okunmuştur: tümleşim takımının 816 erişimi Tümleşim Testinin Kapsamı (4 sorgu), Test Ortamı Yönetimi (6 adım), Test Verisi Yönetimi (772 sorgu), Veritabanı Testleri (21 sorgu, 13 göç adımı) ve Dış Servis Taklidi (0) satırlarının toplamıdır; güvenlik takımının 20 elle adımı beş güvenlik dersinin elle inceleme sayılarının toplamıdır.

TS6 — koşum bedeli model dakikasıdır: bir süreç ayağa kaldırma 1 dakika, bin ağ isteği 1 dakika, bin veri erişimi 1 dakika, bir elle inceleme adımı 15 dakika; taban 1 dakika. Elle adımın ağırlığı diğerlerinin on beş katıdır, çünkü elle iş bir kapıya konulamaz — sırasını bekler.

TS7 — yüz değişiklikte kusur sınıfı başına ortaya çıkma sayısı. Alt katmanların gördüğü sınıflar daha sık yazılır, çünkü her değişiklik koda dokunur; kapasite ve kurtarma sınıfları seyrektir. İki sınıfın yakalayan listesi boştur: bunlar kaynak derslerin “yakalamadığı” sütunundan gelir ve hiçbir takımın göremediği kusurlardır.

// envanter.mjs — dort kursta yazilan sinamalarin takim listesi, bedeli ve kusur kumesi
const yukari = (n) => Math.ceil(n / 1000);

// TS6: surec 1 dk, bin istek 1 dk, bin erisim 1 dk, elle adim 15 dk; taban 1 dk.
export const bedel = (t) => Math.max(1,
  t.surec + yukari(t.istek) + yukari(t.erisim) + t.elle * 15);

// surec / istek / erisim / elle sayilari M21/K03 ve K04 kapanis tablolarindan okundu.
export const takimlar = [
  { ad: 'birim', surec: 0, istek: 0, erisim: 9, elle: 0 },
  { ad: 'sozlesme', surec: 6, istek: 126, erisim: 0, elle: 0 },
  { ad: 'tumlesim', surec: 1, istek: 0, erisim: 816, elle: 0 },
  { ad: 'uctan-uca', surec: 12, istek: 24, erisim: 25511, elle: 0 },
  { ad: 'basarim-kosum', surec: 44, istek: 78939, erisim: 0, elle: 0 },
  { ad: 'basarim-ornekleme', surec: 0, istek: 0, erisim: 536000, elle: 0 },
  { ad: 'guvenlik-otomatik', surec: 9, istek: 8, erisim: 183, elle: 0 },
  { ad: 'guvenlik-elle', surec: 0, istek: 0, erisim: 0, elle: 20 },
  { ad: 'dayaniklilik', surec: 0, istek: 639, erisim: 20, elle: 0 },
];

// TS7: yuz degisiklikte kusur sinifi basina ortaya cikma sayisi.
export const kusurlar = [
  { sinif: 'sinir karsilastirmasi', yakalayan: ['birim'], sayi: 13 },
  { sinif: 'sema uyusmazligi', yakalayan: ['tumlesim'], sayi: 8 },
  { sinif: 'kart durumunun guncellenmemesi', yakalayan: ['uctan-uca'], sayi: 7 },
  { sinif: 'durum kodu eslemesi', yakalayan: ['uctan-uca'], sayi: 6 },
  { sinif: 'kirici degisiklik', yakalayan: ['birim', 'sozlesme'], sayi: 6 },
  { sinif: 'istek bicimi kaymasi', yakalayan: ['sozlesme'], sayi: 5 },
  { sinif: 'erisilebilirlik olcutu', yakalayan: ['guvenlik-elle'], sayi: 5 },
  { sinif: 'sozlesmedeki alanin kaybolmasi', yakalayan: ['sozlesme'], sayi: 4 },
  { sinif: 'reddedilen istekte uyari yoklugu', yakalayan: ['uctan-uca'], sayi: 4, karantina: true },
  { sinif: 'gecikme gerilemesi', yakalayan: ['basarim-kosum'], sayi: 4 },
  { sinif: 'gocte veri kaybi', yakalayan: ['tumlesim'], sayi: 3 },
  { sinif: 'birlestirilmis sorgu', yakalayan: ['guvenlik-otomatik'], sayi: 3 },
  { sinif: 'anlamsal uyusmazlik', yakalayan: [], sayi: 3 },
  { sinif: 'kontrol-sonra-yaz yarisi', yakalayan: ['uctan-uca'], sayi: 2 },
  { sinif: 'dagilima bagli rapor kusuru', yakalayan: ['tumlesim'], sayi: 2 },
  { sinif: 'surec omrune bagli durum', yakalayan: [], sayi: 2 },
  { sinif: 'az ornekle okunan yuzdelik', yakalayan: ['basarim-ornekleme'], sayi: 1 },
  { sinif: 'kurtarma eksigi', yakalayan: ['dayaniklilik'], sayi: 1 },
  { sinif: 'kapasite siniri', yakalayan: ['dayaniklilik'], sayi: 1 },
];
// bedel.mjs — takim basina kosum bedeli ve kusur kumesinin toplami
import { takimlar, kusurlar, bedel } from './envanter.mjs';

const s = (x, n) => String(x).padStart(n);
console.log(`${'takim'.padEnd(20)}${s('surec', 6)}${s('istek', 8)}${s('erisim', 8)}${s('elle', 6)}${s('dakika', 8)}`);
for (const t of takimlar) {
  console.log(`${t.ad.padEnd(20)}${s(t.surec, 6)}${s(t.istek, 8)}${s(t.erisim, 8)}${s(t.elle, 6)}${s(bedel(t), 8)}`);
}
const toplam = takimlar.reduce((a, t) => a + bedel(t), 0);
const kusurToplam = kusurlar.reduce((a, k) => a + k.sayi, 0);
const gorulmeyen = kusurlar.filter((k) => k.yakalayan.length === 0);
console.log(`\ntakim ${takimlar.length}, tam takim ${toplam} dakika`);
console.log(`kusur sinifi ${kusurlar.length}, yuz degisiklikte ${kusurToplam} kusur, `
  + `${100 - kusurToplam} temiz degisiklik`);
console.log(`hicbir takimin gormedigi sinif ${gorulmeyen.length}, `
  + `${gorulmeyen.reduce((a, k) => a + k.sayi, 0)} kusur`);
takim                surec   istek  erisim  elle  dakika
birim                    0       0       9     0       1
sozlesme                 6     126       0     0       7
tumlesim                 1       0     816     0       2
uctan-uca               12      24   25511     0      39
basarim-kosum           44   78939       0     0     123
basarim-ornekleme        0       0  536000     0     536
guvenlik-otomatik        9       8     183     0      11
guvenlik-elle            0       0       0    20     300
dayaniklilik             0     639      20     0       2

takim 9, tam takim 1021 dakika
kusur sinifi 19, yuz degisiklikte 80 kusur, 20 temiz degisiklik
hicbir takimin gormedigi sinif 2, 5 kusur

Tam takım 1021 dakikadır, yani on yedi saat. Bu sayı kararı zaten vermiştir: her değişiklikte tam takımı koşturmak bir seçenek değildir. Piramidin aritmetiği Test Piramidi dersinde yapıldı; burada piramit zamana çevrilir — birim takımı 1 dakika, uçtan uca takımı 39, başarım takımının örnekleme çalışması tek başına 536 dakikadır.

Kapı Düzenleri

Bir düzen, aşamaların sırasını ve her aşamanın sıklığını belirler: her değişiklikte, gecelik ya da sürüm başına. Sıklık bir bekleme üretir.

TS8 — bekleme süreleri: gecelik kapı için 240 dakika, sürüm kapısı için 1200 dakika. Değişiklikler iş gününe düzgün yayılır; gecelik koşum gün sonunda bir kez çalışır, yani ortalama bekleme yarım iş günüdür. Sürüm haftada birdir, ortalama bekleme iki buçuk iş günüdür.

TS9 — yüz değişiklik yirmi iş gününe yayılır: günde beş değişiklik, dört sürüm. Bu varsayım koşum sayısını belirler ve makine bedelini doğrudan etkiler.

TS10 — bir koşucu günde 480 dakika iş yapar. Gereken koşucu sayısı buradan çıkar ve düzenin donanım bedelidir.

Erken kesme (fail fast), bir aşama kırmızıya döndüğünde sonraki aşamaların hiç koşmamasıdır. Aşağıdaki ölçüm bunu bir seçenek olarak açıp kapatır.

// kapi.mjs — ayni kusur kumesi uzerinde yedi kapi duzeninin karsilastirilmasi
import { takimlar, kusurlar, bedel } from './envanter.mjs';

const dk = Object.fromEntries(takimlar.map((t) => [t.ad, bedel(t)]));
const BEKLEME = { degisiklik: 0, gecelik: 240, surum: 1200 };   // TS8
const KOSUM = { degisiklik: 100, gecelik: 20, surum: 4 };       // TS9

const A = ['birim', 'sozlesme'];
const B = ['tumlesim', 'uctan-uca'];
const C = ['basarim-kosum', 'guvenlik-otomatik', 'dayaniklilik'];
const D = ['basarim-ornekleme', 'guvenlik-elle'];

const duzenler = [
  { ad: 'tek kapi', kes: true, asamalar: [['hepsi', 'degisiklik', [...A, ...B, ...C, ...D]]] },
  { ad: 'uc asama', kes: true, asamalar: [['hizli', 'degisiklik', A], ['orta', 'degisiklik', B], ['agir', 'degisiklik', [...C, ...D]]] },
  { ad: 'uc asama, kesmesiz', kes: false, asamalar: [['hizli', 'degisiklik', A], ['orta', 'degisiklik', B], ['agir', 'degisiklik', [...C, ...D]]] },
  { ad: 'agir asama gecelik', kes: true, asamalar: [['hizli', 'degisiklik', A], ['orta', 'degisiklik', B], ['agir', 'gecelik', [...C, ...D]]] },
  { ad: 'uc siklik', kes: true, asamalar: [['hizli', 'degisiklik', A], ['orta', 'degisiklik', B], ['agir', 'gecelik', C], ['surum', 'surum', D]] },
  { ad: 'elle is hat disi', kes: true, asamalar: [['hizli', 'degisiklik', A], ['orta', 'degisiklik', B], ['agir', 'gecelik', C], ['surum', 'surum', ['basarim-ornekleme']]] },
  { ad: 'uc siklik, karantina', kes: true, karantina: true, asamalar: [['hizli', 'degisiklik', A], ['orta', 'degisiklik', B], ['agir', 'gecelik', C], ['surum', 'surum', D]] },
];

const olc = (d) => {
  const asama = d.asamalar.map(([ad, siklik, tk]) => ({ ad, siklik, tk, sure: tk.reduce((a, x) => a + dk[x], 0) }));
  const oncekiSure = (i) => {
    const s = asama[i].siklik;
    const dahaSik = asama.filter((x) => KOSUM[x.siklik] > KOSUM[s]).reduce((a, x) => a + x.sure, 0);
    const ayni = asama.slice(0, i + 1).filter((x) => x.siklik === s).reduce((a, x) => a + x.sure, 0);
    return dahaSik + BEKLEME[s] + ayni;
  };
  let kacan = 0, agirlik = 0, toplamGeri = 0, enKotu = 0, kesilen = 0;
  for (const k of kusurlar) {
    const gorur = (t) => k.yakalayan.includes(t) && (d.karantina !== true || k.karantina !== true);
    const i = asama.findIndex((x) => x.tk.some(gorur));
    if (i === -1) { kacan += k.sayi; continue; }
    const geri = oncekiSure(i);
    agirlik += k.sayi;
    toplamGeri += geri * k.sayi;
    enKotu = Math.max(enKotu, geri);
    // erken kesme: kirmizi asamadan sonraki degisiklik asamalari kosmaz
    if (d.kes && asama[i].siklik === 'degisiklik') {
      kesilen += k.sayi * asama.slice(i + 1).filter((x) => x.siklik === 'degisiklik').reduce((a, x) => a + x.sure, 0);
    }
  }
  const makine = (asama.reduce((a, x) => a + x.sure * KOSUM[x.siklik], 0) - kesilen) / 100;
  return { makine, kosucu: Math.ceil((makine * 5) / 480), geri: toplamGeri / agirlik, enKotu, kacan };
};

const s = (x, n) => String(x).padStart(n);
console.log(`${'duzen'.padEnd(22)}${s('dk/degisiklik', 14)}${s('kosucu', 8)}${s('ort geri', 10)}${s('en kotu', 9)}${s('kacan/100', 11)}`);
for (const d of duzenler) {
  const r = olc(d);
  console.log(`${d.ad.padEnd(22)}${s(r.makine.toFixed(1), 14)}${s(r.kosucu, 8)}`
    + `${s(r.geri.toFixed(1), 10)}${s(r.enKotu, 9)}${s(r.kacan, 11)}`);
}
const sure = (g) => g.reduce((a, x) => a + dk[x], 0);
console.log(`\nasama sureleri: hizli ${sure(A)}, orta ${sure(B)}, gecelik ${sure(C)}, surum ${sure(D)} dakika`);
console.log(`gece penceresi 480 dakika; agir asamanin tamami ${sure([...C, ...D])} dakika, `
  + `${sure([...C, ...D]) <= 480 ? 'sigar' : 'sigmaz'}`);
duzen                  dk/degisiklik  kosucu  ort geri  en kotu  kacan/100
tek kapi                      1021.0      11    1021.0     1021          5
uc asama                       426.3       5     228.1     1021          5
uc asama, kesmesiz            1021.0      11     228.1     1021          5
agir asama gecelik             231.9       3     276.1     1261          5
uc siklik                       98.2       2     252.6     2221          5
elle is hat disi                86.2       1     107.7     1921         10
uc siklik, karantina            98.2       2     264.0     2221          9

asama sureleri: hizli 8, orta 41, gecelik 136, surum 836 dakika
gece penceresi 480 dakika; agir asamanin tamami 972 dakika, sigmaz

Tablonun Okunması

İlk iki satır aşamalara bölmenin ne kazandırdığını verir. Tek kapı düzeninde her kusur 1021 dakika sonra görünür, çünkü sonuç ancak takımın tamamı bitince çıkar. Aynı takım üç aşamaya bölündüğünde ortalama geri bildirim 228,1 dakikaya iner — kaçan hata değişmez, kaçırılan kusur kümesi aynıdır. Bölme tek başına bedava bir kazançtır ve kaybı yoktur.

Üçüncü satır erken kesmenin payını ayırır. Aynı düzen, kesme kapalıyken değişiklik başına 1021 dakika ve on bir koşucu ister; kesme açıkken 426,3 dakika ve beş koşucu. Geri bildirim süresi iki satırda aynıdır: erken kesme kimseye daha erken haber vermez, yalnız kırmızı bir değişikliğin ardından koşacak 1013 dakikalık işi iptal eder. Kazanç yüzde elli sekizdir ve doğrudan kusur sıklığından gelir — kusursuz bir depoda erken kesme hiçbir şey kazandırmaz.

Dördüncü ve beşinci satırlar sıklığın bedelini gösterir. Ağır aşama gecelik kapıya taşındığında değişiklik başına 231,9 dakika kalır, ama son satır bir sorun bildiriyor: ağır aşamanın tamamı 972 dakikadır ve 480 dakikalık gece penceresine sığmaz. Gecelik bir kapı, gece bitmeden bitmek zorundadır. Bu yüzden ağır aşama ikiye ayrılır — başarım koşumu, otomatik güvenlik taraması ve dayanıklılık testleri gecelik (136 dakika, pencereye sığar), başarım örneklemesi ve elle güvenlik incelemesi sürüm kapısına. Üç sıklık düzeni değişiklik başına 98,2 dakikaya ve iki koşucuya iner; karşılığında en kötü geri bildirim 1021’den 2221 dakikaya çıkar.

Altıncı satır bu kursun kuralının en açık örneğidir. Elle güvenlik incelemesi hattan tamamen çıkarıldığında değişiklik başına 86,2 dakika ve tek koşucu yeter, ortalama geri bildirim 107,7 dakikaya düşer — ve kaçan hata beşten ona çıkar. Ortalamanın iyileşmesi bir kazanç değil, bir ölçüm tuzağıdır: en geç yakalanan sınıf artık hiç yakalanmadığı için ortalamanın paydasından da çıkmıştır. Geri bildirim süresi tek başına okunduğunda, testi silmek her zaman iyi görünür.

Son satır karantinayı kapı kararına bağlar. Kırılganlık Yönetimi dersinde karantinaya alınan uçtan uca test hattan çıkarıldığında koşum bedeli değişmez (98,2 dakika aynıdır, çünkü takımın süresi başka testlerce belirlenir) ama koruduğu kusur sınıfı kaçar: beşten dokuza. Karantina bir bakım kararı gibi görünür; tabloda bir kapı kararıdır.

İlk beş satırda kaçan hata beşte sabit kalıyor. Bu beş kusur iki sınıftandır ve hiçbir düzen onları yakalamaz — anlamsal uyuşmazlık ile süreç ömrüne bağlı durum, kaynak derslerin “yakalamadığı” sütunundan gelir. Sıfır kaçan hata isteyen bir düzen yoktur; en pahalı düzen bile beşini kaçırır, çünkü envanterde o sınıfı gören bir sınama yazılmamıştır.

Kararın Geri Dönüşü

Bir kapının değeri, kırmızı sonucunun ne yaptığındadır. Hızlı ve orta aşamalar birleştirmeyi engeller: sonuç kırmızıysa değişiklik ana dala girmez ve sahibi hâlâ o işin başındadır. Gecelik kapı birleştirmeyi engelleyemez, çünkü değişiklik çoktan girmiştir; kırmızı sonucu bir kayıt açar ve o kaydın sahibi gecelik koşuma giren değişiklik kümesidir — kim olduğu tek tek belli değildir, bu da 240 dakikalık beklemenin ikinci bedelidir. Sürüm kapısı yayımı durdurur ve kararın sahibi tek bir kişi değil, sürümü onaylayan ekiptir.

Bu üç geri dönüş, aşama yerleşiminin asıl ölçütüdür: bir sınama, sonucunun karşılığı olan kararın verilebildiği en erken aşamaya konur. Elle güvenlik incelemesinin sürüm kapısında olması bir eksiklik değil, elle işin doğasıdır — sıra bekler ve otomatik bir kapıya konamaz.

Özet

  • Dört kursun otuz dersi dokuz sınama takımına toplandı; tam takım 1021 model dakikasıdır ve bedellerin tamamı kaynak derslerin kapanış tablolarından okundu.
  • Takımı üç aşamaya bölmek ortalama geri bildirimi 1021’den 228,1 dakikaya indirdi ve kaçan hatayı değiştirmedi.
  • Erken kesme geri bildirimi hiç değiştirmeden değişiklik başına makine bedelini 1021’den 426,3 dakikaya, koşucu sayısını on birden beşe indirdi.
  • Ağır aşamanın tamamı 972 dakikadır ve 480 dakikalık gece penceresine sığmaz; ikiye ayrıldığında değişiklik başına bedel 98,2 dakikaya iner, en kötü geri bildirim 2221’e çıkar.
  • Elle incelemeyi hattan çıkarmak ortalama geri bildirimi 107,7 dakikaya düşürdü ve kaçan hatayı beşten ona çıkardı; karantinadaki testin çıkarılması kaçan hatayı dokuza taşıdı.
  • Hiçbir düzen kaçan hatayı sıfırlamadı: iki kusur sınıfını envanterdeki hiçbir sınama görmüyor.

Sonraki Adım

Bu tablonun her satırı bir varsayıma yaslanıyor: aşamanın süresi, sınamanın kendi koşum bedelidir. Oysa hızlı aşama dışındaki her takım bir test ortamı ister — tümleşim takımı bir bağımlı servis, uçtan uca takımı ayakta bir sistem, başarım takımı üretim benzeri bir ortam. Bu ortamlar sonsuz değildir. İki değişiklik aynı anda geldiğinde biri bekler ve beklediği süre aşamanın süresine eklenir; ortam çoğaltıldığında bekleme düşer ama maliyet doğrusal büyür. Sonraki ders kaç ortamın hangi aşamada tutulacağını, paylaşılan ortamın kuyruk süresini ve ayrı ortamın bedelini sayar.

İ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