Ders 13 / 19
Paylaşılan Çekirdek ve Uyum Sağlayan
İki işbirliği kalıbı: paylaşılan çekirdek büyüdüğünde ortak karar gerektiren değişiklik oranının ve iki takımın birlikte düzenlediği dosya sayısının sayılması, tek bağlamın kullandığı çekirdek dosyalarının ölçülmesi, uyum sağlayan düzenin dosya ve satır kazancının aşağı akışın kendi sözlüğünde bıraktığı ad sayısıyla karşılaştırılması.
İçindekiler
Önceki iki ders aynı varsayıma dayanıyordu: karşı taraf yabancıdır, arada bir çeviri durur. Bu varsayım iki durumda tutmaz. Birincisi, iki bağlam aynı takımın ya da birlikte plan yapan iki takımın elindedir; ikisi arasında çeviri tutmak, aynı kararı iki yerde vermek anlamına gelir. İkincisi, aşağı akış takımının yukarı akışı etkileme gücü hiç yoktur; çeviri yazsa da yazmasa da yukarı akış istediği gibi değişir.
Birinci durumun cevabı ortak tutulan bir parçadır: paylaşılan çekirdek (shared kernel). İkinci durumun cevabı çeviriden vazgeçmektir: uyum sağlayan (conformist). İkisi de bedeli ölçülebilir kararlardır. Bu ders çekirdek büyüdüğünde iki takımın birlikte düzenlemek zorunda kaldığı dosya sayısını sayar, sonra uyum sağlayanın kazancını aşağı akışın kendi sözlüğünde bıraktığı ad sayısıyla karşılaştırır.
Paylaşılan Çekirdeğin Tanımı
Paylaşılan çekirdek, iki bağlamın ortak sahiplendiği bir model parçasıdır. Ortaklık kod paylaşımından fazlasını gerektirir: çekirdekteki bir değişiklik iki takımın da onayını ister, iki tarafın testleri birlikte koşar, sürüm birlikte planlanır. Bu yüzden çekirdek küçük tutulduğunda işe yarar bir anlaşmadır, büyüdüğünde iki bağlamı tek bağlama geri döndürür.
Ölçü açık: bir değişikliğin çekirdeğe dokunup dokunmaması, o değişikliğin ortak karar gerektirip gerektirmemesidir. Aşağıdaki günlük ölçüm gönderi ücretlendirme ve yönlendirme kitaplığının on altı model değişikliğini, her birinin dokunduğu alan kavramıyla taşıyor.
// degisiklik-gunlugu.mjs — on alti degisiklik ve her birinin dokundugu alan kavrami export const GUNLUK = [ ["2025-01-14", "olcu-birimi"], ["2025-01-28", "tarife-kademesi"], ["2025-02-11", "durum-kumesi"], ["2025-02-25", "tarife-kademesi"], ["2025-03-11", "esas-agirlik-kurali"], ["2025-03-25", "indirim-kurali"], ["2025-04-08", "tarife-kademesi"], ["2025-04-22", "durum-kumesi"], ["2025-05-06", "arac-secimi"], ["2025-05-20", "olcu-birimi"], ["2025-06-03", "tarife-kademesi"], ["2025-06-17", "esas-agirlik-kurali"], ["2025-07-01", "indirim-kurali"], ["2025-07-15", "durum-kumesi"], ["2025-07-29", "sozlesme-esigi"], ["2025-08-12", "aktarma-kurali"], ];
Aynı sekiz kavram iki düzende farklı dosyalara yerleşiyor. Küçük çekirdekte ortak tutulan tek şey ölçüdür: gram ile desimetreküpün tanımı ve hacim ağırlığının esas ağırlığa çevrilme kuralı. Büyük çekirdekte tarife kademesi ile durum kümesi de çekirdeğe alınmış — ikisi de tek bir bağlamın kavramı olduğu hâlde.
// ortak-say.mjs — paylasilan cekirdek buyudugunde ortak karar gerektiren degisiklik ve dosya sayisi import { GUNLUK } from "./degisiklik-gunlugu.mjs"; const KUCUK = { "olcu-birimi": ["cekirdek/olcu.mjs", ["ucretlendirme", "teslimat"]], "esas-agirlik-kurali": ["cekirdek/olcu.mjs", ["ucretlendirme", "teslimat"]], "tarife-kademesi": ["ucretlendirme/tarife.mjs", ["ucretlendirme"]], "indirim-kurali": ["ucretlendirme/indirim.mjs", ["ucretlendirme"]], "sozlesme-esigi": ["ucretlendirme/indirim.mjs", ["ucretlendirme"]], "durum-kumesi": ["teslimat/durum.mjs", ["teslimat"]], "arac-secimi": ["teslimat/arac.mjs", ["teslimat"]], "aktarma-kurali": ["teslimat/rota.mjs", ["teslimat"]], }; const BUYUK = { ...KUCUK, "tarife-kademesi": ["cekirdek/tarife.mjs", ["ucretlendirme"]], "durum-kumesi": ["cekirdek/durum.mjs", ["teslimat"]], }; for (const [ad, esleme] of [["kucuk cekirdek", KUCUK], ["buyuk cekirdek", BUYUK]]) { const cekirdek = new Map(); for (const [dosya, kullanan] of Object.values(esleme)) { if (dosya.startsWith("cekirdek/")) cekirdek.set(dosya, kullanan); } const ortakDegisiklik = GUNLUK.filter(([, k]) => esleme[k][0].startsWith("cekirdek/")); const dokunulan = new Set(ortakDegisiklik.map(([, k]) => esleme[k][0])); const tekKullanan = [...cekirdek].filter(([, k]) => k.length === 1); console.log(`${ad}: ${cekirdek.size} cekirdek dosyasi`); for (const [dosya, kullanan] of [...cekirdek].sort()) { console.log(` ${dosya.padEnd(20)} kullanan baglam: ${kullanan.join(", ")}`); } console.log(` ortak karar gerektiren degisiklik = ${ortakDegisiklik.length} / ${GUNLUK.length}`); console.log(` iki takimin birlikte duzenledigi dosya = ${dokunulan.size}`); console.log(` tek baglamin kullandigi cekirdek dosyasi = ${tekKullanan.length} / ${cekirdek.size}`); for (const b of ["ucretlendirme", "teslimat"]) { const yalniz = GUNLUK.filter(([, k]) => !esleme[k][0].startsWith("cekirdek/") && esleme[k][1].includes(b)); console.log(` ${b.padEnd(14)} tek basina yapabildigi degisiklik = ${yalniz.length}`); } }
node ortak-say.mjs
kucuk cekirdek: 1 cekirdek dosyasi cekirdek/olcu.mjs kullanan baglam: ucretlendirme, teslimat ortak karar gerektiren degisiklik = 4 / 16 iki takimin birlikte duzenledigi dosya = 1 tek baglamin kullandigi cekirdek dosyasi = 0 / 1 ucretlendirme tek basina yapabildigi degisiklik = 7 teslimat tek basina yapabildigi degisiklik = 5 buyuk cekirdek: 3 cekirdek dosyasi cekirdek/durum.mjs kullanan baglam: teslimat cekirdek/olcu.mjs kullanan baglam: ucretlendirme, teslimat cekirdek/tarife.mjs kullanan baglam: ucretlendirme ortak karar gerektiren degisiklik = 11 / 16 iki takimin birlikte duzenledigi dosya = 3 tek baglamin kullandigi cekirdek dosyasi = 2 / 3 ucretlendirme tek basina yapabildigi degisiklik = 3 teslimat tek basina yapabildigi degisiklik = 2
Küçük çekirdekte on altı değişikliğin dördü ortak karar gerektiriyor; on ikisi bir takımın kendi başına yapabildiği değişiklik. Çekirdek bir dosyadan üç dosyaya çıktığında ortak karar gerektiren değişiklik dörtten on bire yükseliyor, iki takımın kendi başına yapabildiği değişiklik on ikiden beşe iniyor. Ortaklık oranı yüzde yirmi beşten yüzde altmış dokuza çıkmış durumda.
Son satırlardan biri kararın yanlış tarafını gösteriyor: büyük çekirdekteki üç dosyanın ikisini
yalnız tek bağlam kullanıyor. cekirdek/tarife.mjs yalnız ücretlendirmenin, cekirdek/durum.mjs
yalnız teslimatın kavramı; ikisi de ortak sahiplikte durduğu için sahibi olmayan takımın onayını
bekliyor. Çekirdeğin ölçütü buradan çıkar: bir dosya çekirdekte ancak iki bağlam da onu
gerçekten kullanıyorsa durur.
Çekirdeğe Neyin Girdiği
Küçük çekirdekteki iki kavram tesadüfen orada değil. Ölçü birimi ve hacim ağırlığının esas ağırlığa çevrilme kuralı iki bağlamda aynı şeyi anlatır: ücretlendirme onunla fiyat hesaplar, teslimat onunla araç seçer, ama tanım tektir ve iki tarafın da aynı sayıya varması gerekir. İki tanım tutmak burada bir kazanç değil, bir tutarsızlık kaynağıdır.
Tarife kademesi ile durum kümesi böyle değildir. Kademelerin nerede olduğu ücretlendirmenin kararıdır; teslimat onu bilmek zorunda değildir. Durum kümesinin hangi değerleri taşıdığı teslimatın kararıdır; ücretlendirme için gönderi ya fiyatlanmıştır ya değildir. Bu iki kavramın çekirdeğe alınması bir modelleme kararı değil, dosyayı ortada bırakma kolaylığıdır.
Ölçüt bir cümleyle yazılabilir: çekirdeğe yalnız iki bağlamın da aynı anlamla kullandığı ve ayrı tanımlandığında tutarsızlık üretecek kavramlar girer.
Uyum Sağlayan
İkinci kalıp bambaşka bir durumdan çıkar. Aşağı akış takımının yukarı akışın modelini değiştirme gücü yoktur; yukarı akış kendi kararlarını kendi verir, aşağı akışın ihtiyaçları planına girmez. Bu durumda aşağı akışın iki seçeneği vardır: çeviri yazmak ya da yukarı akışın modelini olduğu gibi benimsemek. İkincisi uyum sağlayan kalıbıdır.
Yukarı akış ücretlendirme bağlamıdır ve tarife satırını kendi adlarıyla verir.
mkdir -p yukari cevirili uyumlu
// yukari/tarife.mjs — yukari akis: ucretlendirme baglaminin tarife modeli const KADEMELER = [ { ustEsasGram: 1000, kademeUcretiKurus: 4990 }, { ustEsasGram: 5000, kademeUcretiKurus: 8490 }, { ustEsasGram: 30000, kademeUcretiKurus: 24990 }, ]; const BOLGE_CARPANI = { yakin: 1, orta: 1.35, uzak: 1.8 }; export function tarifeSatiri(esasGram, ucretBolgesi) { const k = KADEMELER.find((x) => esasGram <= x.ustEsasGram); return k === undefined ? null : { kademeUcretiKurus: k.kademeUcretiKurus, bolgeCarpani: BOLGE_CARPANI[ucretBolgesi] ?? 1.8 }; }
Aşağı akış teslimat bağlamıdır ve araç seçimi için gönderinin taşıma değerine ihtiyaç duyar. Uyum sağlayan düzende tek dosya yazıyor ve yukarı akışın adlarını doğrudan kullanıyor.
// uyumlu/arac.mjs — asagi akis, yukari akisin modelini oldugu gibi benimsiyor import { tarifeSatiri } from "../yukari/tarife.mjs"; export function aracSec(esasGram, ucretBolgesi) { const t = tarifeSatiri(esasGram, ucretBolgesi); if (t === null) return "elden-teslim"; const tutar = t.kademeUcretiKurus * t.bolgeCarpani; if (tutar >= 30000) return "kapali-kasa"; return esasGram > 5000 ? "kamyonet" : "motosiklet"; }
Çevirili düzende aynı iş iki dosyaya bölünüyor; sınır dosyası yukarı akışın adlarını teslimatın diline çeviriyor.
// cevirili/tasima-ceviri.mjs — yukari akisin adlarini teslimatin diline cevirir import { tarifeSatiri } from "../yukari/tarife.mjs"; export function tasimaKosullari(esasGram, ucretBolgesi) { const t = tarifeSatiri(esasGram, ucretBolgesi); if (t === null) return { fiyatlanabilir: false, degerKurus: 0, yukGram: esasGram }; return { fiyatlanabilir: true, degerKurus: t.kademeUcretiKurus * t.bolgeCarpani, yukGram: esasGram }; }
// cevirili/arac.mjs — asagi akis cekirdegi: yalniz teslimatin kendi dilini konusur import { tasimaKosullari } from "./tasima-ceviri.mjs"; export function aracSec(yukGram, bolge) { const k = tasimaKosullari(yukGram, bolge); if (!k.fiyatlanabilir) return "elden-teslim"; if (k.degerKurus >= 30000) return "kapali-kasa"; return k.yukGram > 5000 ? "kamyonet" : "motosiklet"; }
Uyumun Kazancı ve Bedeli
Ölçü üç sayıdan oluşuyor: dosya ile satır sayısı, yukarı akışın adlarının aşağı akış çekirdeğinde geçip geçmediği ve aşağı akışın kendi sözlüğünde kaç ad kaldığı.
// uyum-say.mjs — uyum saglayan ile cevirili duzenin dosya, satir ve yukari akis adi sayimi import { readFileSync } from "node:fs"; import { aracSec as uyumluSec } from "./uyumlu/arac.mjs"; import { aracSec as cevriliSec } from "./cevirili/arac.mjs"; const YUKARI_ADLAR = ["tarifeSatiri", "kademeUcretiKurus", "bolgeCarpani", "ucretBolgesi", "esasGram"]; const ASAGI_ADLAR = ["fiyatlanabilir", "degerKurus", "yukGram", "bolge"]; const gecen = (m, adlar) => adlar.filter((a) => new RegExp(`\\b${a}\\b`).test(m)); for (const [ad, dosyalar] of [ ["uyum saglayan", ["uyumlu/arac.mjs"]], ["cevirili", ["cevirili/tasima-ceviri.mjs", "cevirili/arac.mjs"]], ]) { const satir = dosyalar.reduce((t, d) => t + readFileSync(d, "utf8").trimEnd().split("\n").length, 0); console.log(`${ad}: ${dosyalar.length} dosya, ${satir} satir`); let cekirdek = 0, cekirdekSizan = 0; for (const d of dosyalar) { const metin = readFileSync(d, "utf8"); const g = gecen(metin, YUKARI_ADLAR), y = gecen(metin, ASAGI_ADLAR); const sinir = d.includes("-ceviri.mjs"); if (!sinir) { cekirdek += 1; if (g.length > 0) cekirdekSizan += 1; } console.log(` ${d.padEnd(28)} yukari akis adi ${g.length}, kendi adi ${y.length}`); } console.log(` yukari akis adi gecen cekirdek dosyasi = ${cekirdekSizan} / ${cekirdek}`); } const ORNEKLER = [[800, "orta"], [4000, "uzak"], [20000, "yakin"], [20000, "uzak"], [50000, "orta"]]; let ayrilan = 0; for (const [gram, bolge] of ORNEKLER) { const a = uyumluSec(gram, bolge), b = cevriliSec(gram, bolge); if (a !== b) ayrilan += 1; console.log(`${String(gram).padStart(5)} g ${bolge.padEnd(5)} uyumlu ${a.padEnd(13)} cevirili ${b}`); } console.log(`ayrilan arac = ${ayrilan} / ${ORNEKLER.length}`);
node uyum-say.mjs
uyum saglayan: 1 dosya, 10 satir uyumlu/arac.mjs yukari akis adi 5, kendi adi 0 yukari akis adi gecen cekirdek dosyasi = 1 / 1 cevirili: 2 dosya, 17 satir cevirili/tasima-ceviri.mjs yukari akis adi 5, kendi adi 3 cevirili/arac.mjs yukari akis adi 0, kendi adi 4 yukari akis adi gecen cekirdek dosyasi = 0 / 1 800 g orta uyumlu motosiklet cevirili motosiklet 4000 g uzak uyumlu motosiklet cevirili motosiklet 20000 g yakin uyumlu kamyonet cevirili kamyonet 20000 g uzak uyumlu kapali-kasa cevirili kapali-kasa 50000 g orta uyumlu elden-teslim cevirili elden-teslim ayrilan arac = 0 / 5
Kazanç somut: bir dosya ve yedi satır az. Bedel de somut. Uyum sağlayan düzende aşağı akışın tek çekirdek dosyası yukarı akışın beş adını taşıyor ve kendi sözlüğünden hiçbir ad taşımıyor (kendi adı 0). Çevirili düzende çekirdek dosya yukarı akıştan tek ad taşımıyor, kendi sözlüğünden dört ad taşıyor. Beş örnekte seçilen araç iki düzende de aynı.
Bu, uyum sağlayanı bir hata olarak okumak için yeterli değildir. Aşağı akış yukarı akışın modelini gerçekten uygun buluyorsa, çeviri yalnız iki adı birbirine eşleyen bir dosyadır ve bedeli karşılıksızdır. Kalıp bir seçim olarak yazılır: aşağı akış, kendi modelini kurma hakkından sözleşme uyumu karşılığında vazgeçer. Yanlış olan, bu vazgeçmenin farkında olmadan yapılmasıdır — o zaman aşağı akışın kendi sözlüğü hiç kurulmaz ve yukarı akış her değiştiğinde iş kuralı dosyası düzenlenir.
İki Kalıbın Ortak Noktası
Paylaşılan çekirdek ile uyum sağlayan aynı şeyden vazgeçer: çeviriden. Farkı, vazgeçmenin karşılığında ne alındığıdır. Paylaşılan çekirdekte iki taraf da karar hakkının bir kısmını verir ve karşılığında tek bir tanım alır; ölçüsü ortak karar gerektiren değişiklik oranıdır. Uyum sağlayanda yalnız bir taraf verir ve karşılığında dosya ile satır alır; ölçüsü aşağı akışın kendi sözlüğünde kalan ad sayısıdır. İkisi de bir bedelle alınır, ikisi de sayılabilir.
Özet
- Paylaşılan çekirdek, iki bağlamın ortak sahiplendiği model parçasıdır; çekirdeğe dokunan her değişiklik iki takımın kararını gerektirir.
- On altı değişiklikte çekirdek bir dosyadan üç dosyaya çıktığında ortak karar gerektiren değişiklik 4/16’dan 11/16’ya yükseldi; takımların tek başına yapabildiği değişiklik 12’den 5’e indi.
- Büyük çekirdekteki üç dosyanın ikisini yalnız tek bağlam kullanıyordu; çekirdeğe yalnız iki bağlamın da aynı anlamla kullandığı ve ayrı tanımlandığında tutarsızlık üretecek kavramlar girer.
- Uyum sağlayan, aşağı akışın çeviriden vazgeçip yukarı akışın modelini olduğu gibi benimsemesidir; kazancı 1 dosya ve 7 satır oldu.
- Bedeli aynı ölçekte göründü: uyum sağlayan düzende aşağı akış çekirdeği yukarı akıştan 5 ad taşıdı ve kendi sözlüğünden 0 ad taşıdı; çevirili düzende bu sayılar 0 ve 4 oldu. Beş örnekte seçilen araç iki düzende de aynıydı.
- İki kalıp da çeviriden vazgeçer; paylaşılan çekirdek karşılığında tek tanım alır, uyum sağlayan karşılığında dosya ve satır alır.
Sonraki Adım
Buraya kadar bağlamların sınırı çizildi, aralarındaki ilişkiler adlandırıldı ve üç işbirliği biçiminin bedeli sayıldı. Bütün bu kararlar bir soruyu yanıtsız bıraktı: hangi bağlama ne kadar tasarım emeği verilecek? Ölçü birimi tanımı ile ücret kuralı aynı özenle mi modellenir, taşıyıcı sağlayıcısının durum çevirisi ile indirim kuralı aynı sınıfa mı girer? Alan kendi içinde eşit değildir: bir kısmı kurumun kazandığı işin kendisidir, bir kısmı onu ayakta tutar, bir kısmı ise her kurumda aynıdır. Sonraki ders bu üçlü ayrımı kurar, değişiklik günlüğünden hangi parçanın gerçekten ne kadar değiştiğini çıkarır ve emeğin dağılımını değişimin dağılımıyla karşılaştırıp yanlış yere harcanan emeği sayar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.