---
title: 'Modül Yazımı'
source: 'https://academia.sh/tr/kurslar/kod-olarak-altyapi/modul-yazimi'
course: 'Kod Olarak Altyapı'
language: tr
updated: '2026-08-17T18:09:07+00:00'
license: 'CC BY-SA 4.0'
---

# Modül Yazımı

Üç toplama noktasında ayrı ayrı yazılmış aynı kayıt tek bir modüle çıkarılır; girdi yüzeyi, satır kazancı ve altı sürüm boyunca büyüyen esneklik bedeli sayılır. Modülü yazan tarafın hiç görmediği dört kalem de ölçülür.

Bir önceki ders aynı tanımı birden çok ortamda koşturdu: çalışma alanı ayrımı durumu böldü, ortamlar
birbirinin kaynağına dokunmadan aynı metinden üretildi. Tanımın kendisi ise hâlâ tek parça. Bölgesel
ölçüm ağının üç toplama noktası aynı üç kaynağı içeriyor ve her biri kendi kayıt bloğunu baştan sona
taşıyor; ortak hiçbir şey ortak bir yerde durmuyor.

**Modül**, birlikte oluşturulup birlikte silinen bir kaynak kümesine ad veren, arayüzü **girdi** ve
**çıktı** olan yapılandırma parçasıdır. Sözcük yazılım paketlerindeki `modül` ile aynıdır; bağlam
farkı şu: orada arayüz çağrılabilir işlevlerdi ve üretilen şey davranıştı, burada arayüz bir
değişken listesi, üretilen şey kaynağın kendisi.

Ölçüm yine **bölgesel ölçüm ağı** üzerindedir: su sayacı okumalarını toplayan **kurgu** bir yazılım.
Gerçek altyapı sağlanmaz; ölçülen şey `node` ile kurulmuş bir **modeldir**.

**DM25.** Üç toplama noktası vardır; her biri üç kaynaktan oluşur ve üç kayıt ayrı ayrı elle
yazılmıştır.
**DM26.** Yapılandırma dili bu ders için tanımlanır: `kaynak <tür> <ad> { alan = değer }`,
`girdi <ad> [= varsayılan]`, `cikti <ad> = <değer>`, `modul <tür> <ad> { alan = değer }`. `$ad`
çağrının adına bağlıdır, değeri `yok` olan alan üretilmez, `eger` yazan kaynak koşulludur.
**DM27.** Modül **elle yazılır**; hangi alanın arayüze çıkacağına yazan karar verir. Kod modülü üç
kayda karşı sınar ve kayıtta değişen alanlarla arayüzü karşılaştırır.
**DM28.** Çağrıya yalnız zorunlu girdilerle varsayılandan sapan alanlar yazılır.
**DM29.** Gerçekte dört kalem elle değiştirilmiştir ve hiçbir kayıtta yoktur; dördü de yazılıdır,
rastgelelik yoktur.
**DM30.** Modül beş yeni kullanımla altı sürüme çıkar; her sürümün değişikliği önceden yazılıdır ve
açılan sekiz kullanımdan biri sonradan kapatılır.

## Üç Yerde Aynı Kayıt

```js
// kayit.mjs — bolgesel olcum aginin uc toplama noktasi; ucu de ayri ayri elle yazilmis kayit.
export const KAYIT = `# nokta kuzey
kaynak sanal-makine-kumesi kuzey-toplayici {
  sayi = 4
  imaj = toplayici-4.2.0
  bolge = kuzey-1
}
kaynak disk kuzey-tampon {
  boyut-gb = 200
  baglanan = kuzey-toplayici
}
kaynak ag-kurali kuzey-giris {
  kaynak-blok = 10.20.0.0/16
  kapi = 8080
  yon = giris
}
# nokta bati
kaynak sanal-makine-kumesi bati-toplayici {
  sayi = 6
  imaj = toplayici-4.2.0
  bolge = bati-1
}
kaynak disk bati-tampon {
  boyut-gb = 500
  baglanan = bati-toplayici
}
kaynak ag-kurali bati-giris {
  kaynak-blok = 10.30.0.0/16
  kapi = 8080
  yon = giris
}
# nokta dogu
kaynak sanal-makine-kumesi dogu-toplayici {
  sayi = 6
  imaj = toplayici-4.2.0
  bolge = dogu-1
}
kaynak disk dogu-tampon {
  boyut-gb = 500
  baglanan = dogu-toplayici
}
kaynak ag-kurali dogu-giris {
  kaynak-blok = 10.40.0.0/16
  kapi = 8443
  yon = giris
}`;

export const NOKTA = ["kuzey", "bati", "dogu"];
export const ELLE = [                                  // gerceginde var, hicbir kayitta yok
  ["bati", "disk", "boyut-gb", "750"],                 // disk elle buyutuldu
  ["kuzey", "sanal-makine-kumesi", "sayi", "6"],       // kume elle olceklendi
  ["dogu", "ag-kurali", "ek-kapi", "9443"],            // kayitta olmayan alan elle eklendi
  ["kuzey", "nesne-deposu", "*", "kuzey-arsiv"]];      // kayit disi kaynak elle acildi
```

Kayıt kırk iki satırdır ve her noktada sekiz alan yuvası vardır. `ELLE` listesi kaydın yanında duran
ikinci gerçekliktir: kayıtta yazmayan ama sahada var olan dört kalem. Modülü yazan taraf bu dördün
kaçını görür — ölçülecek şey budur.

## Modülün Girdi Yüzeyi

```js
// modul.mjs — elle yazilmis modul uc kayda karsi sinanir; girdi yuzeyi, satir kazanci ve kayit-gercek farki.
import { KAYIT, NOKTA, ELLE } from "./kayit.mjs";

export const satir = (t) => t.split("\n").filter((s) => s.trim()).length;
export const METIN = Object.fromEntries(KAYIT.split(/^# nokta /m).filter(Boolean)
  .map((p) => [p.split("\n")[0], p.split("\n").slice(1).join("\n").trim()]));
export const K = Object.fromEntries(NOKTA.map((n) => [n, METIN[n].split("\n").reduce((b, s) => {
  const k = s.match(/^kaynak (\S+) (\S+) \{$/), f = s.match(/^ {2}([\w-]+) = (.+)$/);
  if (k) b.push({ tur: k[1], ad: k[2], alan: [] }); else if (f) b.at(-1).alan.push([f[1], f[2]]);
  return b;
}, [])]));
const D = Object.fromEntries(NOKTA.map((n) => [n, Object.fromEntries(K[n].flatMap((b) => b.alan))]));

export const MODUL = `girdi kaynak-blok
girdi sayi = 6
girdi boyut-gb = 500
girdi kapi = 8080
kaynak sanal-makine-kumesi $ad-toplayici {
  sayi = $sayi
  imaj = toplayici-4.2.0
  bolge = $ad-1
}
kaynak disk $ad-tampon {
  boyut-gb = $boyut-gb
  baglanan = $ad-toplayici
}
kaynak ag-kurali $ad-giris {
  kaynak-blok = $kaynak-blok
  kapi = $kapi
  yon = giris
}
cikti tampon-adi = $ad-tampon
cikti giris-kapisi = $kapi`;                           // elle yazildi; cikti secimi kayitta yok

export const uret = (mod, ad, arg) => {                // $ad cagri adidir; degeri `yok` olan alan yazilmaz
  const isim = [...mod.matchAll(/^girdi ([\w-]+)/gm)].map((m) => m[1]).sort((a, b) => b.length - a.length);
  const c = []; let atla = false;
  for (const s of mod.split("\n")) {
    if (/^(girdi|cikti) /.test(s)) continue;
    let t = s.replaceAll("$ad", ad);
    for (const i of isim) t = t.replaceAll("$" + i, arg[i]);
    const k = t.match(/ eger (\S+) \{$/);
    if (k) { atla = k[1] === "hayir"; t = t.replace(/ eger \S+ \{$/, " {"); }
    else if (t === "}" && atla) { atla = false; continue; }
    if (!atla && !/ = yok$/.test(t)) c.push(t);
  }
  return c.join("\n");
};
export const zorunlu = (m) => [...m.matchAll(/^girdi ([\w-]+)$/gm)].map((x) => x[1]);
export const varsayilan = (m) => Object.fromEntries([...m.matchAll(/^girdi ([\w-]+) = (.+)$/gm)].map((x) => [x[1], x[2]]));
export const gecen = (m, n) => {                       // cagriya zorunlu ve varsayilandan sapan alan yazilir
  const v = varsayilan(m);
  return Object.fromEntries([...zorunlu(m), ...Object.keys(v)].filter((a) => D[n][a] !== v[a]).map((a) => [a, D[n][a]]));
};
export const cs = (m, n) => Object.keys(gecen(m, n)).length + 2;   // cagrinin satir sayisi

if (import.meta.main) {                                // surum.mjs iceri aktarinca bu bolum kosmaz
  const alan = Object.keys(D[NOKTA[0]]), v = varsayilan(MODUL);
  const degis = alan.filter((a) => new Set(NOKTA.map((n) => D[n][a])).size > 1);
  const ad = [...zorunlu(MODUL), ...Object.keys(v)], disi = degis.filter((a) => !ad.includes(a));
  const ayni = NOKTA.filter((n) => uret(MODUL, n, { ...v, ...gecen(MODUL, n) }) === METIN[n]).length;
  const el = NOKTA.reduce((t, n) => t + satir(METIN[n]), 0), c = NOKTA.map((n) => cs(MODUL, n));
  const t = c.reduce((x, y) => x + y);
  console.log(`alan yuvasi ${alan.length} | degisen ${degis} | sabit ${alan.length - degis.length}` +
    ` | girdi yuzeyi ${ad.length} = zorunlu ${zorunlu(MODUL)} + varsayilanli ${Object.keys(v)}`);
  console.log(`degisip arayuzde olmayan ${disi.length} (${disi}) | modul ${satir(MODUL)} = ${ad.length}+` +
    `${satir(MODUL) - ad.length - 2}+2 | elle ${el} | modul yolu ${satir(MODUL)}+${t}=${satir(MODUL) + t}` +
    ` | kazanc ${el - satir(MODUL) - t} | cagri ${c} | asliyla ayni ${ayni}/3`);
  let kap = 0;
  for (const [n, tur, a, d] of ELLE) {                 // kayit ile gercek arasindaki fark
    const b = K[n].find((x) => x.tur === tur), y = b?.alan.find(([x]) => x === a);
    console.log(`  ${n.padEnd(6)} ` + (!b ? `kayit disi kaynak ${d}` : !y ? `kayitta olmayan alan ${a} = ${d}`
      : `deger farki ${a}: kayit ${y[1]}, gercek ${d}`).padEnd(44) + (b ? "kapsamda" : "kapsam disi"));
    if (b) kap++;
  }
  console.log(`kayitta yazmayan kalem ${ELLE.length} | modulu yazanin gordugu 0 | geri alinacak ${kap}` +
    ` | kayitsiz kalan ${ELLE.length - kap}`);
}
```

```
alan yuvasi 8 | degisen sayi,bolge,boyut-gb,baglanan,kaynak-blok,kapi | sabit 2 | girdi yuzeyi 4 = zorunlu kaynak-blok + varsayilanli sayi,boyut-gb,kapi
degisip arayuzde olmayan 2 (bolge,baglanan) | modul 20 = 4+14+2 | elle 42 | modul yolu 20+12=32 | kazanc 10 | cagri 5,3,4 | asliyla ayni 3/3
  bati   deger farki boyut-gb: kayit 500, gercek 750 kapsamda
  kuzey  deger farki sayi: kayit 4, gercek 6         kapsamda
  dogu   kayitta olmayan alan ek-kapi = 9443         kapsamda
  kuzey  kayit disi kaynak kuzey-arsiv               kapsam disi
kayitta yazmayan kalem 4 | modulu yazanin gordugu 0 | geri alinacak 3 | kayitsiz kalan 1
```

Girdi yüzeyi dörttür: bir zorunlu, üç varsayılanlı. Modül yirmi satırdır; dördü arayüz bildirimi,
on dördü gövde, ikisi çıktıdır.

İlk satır arayüzün eksik yanını sayar. Sekiz alan yuvasının altısı üç kayıt arasında değişiyor ama
arayüzde yalnız dördü var; **değişip arayüzde olmayan iki alan** `bolge` ile `baglanan`. İkisi de
`$ad` üzerinden yazıldığı için ölçüm onları ayırt edemiyor. Oysa `baglanan` haklı olarak dışarıdadır:
bir bağımlılık adıdır, modülün kendi ürettiği kaynağa işaret eder. `bolge` öyle değil — bölge
değerinin nokta adıyla başlaması bir kural değil, üç örnekte tutan bir kalıptır. Modülü yazan taraf
kalıba güvenip alanı arayüzden sakladı; bedeli bir sonraki bölümde ödenir.

Ters yönde `cikti` durur. Hangi kaynağın adının dışarıya verileceği üç kaydın hiçbir yerinde yazmaz.
Girdi kaydın farkından çıkarılabilir, **çıktı çıkarılamaz.**

Çağrı satırları beş, üç ve dörttür. Batı üç satırla çağrılıyor çünkü üç varsayılanın üçü de batının
kaydına uyuyor; varsayılan seçimi çoğunluğu ödüllendirir, azınlıkta kalana bir satır fazladan
yazdırır. Satır kazancı kırk ikiden otuz ikiye, yani yüzde yirmi dörttür — üç kullanımda tek başına
modül yazdıracak bir sayı değil.

## Kaydın Kapsamadığı Dört Kalem

Modül üç kaydı birebir yeniden üretti, ama gerçekte var olan dört kalemin sıfırını gördü: bakılan
yer kayıttı. İkisi **değer farkıdır** — batının diski beş yüz yerine yedi yüz elli gigabayt,
kuzeyin makine kümesi dört yerine altı. Biri **kayıtta hiç olmayan alandır**: doğunun ağ kuralına
elle ikinci bir kapı açılmış. Sonuncusu **kayıt dışı kaynaktır**: kuzeydeki `kuzey-arsiv` nesne
deposundan hiçbir kayıt söz etmiyor.

Modülün kapsamı bu dördü ikiye böler. Üçü modülün ürettiği blokların içindedir; uygulandığı anda
disk beş yüze, makine sayısı dörde döner ve fazladan kapı silinir — modülleştirme bu üç kalemi
görmedi ama uygulama onları **sessizce geri alacak**. Dördüncüsü modülün hiç bilmediği bir kaynak
türündedir; modül onu ne üretir ne siler. Modülleştirme sapmayı azaltmaz: kapsadığını tek bir
yerden geri alınabilir kılar, kapsamadığını olduğu yerde bırakır.

## Esneklik Bedeli

```js
// surum.mjs — modul bes yeni kullanimla alti surume cikar; girdi yuzeyi ve esneklik bedeli sayilir.
import { MODUL, uret, satir, zorunlu, varsayilan } from "./modul.mjs";

const ek = (m, ...s) => m.replace(/^(girdi .*)$(?![\s\S]*^girdi )/m, ["$1", ...s].join("\n"));
const SURUM = [                                        // her surumun getirdigi degisiklik onceden yazili
  ["v1", "elle yazildi", ["kuzey", "bati", "dogu"], (m) => m],
  ["v2", "bolge adi kalibi kirildi", ["guney"], (m) =>
    ek(m.replace("  bolge = $ad-1", "  bolge = $bolge"), "girdi bolge")],
  ["v3", "disk istemeyen kullanim", ["sinama-alani"], (m) =>
    ek(m.replace("k disk $ad-tampon {", "k disk $ad-tampon eger $disk-var {"), "girdi disk-var = evet")],
  ["v4", "cikis yonu ve ikinci kapi", ["saha-laboratuvari"], (m) => ek(m.replace("  yon = giris", "  yon = $yon")
    .replace("  kapi = $kapi\n", "  kapi = $kapi\n  ek-kapi = $ek-kapi\n"), "girdi yon = giris", "girdi ek-kapi = yok")],
  ["v5", "eski imaji isteyen kullanim", ["arsiv-nokta"], (m) =>
    ek(m.replace("  imaj = toplayici-4.2.0", "  imaj = $imaj"), "girdi imaj = toplayici-4.2.0")],
  ["v6", "makine etiketi", ["ic-anadolu"], (m) =>
    ek(m.replace("  sayi = $sayi\n", "  sayi = $sayi\n  etiket = $etiket\n"), "girdi etiket = yok")]];

const HAM = {                                          // her kullanimin module gectigi degerler
  kuzey: "kaynak-blok=10.20.0.0/16 bolge=kuzey-1 sayi=4 boyut-gb=200",
  bati: "kaynak-blok=10.30.0.0/16 bolge=bati-1",
  dogu: "kaynak-blok=10.40.0.0/16 bolge=dogu-1 kapi=8443",
  guney: "kaynak-blok=10.50.0.0/16 bolge=guney-2",
  "sinama-alani": "kaynak-blok=10.60.0.0/16 bolge=sinama-1 disk-var=hayir sayi=1",
  "saha-laboratuvari": "kaynak-blok=10.70.0.0/16 bolge=saha-1 yon=cikis ek-kapi=9443 sayi=1",
  "arsiv-nokta": "kaynak-blok=10.80.0.0/16 bolge=arsiv-1 imaj=toplayici-3.9.0 boyut-gb=2000",
  "ic-anadolu": "kaynak-blok=10.90.0.0/16 bolge=ic-1 etiket=pilot sayi=2" };
const G = Object.fromEntries(Object.entries(HAM).map(([k, v]) =>
  [k, Object.fromEntries(v.split(" ").map((x) => x.split("=")))]));

let m = MODUL, acik = [], onceki = 1;
console.log("surum  degisiklik                   kullanim  girdi (zorunlu/varsayilanli)  modul satir  kirici duzeltme");
for (const [s, not, yeni, d] of SURUM) {
  m = d(m);
  const zor = zorunlu(m).length, top = zor + Object.keys(varsayilan(m)).length;
  const kirici = zor > onceki ? acik.length : 0;       // yeni zorunlu girdi eski cagrilari kirar
  acik = [...acik, ...yeni];
  console.log(`${s}     ${not.padEnd(28)} ${String(acik.length).padEnd(9)} ${`${top} (${zor}/${top - zor})`.padEnd(28)} ` +
    `${String(satir(m)).padEnd(12)} ${kirici ? `${kirici} tanim, ${kirici} satir` : "-"}`);
  onceki = zor;
}
acik = acik.filter((a) => a !== "arsiv-nokta");        // bir kullanim kapandi, actirdigi girdi modulde kaldi
const vd = varsayilan(m), vars = Object.keys(vd);
const kac = (a) => acik.filter((k) => a in G[k]).length;
const olu = vars.filter((a) => kac(a) === 0), tek = vars.filter((a) => kac(a) === 1);
const el = acik.reduce((t, a) => t + satir(uret(m, a, { ...vd, ...G[a] })), 0);
const cs = acik.reduce((t, a) => t + Object.keys(G[a]).length + 2, 0);

console.log(`acilan 8, kapanan 1, acik ${acik.length} | modulu degistirmeden karsilanan 0/5 | girdi yuzeyi 4 -> ` +
  `${vars.length + 2}, kullanim basina ${((vars.length - 2) / 5).toFixed(1)} | elle ${el} | modul yolu ` +
  `${satir(m)}+${cs}=${satir(m) + cs} | kazanc ${el - satir(m) - cs}`);
console.log(`varsayilanli girdi ${vars.length}: iki ve uzeri ${vars.length - tek.length - olu.length}` +
  `, tek kullanim ${tek.length} (${tek}), olu ${olu.length} (${olu})`);
```

```
surum  degisiklik                   kullanim  girdi (zorunlu/varsayilanli)  modul satir  kirici duzeltme
v1     elle yazildi                 3         4 (1/3)                      20           -
v2     bolge adi kalibi kirildi     4         5 (2/3)                      21           3 tanim, 3 satir
v3     disk istemeyen kullanim      5         6 (2/4)                      22           -
v4     cikis yonu ve ikinci kapi    6         8 (2/6)                      25           -
v5     eski imaji isteyen kullanim  7         9 (2/7)                      26           -
v6     makine etiketi               8         10 (2/8)                     28           -
acilan 8, kapanan 1, acik 7 | modulu degistirmeden karsilanan 0/5 | girdi yuzeyi 4 -> 10, kullanim basina 1.2 | elle 96 | modul yolu 28+38=66 | kazanc 30
varsayilanli girdi 8: iki ve uzeri 1, tek kullanim 6 (boyut-gb,kapi,disk-var,yon,ek-kapi,etiket), olu 1 (imaj)
```

Esnekliğin bedeli tek satırda okunur: **beş yeni kullanımın sıfırı modülü değiştirmeden
karşılandı.** Her yeni kullanım arayüzde yeni bir girdi açtırdı; girdi yüzeyi dörtten ona, modül
metni yirmiden yirmi sekiz satıra çıktı. Ölçülen hız kullanım başına 1,2 girdidir.

İkinci sürümdeki `kirici duzeltme` sütunu bedelin en görünür biçimidir. `bolge` alanının ad kalıbı
dördüncü noktada kırılınca alan zorunlu girdiye çıktı ve o anda var olan üç tanımın **üçü birden**
geçersiz oldu. Yeni bir zorunlu girdi açmanın yükü modülü yazanın değil, kullananların üstündedir.

Son satır esnekliğin kime hizmet ettiğini sayar. Sekiz varsayılanlı girdinin yalnız biri iki ya da
daha çok kullanım tarafından geçiliyor; altısı tek bir kullanım için var, biri hiçbiri için. Ölü
girdi olan `imaj` beşinci sürümde arşiv noktası eski bir imaj istediği için açıldı; arşiv noktası
kapandı, girdi modülde kaldı. Bir girdiyi kaldırmak kullanan her tanımı ilgilendirdiği için kimse
kaldırmaya girişmez.

Kazanç yine de büyüdü. Yedi açık kullanım elle yazılsaydı doksan altı satır tutardı; modül yolu
altmış altı satır ve kazanç yüzde otuz bir. Kazanç kullanım sayısıyla büyür, arayüz de büyür, ikisi
aynı hızda gitmez.

## Özet

- Elle yazılan modülün girdi yüzeyi 4'tür: 1 zorunlu, 3 varsayılanlı; 20 satırın 4'ü arayüz
  bildirimi, 2'si çıktıdır ve çıktı seçimi kayıttan okunamaz.
- 8 alan yuvasının 6'sı üç kayıt arasında değişiyor ama arayüzde 4'ü var; `bolge` bir ad kalıbına
  güvenildiği için dışarıda kaldı.
- Satır kazancı üç kullanımda 42'den 32'ye (yüzde 24), yedi kullanımda 96'dan 66'ya (yüzde 31)
  çıktı.
- Modül üç kaydı birebir yeniden üretti (3/3) ama gerçekte var olan 4 kalemin 0'ını gördü;
  uygulamada 3'ü sessizce geri alınır, kayıt dışı `kuzey-arsiv` kayıtsız kalır.
- 5 yeni kullanımın 0'ı modülü değiştirmeden karşılandı; girdi yüzeyi 4'ten 10'a çıktı, 8
  varsayılanlı girdinin 6'sı tek kullanıma, 1'i hiçbirine hizmet ediyor.
- Yeni bir zorunlu girdi açmak kırıcıdır: ikinci sürüm o anki 3 tanımın 3'ünde 1'er satır düzeltme
  istedi; bu yük kullananların üstündedir.

## Sonraki Adım

Bu modülün altı sürümü de aynı depoda, aynı elin altında durdu; yazan da kullanan da aynı taraftı.
Modüller paylaşılan bir yerden çekilmeye başladığında bu ilişki kopar: yükseltme kararı bir yerde
alınır, başka yerlerde hissedilir. Bir sonraki ders sürümü sabitlenmiş ve sabitlenmemiş iki düzeni
bir dönem boyunca koşturur ve şunu sayar: tanım dosyası hiç değişmediği hâlde kaç uygulama farklı
sonuç üretiyor, bir yükseltme kaç kullanıcıya dokunuyor, kırıcı bir değişiklik kaç tanımda kaç satır
düzeltme istiyor. Sabitlemenin bedeli de aynı yerde ölçülür: kaç modül eski sürümde kalıyor ve kaç
düzeltmeyi hiç almıyor.
