Ders 07 / 21
Hat Kodu Olarak Yapılandırma
Aynı hat iki biçimde tanımlanır — arayüzden elle ve depoda dosya olarak: şema ders içinde tanımlanıp altı hatalı değişiklik gerçek bir doğrulayıcıdan geçirilir, izlenebilirlik, gözden geçirmeden geçen değişiklik oranı, geri alma adımı ve beş servis için tanımın çoğaltılması sayılır.
İçindekiler
Önceki iki ders hattın süresini kısalttı ve nerede koşacağına karar verdi. İkisi de aynı nesneye dokundu ama o nesnenin kendisi hiç sorgulanmadı: sekiz aşama, aralarındaki bağımlılıklar, hangi aşamanın kapı olduğu ve kaç yuva kullanıldığı bir yerde yazılıdır. Yuva sayısını ikiden dörde çıkarmak da, bir kapıyı kaldırmak da o yazılı şeye dokunmaktır.
Nerede yazılı olduğu iki seçenektir. Hat bir arayüzden elle yapılandırılabilir: alanlar doldurulur, kaydedilir, o an yürürlüğe girer. Ya da depoda bir dosya olarak durur: değişiklik bir işleme olarak gelir, gözden geçirilir, sürümlenir. Bu ders iki biçimi dört sayıda karşılaştırır — izlenebilirlik, gözden geçirmeden geçen değişiklik oranı, geri alma adımı ve tanımın beş servise çoğaltılması.
HT37. Şema, doğrulayıcı ve dosya biçimi bu derste tanımlanmıştır; hat, yuva, asama,
komut, onceki, kapi alan adları gerçek bir aracın alanları değildir. HT38. Yirmi hat
değişikliği kurgu ölçüm ağının bir çeyreğini temsil eder; altısı hatalıdır. HT39. Arayüz
biçiminin seçim listesi yapı gereği üç hata türünü engeller — bilinmeyen alan yazılamaz, olmayan
bir aşamaya bağlanılamaz, sayı alanına metin girilemez; buna karşılık arayüzde gözden geçirme adımı
yoktur. HT40. Depo biçiminde 3, 11 ve 17 numaralı değişiklikler acil gerekçesiyle gözden
geçirmeden geçmiştir. HT41. Gözden geçirme beklemesi 42 dakika, şema doğrulaması 4 saniyedir;
geri alma adımları sayılır, dakikaları kurgudur. HT42. Satır sayıları gerçekten üretilen
metinden sayılır.
Şemanın Kendisi
Bir hat tanımı dosya olarak durduğunda kazanılan ilk şey şudur: tanım artık veridir ve veri okunabilir. Şema alanları ve türlerini bildirir; doğrulayıcı şemayı okuyup tanımı denetler.
// olcum-agi/sema.mjs — hat tanimi semasi, dogrulayici ve yazici (alan adlari bu derste tanimlandi) export const SEMA = { hat: "metin", yuva: "sayi", asamalar: "dizi" }; const ASAMA = { ad: "metin", komut: "metin", onceki: "dizi", kapi: "eh" }; const tip = { metin: (d) => typeof d === "string", sayi: (d) => Number.isInteger(d), dizi: (d) => Array.isArray(d), eh: (d) => d === "evet" || d === "hayir" }; const alanDenetle = (nesne, sema, on, h) => { for (const a of Object.keys(nesne)) if (!sema[a]) h.push(`bilinmeyen alan: ${on}${a}`); for (const [a, tp] of Object.entries(sema)) if (!(a in nesne)) h.push(`eksik alan: ${on}${a}`); else if (!tip[tp](nesne[a])) h.push(`tur hatasi: ${on}${a}, ${tp} bekleniyor`); }; export const dogrula = (t) => { const h = []; alanDenetle(t, SEMA, "", h); if (!Array.isArray(t.asamalar)) return h; const adlar = t.asamalar.map((s) => s.ad); for (const s of t.asamalar) { alanDenetle(s, ASAMA, `${s.ad}.`, h); for (const o of s.onceki || []) if (!adlar.includes(o)) h.push(`bilinmeyen onceki: ${s.ad} -> ${o}`); } const bak = (ad, iz) => (iz.includes(ad) ? true : (t.asamalar.find((s) => s.ad === ad)?.onceki || []).some((o) => bak(o, [...iz, ad]))); for (const s of t.asamalar) if (bak(s.ad, [])) { h.push(`dongu: ${s.ad}`); break; } return h; }; export const TABAN = { hat: "okuma-toplayici", yuva: 3, asamalar: [ ["bagimlilik-cozumu", "coz", [], "hayir"], ["derleme", "derle", ["bagimlilik-cozumu"], "hayir"], ["statik-cozumleme", "coz-statik", ["bagimlilik-cozumu"], "evet"], ["birim-sinamasi", "sina-birim", ["derleme"], "evet"], ["tumlesim-sinamasi", "sina-tumlesim", ["derleme"], "evet"], ["paketleme", "paketle", ["derleme"], "hayir"], ["sozlesme-sinamasi", "sina-sozlesme", ["paketleme"], "evet"], ["imaj-uretimi", "uret-imaj", ["paketleme"], "hayir"], ].map(([ad, komut, onceki, kapi]) => ({ ad, komut, onceki, kapi })) }; // Tanimin dosya bicimi: her asama uc ya da dort satir. export const yaz = (t) => [`hat ${t.hat}`, `yuva ${t.yuva}`, ...t.asamalar.flatMap((s) => [`asama ${s.ad}`, ` komut ${s.komut}`, ...(s.onceki.length ? [` onceki ${s.onceki.join(" ")}`] : []), ` kapi ${s.kapi}`])]; // Kurgu olcum aginin bes servisi: taban hat + servise ozgu farklar. export const SERVISLER = [ { ad: "okuma-toplayici", fark: [] }, { ad: "dogrulayici", fark: [["yuva", 2]] }, { ad: "faturalama", fark: [["sil", "sozlesme-sinamasi"]] }, { ad: "is-emri", fark: [["ekle", { ad: "gecikme-sinamasi", komut: "sina-gecikme", onceki: ["paketleme"], kapi: "evet" }]] }, { ad: "gecelik-toplu-is", fark: [["yuva", 4], ["sil", "statik-cozumleme"]] }, ]; export const uygula = (s) => { const t = { ...TABAN, hat: s.ad, asamalar: TABAN.asamalar.map((a) => ({ ...a })) }; for (const [tur, d] of s.fark) { if (tur === "yuva") t.yuva = d; if (tur === "sil") t.asamalar = t.asamalar.filter((a) => a.ad !== d); if (tur === "ekle") t.asamalar.push(d); } return t; };
Doğrulayıcı dört şey denetler: bilinmeyen ve eksik alan, tür uyuşmazlığı, olmayan bir aşamaya
yapılan onceki atfı ve bağımlılıklarda döngü. Dördü de tanımın metninden kararlaştırılır;
hattı koşturmak gerekmez.
İki Biçim
Altı hatalı değişiklik gerçekten kurulur, gerçekten doğrulayıcıdan geçirilir ve her biri için iki biçimin tuttuğu ayrı ayrı yazılır.
// olcum-agi/bicimler.mjs — ayni hat iki bicimde: izlenebilirlik, gozden gecirme, kapi, geri alma import { dogrula, TABAN } from "./sema.mjs"; const kopya = () => ({ ...TABAN, asamalar: TABAN.asamalar.map((a) => ({ ...a })) }); const N = 20, GECEN = [3, 11, 17]; // gozden gecirmeden gecen uc hat degisikligi const IZ = ["kim", "ne zaman", "neden", "ne degisti"]; // Alti hatali hat degisikligi. no: kacinci degisiklikte geldigi. arayuz: bicim geregi arayuzde // yazilamayan hata (secim listesi engeller). gozden: yalniz insanin gorebilecegi hata. const HATALAR = [ ["H1 bilinmeyen alan", 2, 1, 0, (t) => { t.asama = t.asamalar; delete t.asamalar; }], ["H2 dongu", 5, 0, 1, (t) => { t.asamalar[1].onceki = ["paketleme"]; }], ["H3 bilinmeyen onceki", 8, 1, 0, (t) => { t.asamalar[3].onceki = ["derleme-eski"]; }], ["H4 tur hatasi (yuva)", 11, 1, 0, (t) => { t.yuva = "uc"; }], ["H5 kapi kaldirildi", 14, 0, 1, (t) => { t.asamalar.splice(2, 1); }], ["H6 kapi devre disi", 17, 0, 1, (t) => { t.asamalar[4].kapi = "hayir"; }], ]; const s = { arayuz: 0, depo: 0, sema: 0, yalniz: 0 }; console.log("hata no sema dogrulayicisinin dedigi arayuz depo"); for (const [ad, no, arayuz, gozden, boz] of HATALAR) { const t = kopya(); boz(t); const bulgu = dogrula(t), sema = bulgu.length > 0; const depo = sema || (gozden && !GECEN.includes(no)); // gecen degisiklige insan bakmaz s.sema += sema; s.arayuz += arayuz; s.depo += depo; s.yalniz += depo && !sema; console.log(ad.padEnd(22) + String(no).padStart(2) + " " + (bulgu[0] || "— (sema gecti)").slice(0, 42).padEnd(44) + (arayuz ? "tutar" : "kacar").padEnd(8) + (depo ? "tutar" : "kacar")); } const BICIM = { arayuz: { iz: IZ.slice(0, 2), gecen: N, dakika: 35, geri: ["degisikligi bul", "eski degeri hatirla", "elle geri yaz", "kaydet", "elle tetikle", "dogrula"] }, depo: { iz: IZ, gecen: GECEN.length, dakika: 4, geri: ["onceki surume don", "hat kendini kossun"] }, }; console.log(`\nizlenebilirlik (${N} degisiklik x ${IZ.length} alan):`); for (const [ad, b] of Object.entries(BICIM)) console.log(` ${ad.padEnd(8)}${b.iz.length * N}/${IZ.length * N} alan — ${b.iz.join(", ")}`); console.log(`gozden gecirmeden gecen: arayuz ${N}/${N} (%100), depo ${GECEN.length}/${N} (%${(GECEN.length * 100) / N})`); console.log(`kapi hesabi (6 hata): arayuz ${s.arayuz}/6, depo ${s.depo}/6 (sema ${s.sema}, yalniz gozden gecirme ${s.yalniz})`); console.log("\nhat kirildiginda geri alma:"); for (const [ad, b] of Object.entries(BICIM)) console.log(` ${ad.padEnd(8)}${b.geri.length} adim, ${b.dakika} dk — ${b.geri.join(" / ")}`); console.log(`\nkapinin ekledigi sure: sema 4 s x ${N} = ${4 * N} s; gozden gecirme 42 dk x ${N - GECEN.length} = ${42 * (N - GECEN.length)} dk`);
hata no sema dogrulayicisinin dedigi arayuz depo H1 bilinmeyen alan 2 bilinmeyen alan: asama tutar tutar H2 dongu 5 dongu: derleme kacar tutar H3 bilinmeyen onceki 8 bilinmeyen onceki: birim-sinamasi -> derle tutar tutar H4 tur hatasi (yuva) 11 tur hatasi: yuva, sayi bekleniyor tutar tutar H5 kapi kaldirildi 14 — (sema gecti) kacar tutar H6 kapi devre disi 17 — (sema gecti) kacar kacar izlenebilirlik (20 degisiklik x 4 alan): arayuz 40/80 alan — kim, ne zaman depo 80/80 alan — kim, ne zaman, neden, ne degisti gozden gecirmeden gecen: arayuz 20/20 (%100), depo 3/20 (%15) kapi hesabi (6 hata): arayuz 3/6, depo 5/6 (sema 4, yalniz gozden gecirme 1) hat kirildiginda geri alma: arayuz 6 adim, 35 dk — degisikligi bul / eski degeri hatirla / elle geri yaz / kaydet / elle tetikle / dogrula depo 2 adim, 4 dk — onceki surume don / hat kendini kossun kapinin ekledigi sure: sema 4 s x 20 = 80 s; gozden gecirme 42 dk x 17 = 714 dk
Kapının Hesabı
Tablo beklenmedik bir yerden başlar: arayüz biçimi üç hatayı yapı gereği tutar. Bilinmeyen bir alan yazılamaz çünkü alan listesi sabittir; olmayan bir aşamaya bağlanılamaz çünkü seçim listeden yapılır; sayı alanına metin girilemez. Depo biçiminde bu üç hata yazılabilir ve ancak şema doğrulayıcısı koşarsa yakalanır. Doğrulayıcı olmadan depo biçimi bu üç hatada arayüzden kötüdür.
Doğrulayıcı koştuğunda tablo döner. Şema dört hatayı tutar; dördüncüsü döngüdür ve arayüzün
göremediği hatadır: derleme aşaması paketlemeye bağlanınca çizge kendine kapanır, ama her alan
tek tek geçerlidir. Bir arayüz alan denetler, çizge denetlemez.
Geriye iki hata kalır ve ikisi de kapının kendisine dokunur: H5 statik çözümleme aşamasını siler,
H6 tümleşim sınamasının kapi alanını hayir yapar. İkisi de şemaya göre kusursuz tanımlardır.
Yalnız bir insan “bu değişiklik neden bir kapıyı kaldırıyor” diye sorabilir. Depo biçimi H5’i
tutar; H6 kaçar, çünkü 17 numaralı değişiklik gözden geçirmeden geçen üçten biridir.
Toplam arayüz 3/6, depo 5/6. Kapının marjinal değeri ise ayrı okunur: şema dört hata tutar ve 20 değişiklikte toplam 80 saniye ekler. Gözden geçirme yalnız bir hatayı tek başına tutar ve 17 değişiklikte 714 dakika bekletir. Bir kusur için on iki saat — bu kursun ölçüsüne göre savunulması en zor kapı budur. Savunması tek bir yerdedir: tuttuğu hata, hattın bir kapısını kaldıran değişikliktir; bu kursun bütün ölçümlerinin dayandığı şeyi sessizce silen türden. Buna karşılık aynı hatayı çok daha ucuza tutmanın yolu görünüyor — şemaya “kapı sayısı azalamaz” kuralını eklemek. Bu ders o kuralı ölçmez; ama ölçseydi gözden geçirmenin marjinal değeri sıfıra inerdi ve 714 dakikalık kapı kaldırılabilir hâle gelirdi.
İzlenebilirlik iki alanda ayrılır. Arayüz 80 alanın 40’ını kaydeder — kim ve ne zaman. Eksik olan ikisi “neden” ve “ne değişti”dir, yani hat gece bozulduğunda sorulacak tam olarak iki soru. Geri alma tablosu aynı boşluğun ikinci faturasıdır: arayüzde geri almanın ilk adımı “değişikliği bul”dur ve cevabı hiçbir yerde yazılı değildir — altı adım, 35 dakika. Depoda önceki sürüme dönmek iki adım ve 4 dakikadır, çünkü “ne değişti” zaten kayıtlıdır.
Beş Servis
Kurgu ölçüm ağının beş servisi neredeyse aynı hattı kullanır. Tanım her servise kopyalanabilir ya da ortak parça ayrılıp farklar bildirilebilir. İki biçimin satırları gerçekten üretilip sayılır.
// olcum-agi/cogaltma.mjs — bes servis: tam kopya ile ortak parca ayrilmis bicim import { SERVISLER, uygula, yaz, TABAN } from "./sema.mjs"; const tam = SERVISLER.map((s) => ({ ad: s.ad, satir: yaz(uygula(s)) })); const toplamTam = tam.reduce((t, s) => t + s.satir.length, 0); // Ortak parca: hat adi disindaki her sey. Servis dosyasi: kalitim + ad + yalniz farklar. const ortak = yaz(TABAN).filter((l) => !l.startsWith("hat ")); const ayrik = SERVISLER.map((s) => ({ ad: s.ad, satir: ["kalitim ortak", `hat ${s.ad}`, ...s.fark.flatMap(([tur, d]) => tur === "yuva" ? [`yuva ${d}`] : tur === "sil" ? [`asama-sil ${d}`] : [`asama ${d.ad}`, ` komut ${d.komut}`, ` onceki ${d.onceki.join(" ")}`, ` kapi ${d.kapi}`])], })); const toplamAyrik = ortak.length + ayrik.reduce((t, s) => t + s.satir.length, 0); // Bes dosyanin hepsinde birebir gecen satirlar (tekrar sayisiyla birlikte). const say = (a) => a.reduce((m, l) => m.set(l, (m.get(l) || 0) + 1), new Map()); const sayimlar = tam.map((s) => say(s.satir)); let hepsinde = 0; for (const [l, c] of sayimlar[0]) hepsinde += Math.min(c, ...sayimlar.slice(1).map((m) => m.get(l) || 0)); console.log("servis tam kopya ortak parca ile"); tam.forEach((s, i) => console.log(s.ad.padEnd(20) + String(s.satir.length).padStart(6) + String(ayrik[i].satir.length).padStart(12))); console.log("ortak dosya".padEnd(20) + " —" + String(ortak.length).padStart(12)); console.log("toplam".padEnd(20) + String(toplamTam).padStart(6) + String(toplamAyrik).padStart(12) + ` (%${(((toplamTam - toplamAyrik) * 100) / toplamTam).toFixed(1)} az satir)`); console.log(`\nbes dosyanin hepsinde birebir gecen satir: ${hepsinde} — tam kopyada ` + `${hepsinde * 5} satirin ${hepsinde * 4}'u tekrardir`); console.log("\nokunabilirlik bedeli"); console.log(` bir servisin hattini okumak: tam kopya 1 dosya ${tam[1].satir.length} satir; ` + `ortak parca ile 2 dosya ${ortak.length + ayrik[1].satir.length} satir`); console.log(` ortak dosyada tek satir degisince etkilenen servis: ${SERVISLER.length}/${SERVISLER.length}`); console.log(` bes servise yeni bir kapi eklemek: tam kopya ${5 * 4} satir / 5 ayri degisiklik; ` + `ortak parca ile 4 satir / 1 degisiklik`);
servis tam kopya ortak parca ile okuma-toplayici 33 2 dogrulayici 33 3 faturalama 29 3 is-emri 37 6 gecelik-toplu-is 29 4 ortak dosya — 32 toplam 161 50 (%68.9 az satir) bes dosyanin hepsinde birebir gecen satir: 24 — tam kopyada 120 satirin 96'u tekrardir okunabilirlik bedeli bir servisin hattini okumak: tam kopya 1 dosya 33 satir; ortak parca ile 2 dosya 35 satir ortak dosyada tek satir degisince etkilenen servis: 5/5 bes servise yeni bir kapi eklemek: tam kopya 20 satir / 5 ayri degisiklik; ortak parca ile 4 satir / 1 degisiklik
Kazanç büyüktür: 161 satır 50 satıra iner, yüzde 68,9 azalma. Kaynağı da açıktır — beş dosyanın hepsinde birebir aynı geçen 24 satır vardır ve tam kopyada bu satırlar 120 kez yazılmıştır, yani 96 satır tekrardır.
Bedel satır sayısında görünmez. Bir servisin hattını okumak tam kopyada 33 satırlık tek dosya,
ortak parça ayrıldığında 35 satır ve iki dosya demektir. Fark iki satırdır ama
asama-sil sozlesme-sinamasi satırı tek başına hiçbir şey anlatmaz; silinen şeyin ne olduğu ancak
öteki dosyada görünür ve etkin tanım hiçbir yerde bütün hâlde durmaz.
Asıl bedel etki alanıdır. Ortak dosyada değiştirilen tek satır beş servisin beşini birden etkiler — ki bu aynı zamanda kazançtır: beş servise yeni bir kapı eklemek tam kopyada 20 satır ve beş ayrı değişiklik, ortak parça ile 4 satır ve tek değişikliktir. Çoğaltma dağınıktır ama bir servisi tek başına değiştirmeye izin verir; ortaklaştırma her değişikliği beş servisin ortak kararı yapar. Beş hat gerçekten aynı kalmalıysa ortaklaştırma doğrudur; ayrışmaları bekleniyorsa 96 satırlık tekrar, ödenmiş bir bağımsızlık bedelidir.
Özet
- Tanım dosya olduğunda veri olur: şema alanları ve türleri bildirir, doğrulayıcı bilinmeyen alanı, tür hatasını, olmayan aşamaya atfı ve bağımlılık döngüsünü hattı koşturmadan yakalar.
- Arayüz biçimi üç hatayı yapı gereği tutar (alan listesi sabit, seçim listeden yapılır) ama çizgeyi denetlemez ve döngüyü kaçırır. Toplam: arayüz 3/6, depo 5/6.
- Kapının marjinal değeri: şema dört hata tutar ve 20 değişiklikte 80 saniye ekler; gözden geçirme yalnız bir hatayı tek başına tutar ve 17 değişiklikte 714 dakika bekletir. Tuttuğu hata bir kapıyı kaldıran değişikliktir; şemaya “kapı sayısı azalamaz” kuralı eklenirse bu kapı savunulamaz olur.
- İzlenebilirlik 40/80 alana karşı 80/80’dir. Arayüzde eksik olan iki alan “neden” ve “ne değişti”dir; geri almanın ilk adımı da tam olarak odur — 6 adım ve 35 dakikaya karşı 2 adım ve 4 dakika.
- Beş servis için tam kopya 161, ortak parça ayrılmış biçim 50 satırdır (yüzde 68,9 az); beş dosyada birebir aynı geçen 24 satırın 96 tekrarı silinir. Bedeli okunabilirlik ve etki alanıdır: bir servisi okumak 2 dosya 35 satıra çıkar, ortak dosyadaki tek satır beş servisi birden etkiler.
Sonraki Adım
Hat artık tanımlı, doğrulanmış, sürümlenmiş ve geri alınabilir. Bir soru hâlâ dışarıda: bu hat ne zaman koşuyor? Önceki derste günde 10 ile 80 arasında koşum sayısı verildi ve eşik o sayıdan hesaplandı, ama sayının nereden geldiği hiç söylenmedi. Koşumu başlatan şey bir tetikleyicidir ve tetikleyici de bir kapıdır: koşumun olup olmayacağına karar verir. Üç tür vardır — bir olaya bağlanan, saate bağlanan ve elle çekilen — ve üçü aynı takvimde çok farklı sayılar üretir: kaç koşum, kaçı hiçbir şey değişmemişken, değişiklikle koşum arasında ne kadar gecikme, kaç koşum birbirinin üstüne biniyor. Sonraki ders üçünü aynı iki haftalık kurgu takvimde koşturur, sonra yol tabanlı bir filtre ekleyip gereksiz koşumun düşüşünü ve filtrenin kaçırdığı gerçek değişikliği birlikte sayar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.