Ders 06 / 11
Denge Kurmak
Çelişen kalite nitelikleri arasında seçim yapma pratiği: alternatif–nitelik tablosunun bir veri yapısına çevrilmesi, aynı tablonun iki ağırlık kümesiyle puanlanması, ağırlık değiştikçe kazanan alternatifin değişmesinin duyarlılık taramasıyla sayılması ve her ağırlığın kaynağının yazılı olup olmamasının kararın savunulabilirliğine bağlanması.
İçindekiler
Önceki konu rolün ne olduğunu ve verilmiş bir kararın ekibe nasıl aktarıldığını tanımladı. Açıkta kalan şey kararın kendisidir: iki kalite niteliği aynı anda sağlanamadığında hangisinin seçileceği, seçimin neye dayandığı ve seçim sorgulandığında geriye ne kalacağı.
Niteliklerin birbiriyle çelişebildiği ve bu çelişkinin bir olgu olduğu Sistem Tasarımına Giriş kursunda ölçüldü; erişilebilirlik ile tutarlılık arasındaki gerilim orada sayılarla gösterildi. Burada tekrarlanmaz. Bu dersin sorusu gerilimin var olup olmadığı değil, gerilim verilmişken kararın nasıl verildiğidir. Ölçülecek nicelik de budur: bir kararın hangi varsayıma ne kadar tutunduğu.
Kararın Nesnesi: Alternatif–Nitelik Tablosu
Through-line bölgesel bir kütüphane ağının yazılım sistemidir. Kurgu bir modeldir; gerçek bir kurum, ekip ya da ürün anlatılmaz. Modelde on dört şube, merkezde kendi bütçesi olan bir bilgi işlem birimi, dışarıdan alınmış bir katalog sistemi ve içeride yazılmış ödünç servisleri vardır.
Karar şudur: şubelerin ödünç kayıtlarına erişimi nasıl kurulacak? Dört alternatif ve dört nitelik tanımlanır (MP1; puanlar 1–5 aralığında, büyük olan daha iyidir ve model değerleridir):
tek-merkez— tek yetkili veritabanı, her şube her işlemde merkeze bağlanır.sube-onbellek— merkez yetkili, her şubede salt-okunur önbellek; yazma merkeze gider.sube-kopya— her şubede tam kopya, şube işlemi yerelde kapatır, eşitleme sonradan olur.dis-modul— dışarıdan alınmış katalog sisteminin kendi ödünç modülü kullanılır.
Karar, kafada tutulan bir tercih olduğu sürece ölçülemez. Onu ölçülebilir kılan tek şey bir veri yapısına çevrilmesidir.
// denge/tablo.mjs — alternatif-nitelik tablosu bir veri yapisi olarak; kurgu bir modeldir export const NITELIK = ["kesinti-dayanimi", "kayit-tazeligi", "degistirilebilirlik", "isletme-maliyeti"]; // Puanlar 1-5 arasinda, buyuk olan daha iyi (MP1). Kaynaklari derste yazilidir. export const ALTERNATIF = [ { ad: "tek-merkez", puan: [1, 5, 4, 5] }, { ad: "sube-onbellek", puan: [3, 4, 3, 4] }, { ad: "sube-kopya", puan: [5, 2, 2, 2] }, { ad: "dis-modul", puan: [4, 3, 1, 3] }, ]; export function puanla(agirlik) { return ALTERNATIF.map((a) => ({ ad: a.ad, toplam: a.puan.reduce((t, p, i) => t + p * agirlik[i], 0), })).sort((x, y) => y.toplam - x.toplam); } export const kazanan = (agirlik) => puanla(agirlik)[0].ad;
Tabloda hiçbir satır bütün sütunlarda üstün değildir. Ödünleşim tam olarak budur: bir sütunu yükselten alternatif başka bir sütunu düşürür. Kazananı belirleyen şey artık puanlar değil, sütunlara verilen ağırlıklardır.
Aynı Tablo, İki Ağırlık Kümesi
İki uygulama biçimi aynı girdide koşturulur. Birincisi ağırlık sorusunu hiç sormaz ve dört niteliği eşit sayar — pratikte en sık görülen biçim budur, çünkü ağırlık yazılmadığında öntanımlı olarak eşit kabul edilmiş olur. İkincisi ağırlıkları açıkça yazar.
// denge/puanla.mjs — ayni tablo iki agirlik kumesiyle: kazanan alternatif degisiyor mu import { NITELIK, ALTERNATIF, puanla } from "./tablo.mjs"; const KUME = { "esit-agirlik": [0.25, 0.25, 0.25, 0.25], "yazili-agirlik": [0.50, 0.20, 0.15, 0.15], }; console.log(`nitelik : ${NITELIK.join(" ")}`); for (const a of ALTERNATIF) console.log(`${a.ad.padEnd(13)}: ${a.puan.join(" ")}`); console.log(""); for (const [ad, w] of Object.entries(KUME)) { const s = puanla(w); console.log(`${ad.padEnd(15)} w=[${w.join(" ")}]`); console.log(` ${s.map((x) => `${x.ad}=${x.toplam.toFixed(2)}`).join(" ")}`); console.log(` kazanan: ${s[0].ad} (ikinciyle fark ${(s[0].toplam - s[1].toplam).toFixed(2)})`); }
nitelik : kesinti-dayanimi kayit-tazeligi degistirilebilirlik isletme-maliyeti tek-merkez : 1 5 4 5 sube-onbellek: 3 4 3 4 sube-kopya : 5 2 2 2 dis-modul : 4 3 1 3 esit-agirlik w=[0.25 0.25 0.25 0.25] tek-merkez=3.75 sube-onbellek=3.50 sube-kopya=2.75 dis-modul=2.75 kazanan: tek-merkez (ikinciyle fark 0.25) yazili-agirlik w=[0.5 0.2 0.15 0.15] sube-kopya=3.50 sube-onbellek=3.35 dis-modul=3.20 tek-merkez=2.85 kazanan: sube-kopya (ikinciyle fark 0.15)
Aynı puan tablosu, aynı dört alternatif; kazanan tek-merkez yerine sube-kopya oldu ve
tek-merkez sondan birinciliğe düştü. Kararı belirleyen şey alternatiflerin ölçülmüş
nitelikleri değil, ağırlıklardır. Ağırlıklar yazılmadığında karar yine de verilir — eşit ağırlık
da bir ağırlık kümesidir, yalnızca kimse onu seçtiğini söylememiştir.
İki kümedeki fark sütunlarının küçüklüğü de bir bilgidir: 0,25 ve 0,15. Kazanan alternatif ikincisini yarım puan geride bırakmıyor. Bu, kararın ne kadar sağlam durduğunu sormayı gerektirir.
Duyarlılık Taraması
Tek bir ağırlık kümesiyle çıkan sonuç bir karar değil, bir örnektir. Kararın ne kadar dayanıklı olduğu ancak ağırlık uzayı taranarak görülür. Tarama, dört ağırlığın toplamı 1 olacak biçimde 0,05 adımlı bir ızgara üretir ve her noktada kazananı sayar.
// denge/tarama.mjs — duyarlilik taramasi: agirlik degistikce kazanan alternatif degisiyor mu import { NITELIK, ALTERNATIF, puanla, kazanan } from "./tablo.mjs"; const ADIM = 0.05, N = NITELIK.length, ESIT = 1e-9; const izgara = []; (function uret(kalan, biriken) { if (biriken.length === N - 1) return izgara.push([...biriken, kalan]); for (let p = 0; p <= kalan + ESIT; p += ADIM) uret(kalan - p, [...biriken, p]); })(1, []); const kesin = new Map(ALTERNATIF.map((a) => [a.ad, 0])); let berabere = 0; for (const w of izgara) { const s = puanla(w); if (s[0].toplam - s[1].toplam < ESIT) { berabere += 1; continue; } kesin.set(s[0].ad, kesin.get(s[0].ad) + 1); } console.log(`izgara: adim ${ADIM}, ${izgara.length} agirlik vektoru`); for (const [ad, k] of [...kesin].sort((a, b) => b[1] - a[1])) console.log(` ${ad.padEnd(13)} tek basina ilk sirada: ${String(k).padStart(4)} vektor (%${(100 * k / izgara.length).toFixed(1)})`); console.log(` (ilk iki alternatifin esitlendigi ${berabere} vektor sayilmadi)`); const SECILEN = [0.50, 0.20, 0.15, 0.15]; const secilenKazanan = kazanan(SECILEN); const l1 = (a, b) => a.reduce((t, v, i) => t + Math.abs(v - b[i]), 0); let enYakin = { d: Infinity, w: null, ad: null }; for (const w of izgara) { const k = kazanan(w); if (k === secilenKazanan) continue; const d = l1(w, SECILEN); if (d < enYakin.d) enYakin = { d, w, ad: k }; } console.log(`\nsecilen agirlik [${SECILEN.join(" ")}] -> ${secilenKazanan}`); console.log(`kirilma mesafesi (L1): ${enYakin.d.toFixed(2)}`); console.log(` en yakin farkli vektor [${enYakin.w.map((x) => x.toFixed(2)).join(" ")}] -> ${enYakin.ad}`); console.log("\ntek agirligi 0.10 oynatinca (kalan uc agirlik oranti korunarak dengelenir):"); for (let i = 0; i < N; i++) { const satir = []; for (const delta of [-0.10, +0.10]) { const yeni = SECILEN.slice(); yeni[i] = Math.max(0, Math.min(1, yeni[i] + delta)); const pay = (1 - yeni[i]) / (1 - SECILEN[i]); for (let j = 0; j < N; j++) if (j !== i) yeni[j] = SECILEN[j] * pay; satir.push(`${delta > 0 ? "+" : ""}${delta.toFixed(2)} -> ${kazanan(yeni)}`); } const degisti = satir.some((s) => !s.endsWith(secilenKazanan)); console.log(` ${NITELIK[i].padEnd(20)} ${satir.join(" , ")}${degisti ? " <- kazanan degisti" : ""}`); }
izgara: adim 0.05, 1771 agirlik vektoru tek-merkez tek basina ilk sirada: 1211 vektor (%68.4) sube-kopya tek basina ilk sirada: 314 vektor (%17.7) sube-onbellek tek basina ilk sirada: 220 vektor (%12.4) dis-modul tek basina ilk sirada: 0 vektor (%0.0) (ilk iki alternatifin esitlendigi 26 vektor sayilmadi) secilen agirlik [0.5 0.2 0.15 0.15] -> sube-kopya kirilma mesafesi (L1): 0.10 en yakin farkli vektor [0.45 0.20 0.15 0.20] -> sube-onbellek tek agirligi 0.10 oynatinca (kalan uc agirlik oranti korunarak dengelenir): kesinti-dayanimi -0.10 -> sube-onbellek , +0.10 -> sube-kopya <- kazanan degisti kayit-tazeligi -0.10 -> sube-kopya , +0.10 -> sube-onbellek <- kazanan degisti degistirilebilirlik -0.10 -> sube-kopya , +0.10 -> sube-kopya isletme-maliyeti -0.10 -> sube-kopya , +0.10 -> sube-onbellek <- kazanan degisti
Tarama üç ayrı sayı üretiyor ve üçü de karar hakkında farklı şey söylüyor.
Kazanılan bölge. Seçilen alternatif sube-kopya, 1771 vektörün 314’ünde ilk sırada. Karar,
ağırlık uzayının yüzde 17,7’sinde savunulabilir bir karardır. tek-merkez yüzde 68,4 ile en geniş
bölgeyi tutuyor; yani seçilen alternatif “genelde en iyi” olan değil, yazılan ağırlıklar altında
en iyi olandır. Bu iki cümle arasındaki fark, kararın gerekçesinin tamamıdır.
Kırılma mesafesi. Seçilen vektörden kazananın değiştiği en yakın noktaya L1 uzaklığı 0,10;
işletme maliyeti ağırlığı 0,15’ten 0,20’ye çıkarıldığında kazanan sube-onbellek oluyor. Dört
ağırlığın üçünde 0,10’luk bir oynama kazananı değiştiriyor. Bu bir kusur değil, kararın gerçek
hassasiyetidir; sorun ancak bu hassasiyet bilinmeden karar verildiğinde başlar.
Hiçbir ağırlıkta kazanmayan alternatif. dis-modul 1771 vektörün hiçbirinde tek başına ilk
sırada değil. Bu sonuç ağırlık tartışmasından bağımsızdır: hangi ağırlık kümesi üzerinde
anlaşılırsa anlaşılsın bu alternatif seçilmeyecektir, çünkü sube-onbellek ile sube-kopya
birlikte onu her yönden kapatıyor. Toplantıda üzerinde en çok konuşulacak alternatif, tartışmaya
hiç girmeden elenebilir olandır.
Ağırlığın Kaynağı
Buraya kadar ölçülen her şey ağırlıkların doğru olduğunu varsaydı. Ağırlıkların kendisi nereden geliyor? Sistem Tasarımına Giriş kursunda kurulan eşiğin kaynağı ölçüsü tam olarak bunu sorar: bir sayı yazıldığında, onun neden o sayı olduğu da yazılmış olmalıdır. Ağırlık da bir eşiktir; kaynağı yazılmamış ağırlık, kaynağı yazılmamış eşikle aynı boşluğu bırakır.
Aşağıdaki koşum her ağırlığa bir kaynak alanı ekler ve iki şeyi kesiştirir: ağırlığın kaynağı yazılı mı, ve o ağırlık kazananı değiştirebiliyor mu. Kaynak olarak gösterilen üç kayıt modelin girdisidir (MP2); dördüncü ağırlığın arkasında bir kayıt yoktur.
// denge/kaynak.mjs — her agirligin kaynagi yazili mi, kirilma o agirligin uzerinde mi import { NITELIK, kazanan } from "./tablo.mjs"; const AGIRLIK = [ { deger: 0.50, kaynak: "sube aginin kopuk kaldigi sure kaydi (MP2)" }, { deger: 0.20, kaynak: "odunc kurallarindaki yazili iade toleransi (MP2)" }, { deger: 0.15, kaynak: "yillik kural degisikligi sayisi kaydi (MP2)" }, { deger: 0.15, kaynak: null }, ]; function oynat(w, i, delta) { const yeni = w.slice(); yeni[i] = Math.max(0, Math.min(1, yeni[i] + delta)); const pay = (1 - yeni[i]) / (1 - w[i]); for (let j = 0; j < w.length; j++) if (j !== i) yeni[j] = w[j] * pay; return yeni; } function denetle(agirlik) { const w = agirlik.map((a) => a.deger); const temel = kazanan(w); const satir = agirlik.map((a, i) => ({ ad: NITELIK[i], kaynakli: a.kaynak !== null, kirilgan: [-0.10, 0.10].some((d) => kazanan(oynat(w, i, d)) !== temel), })); return { temel, satir, acik: satir.filter((s) => s.kirilgan && !s.kaynakli).length }; } for (const [ad, agirlik] of [["ilk hali", AGIRLIK], ["maliyet agirligi olculdukten sonra", AGIRLIK.map((a, i) => i === 3 ? { deger: 0.25, kaynak: "birim butce tavani ve kopya basina yillik gider (MP3)" } : i === 0 ? { deger: 0.40, kaynak: a.kaynak } : a)]]) { const d = denetle(agirlik); console.log(`${ad}: w=[${agirlik.map((a) => a.deger.toFixed(2)).join(" ")}] -> ${d.temel}`); for (const s of d.satir) console.log(` ${s.ad.padEnd(20)} kaynak: ${(s.kaynakli ? "yazili" : "yok").padEnd(7)}` + ` kirilgan: ${s.kirilgan ? "evet" : "hayir"}`); console.log(` kazanani degistirebilen ama kaynagi yazilmamis agirlik: ${d.acik}\n`); }
ilk hali: w=[0.50 0.20 0.15 0.15] -> sube-kopya kesinti-dayanimi kaynak: yazili kirilgan: evet kayit-tazeligi kaynak: yazili kirilgan: evet degistirilebilirlik kaynak: yazili kirilgan: hayir isletme-maliyeti kaynak: yok kirilgan: evet kazanani degistirebilen ama kaynagi yazilmamis agirlik: 1 maliyet agirligi olculdukten sonra: w=[0.40 0.20 0.15 0.25] -> sube-onbellek kesinti-dayanimi kaynak: yazili kirilgan: evet kayit-tazeligi kaynak: yazili kirilgan: hayir degistirilebilirlik kaynak: yazili kirilgan: hayir isletme-maliyeti kaynak: yazili kirilgan: hayir kazanani degistirebilen ama kaynagi yazilmamis agirlik: 0
İlk koşumda kararı taşıyamayan tek bir sayı var: işletme maliyeti ağırlığı hem kazananı
değiştirebiliyor hem de kaynağı yazılmamış. Karar sube-kopya yönünde verildi, ama gerekçesinin
bir bacağı boşta. degistirilebilirlik ağırlığının kaynağı yazılmış olması ise kararı hiç
sağlamlaştırmıyor — o ağırlık 0,10 oynatıldığında kazanan değişmiyor. Gerekçe yazma emeği,
kararın taşındığı yere değil, taşınmadığı yere harcanmış.
İkinci koşum eksik kaynağın kapatılmasının sonucudur: birim bütçe tavanı ve kopya başına yıllık
gider ölçülünce (MP3) maliyet ağırlığı 0,15 yerine 0,25 çıkıyor, kesinti ağırlığı da 0,50’den 0,40’a
iniyor. Kazanan sube-onbellek oluyor ve kaynağı yazılmamış kırılgan ağırlık sayısı 1’den 0’a
düşüyor. İlk karar yanlış değildi; ölçülmemişti. İki koşumun farkı, mimarın işinin ağırlıkları
tartışmak değil, ağırlıkların kaynağını bulmak olduğunu gösteriyor.
Buradan çıkan ölçü şudur: kazananı değiştirebilen ama kaynağı yazılmamış ağırlık sayısı. Bu sayı sıfır değilse karar savunulabilir değildir — doğru ya da yanlış olduğu bilinmiyor demektir. Sayı sıfırsa karar tartışılabilir kalır, ama tartışma artık ağırlık üzerinde değil, ağırlığın dayandığı kayıt üzerinde yürür ve o kayıt ölçülebilir bir şeydir.
Özet
- Denge kurmak, tercih beyan etmek değil, alternatif–nitelik tablosunu bir veri yapısına çevirip ağırlıklı puanlamayı koşturmaktır; tabloda hiçbir alternatif bütün sütunlarda üstün değildir.
- Aynı dört alternatif ve aynı puanlar, eşit ağırlıkla
tek-merkez, yazılı ağırlıklasube-kopyakazandırdı; ağırlık yazılmadığında eşit ağırlık öntanımlı olarak seçilmiş olur. - 1771 vektörlük duyarlılık taramasında
sube-kopyayüzde 17,7’lik bir bölgede kazandı,tek-merkezyüzde 68,4’lük bölgede; kırılma mesafesi 0,10 ve dört ağırlığın üçünde 0,10’luk oynama kazananı değiştiriyor. dis-modul1771 vektörün hiçbirinde tek başına ilk sırada değil; ağırlık üzerinde anlaşmaya gerek kalmadan elenebilen alternatif, tartışmadan önce elenir.- Kararın savunulabilirliği tek bir sayıyla ölçülür: kazananı değiştirebilen ama kaynağı
yazılmamış ağırlık sayısı. İlk koşumda 1, maliyet ağırlığı ölçüldükten sonra 0 ve kazanan
sube-onbellekoldu.
Sonraki Adım
Denge kurma ölçümü kararın hangi alternatife gittiğini verdi, ama seçilen alternatifin
kendisinin ne kadar ağır olduğunu sormadı. sube-onbellek seçildiğinde ortaya bir önbellek
katmanı, bir eşitleme yolu ve bir geçersizleştirme kuralı çıkar; her biri sistemin taşıyacağı
yeni bir parçadır. Bir sonraki soru şudur: bu parçaların hangisi işin kendisinden gelir,
hangisi yalnızca çözümün kurulma biçiminden? İkisini ayırmadan yapılan her sadeleştirme, ya
gereken bir şeyi siler ya da hiçbir şeyi silmez. Sonraki ders karmaşıklığı iki bileşene ayırır
ve sadeleştirmenin kaldırabildiği payı, kaldıramadığı paydan sayarak ayırır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.