---
title: 'Hat Kodu Olarak Yapılandırma'
source: 'https://academia.sh/tr/kurslar/ci-cd/hat-kodu-olarak-yapilandirma'
course: 'Sürekli Tümleştirme ve Teslimat'
language: tr
updated: '2026-08-17T18:09:03+00:00'
license: 'CC BY-SA 4.0'
---

# 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.

Ö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.

```js
// 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.

```js
// 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ı `paketleme`ye 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.

```js
// 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.
