---
title: 'Yapı Çıktısı ve Değişmezlik'
source: 'https://academia.sh/tr/kurslar/devops-temelleri/yapi-ciktisi-ve-degismezlik'
course: 'DevOps Kültürü ve Temelleri'
language: tr
updated: '2026-08-17T18:09:05+00:00'
license: 'CC BY-SA 4.0'
---

# Yapı Çıktısı ve Değişmezlik

Bir kez üret her yere dağıt ilkesi ölçülür: aynı kaynaktan iki kez üretilen çıktının özeti karşılaştırılır, yeniden üretilebilirliği bozan dört kaynak tek tek kapatılır ve ortam başına yeniden derlemenin doğurduğu ayrı ikili sayısı tek çıktı düzeniyle karşılaştırılır.

Önceki ders dört ortamı üretime giden zincirin halkaları olarak ölçtü ve her halkanın yalnız
üretimle aynı olduğu boyutlardaki kusurları görebildiğini saydı. O ölçüm bir şeyi varsaydı:
halkalar arasında yolculuk eden nesne hep aynı nesnedir. Sınamada doğrulanan şey ile üretimde
koşan şey aynı değilse, sınamanın verdiği güvence de o kadar azalır.

Bu ders o nesneyi adlandırır. **Yapı çıktısı** (build artifact), kaynaktan üretilen ve
dağıtılabilen tek nesnedir: bir arşiv, bir paket, çalıştırılabilir tek bir dosya. Sorular
ölçülebilir: aynı kaynaktan iki kez üretilen çıktı gerçekten aynı mı, değilse farkı nereden
alıyor, ve her ortam için yeniden derlemek kaç ayrı nesne doğuruyor?

**DK7.** Aşağıdaki yapı adımı bir modeldir: üç kaynak dosya okunur ve tek dosyada birleştirilir.
Gerçek bir derleyicinin yaptığı iş değildir, ama yeniden üretilebilirlik açısından aynı
kırılganlıkları taşır. **DK8.** İki makine künyeyle modellenir — `A` yazan ekibin makinesi,
`B` yapıyı çalıştıran koşucu — ve saat de künyede yazılıdır ki ölçüm yinelenebilsin. `A1` ve
`A2` aynı makinenin iki ayrı koşumudur. **DK9.** Özet, dosyanın sha256 değerinin ilk on altı
onaltılık basamağıdır; kısaltma yalnız okunabilirlik içindir.

## Bir Kez Üret, Her Yere Dağıt

Değişmezlik ilkesi tek cümledir: **bir yapı çıktısı bir kez üretilir, üretildikten sonra
değiştirilmez ve her ortama aynısı dağıtılır.** Üretimdeki bir dosyayı yerinde yamalamak,
sınamada doğrulanmış nesneyi bilinmeyen bir nesneye çevirir; düzeltme gerekiyorsa yeni bir
çıktı üretilir ve yeni bir özet doğar. Geri alma da aynı kuralla çalışır: önceki çıktı yeniden
dağıtılır, mevcut çıktı geri düzenlenmez.

İlkenin ihlali de somuttur. Üretimde tek bir dosyanın elle düzeltilmesi, o ortamda artık hiçbir
yerde kaydı olmayan yeni bir nesne yaratır: zincirde bir nesne dolaşırken üretimde ikincisi
durur, ikincisinin özeti hiçbir dağıtım kaydında yazmaz ve ortam künyesi bunu göstermez. Bir
sonraki dağıtım bu düzeltmeyi sessizce siler; ortadan kalkan davranışın nedeni de aranacak
yerlerin hiçbirinde bulunmaz. Yerinde yamalamanın bedeli budur: fark, ölçüm alanının tümüyle
dışında bir yere saklanır.

İlkenin işe yaraması tek koşula bağlıdır: çıktının **kimliğinin** olması. Kimlik, içeriğin
özetidir. İki çıktının aynı olup olmadığı ancak özetleri karşılaştırılarak söylenebilir; dosya
adı, üretim tarihi ya da sürüm etiketi bunu söylemez. Bu yüzden ölçüm özetten başlar.

```js
// olcum-agi/yapi.mjs — kaynagi diske yazar, tek dosyalik yapi ciktisi uretir (model)
import { mkdirSync, writeFileSync, readFileSync, rmSync } from "node:fs";
import { createHash } from "node:crypto";

const KAYNAK = "kaynak";

export const kaynagiYaz = () => {
  rmSync(KAYNAK, { recursive: true, force: true });
  mkdirSync(KAYNAK, { recursive: true });
  writeFileSync(`${KAYNAK}/okuma-cozumleyici.js`, "export const cozumle = (s) => Number(s.slice(4));\n");
  writeFileSync(`${KAYNAK}/fatura-kurallari.js`, "export const tutar = (m3) => m3 * 7.4;\n");
  writeFileSync(`${KAYNAK}/is-emri.js`, "export const emirAc = (no) => ({ no, tur: 'yerinde' });\n");
};

// Makine kunyesi: saat de kunyede yazilidir, boylece olcum yinelenebilir.
export const A1 = { ad: "A1", saat: "21:04:07", dizin: "/ev/yazan/olcum-agi", yapan: "yazan-ekip",
  okumaSirasi: ["okuma-cozumleyici.js", "fatura-kurallari.js", "is-emri.js"] };
export const A2 = { ...A1, ad: "A2", saat: "21:19:52" };
export const B = { ad: "B", saat: "22:41:30", dizin: "/opt/kosucu/is-1873", yapan: "hat-kosucusu",
  okumaSirasi: ["is-emri.js", "fatura-kurallari.js", "okuma-cozumleyici.js"] };

export const DORT_KAYNAK = ["zamanDamgasi", "dizinSirasi", "ortamDegiskeni", "mutlakYol"];
export const yalniz = (ad) => Object.fromEntries(DORT_KAYNAK.map((k) => [k, k === ad]));

export const yapiYap = (kunye, secenek, hedef, gomulu = null) => {
  const dosyalar = secenek.dizinSirasi ? kunye.okumaSirasi : [...kunye.okumaSirasi].sort();
  const baslik = [];
  if (secenek.zamanDamgasi) baslik.push(`// uretim ani: ${kunye.saat}`);
  if (secenek.ortamDegiskeni) baslik.push(`// yapan: ${kunye.yapan}`);
  if (secenek.mutlakYol) baslik.push(`// kaynak koku: ${kunye.dizin}/${KAYNAK}`);
  if (gomulu) baslik.push(`export const AYAR = ${JSON.stringify(gomulu)};`);
  const govde = dosyalar.map((d) => readFileSync(`${KAYNAK}/${d}`, "utf8")).join("");
  mkdirSync("dagitim", { recursive: true });
  writeFileSync(`dagitim/${hedef}`, baslik.join("\n") + (baslik.length ? "\n" : "") + govde);
  return createHash("sha256").update(readFileSync(`dagitim/${hedef}`)).digest("hex").slice(0, 16);
};
```

## Yeniden Üretilebilirliği Bozan Dört Kaynak

**DK10.** Yapı adımına dört ayrı bozucu kaynak yerleştirilmiştir ve her biri ayrı ayrı
kapatılabilir: üretim anının çıktıya yazılması (`zamanDamgasi`), kaynak dosyaların dizin okuma
sırasıyla birleştirilmesi (`dizinSirasi`), yapıyı çalıştıranın adının gömülmesi
(`ortamDegiskeni`) ve kaynak kökünün mutlak yolunun yazılması (`mutlakYol`). Dördü de gerçek
yapı adımlarında karşılaşılan kaynaklardır.

Tarama üç koşum yapar — aynı makinede iki an (`A1`, `A2`) ve ikinci makine (`B`) — ve her
seçenek kümesi için kaç ayrı özet doğduğunu sayar.

```js
// olcum-agi/tara.mjs — yeniden uretilebilirligi bozan kaynaklari tek tek tarar
import { kaynagiYaz, yapiYap, yalniz, DORT_KAYNAK, A1, A2, B } from "./yapi.mjs";

kaynagiYaz();
const hepsi = Object.fromEntries(DORT_KAYNAK.map((k) => [k, true]));
const hicbiri = Object.fromEntries(DORT_KAYNAK.map((k) => [k, false]));

const ozetler = (secenek) =>
  [A1, A2, B].map((k) => yapiYap(k, secenek, `paket-${k.ad}.js`));

const satir = (etiket, secenek) => {
  const [a1, a2, b] = ozetler(secenek);
  const ayri = new Set([a1, a2, b]).size;
  console.log(etiket.padEnd(24) + (a1 === a2 ? "AYNI   " : "FARKLI ").padEnd(12) +
    (a1 === b ? "AYNI   " : "FARKLI ").padEnd(11) + ayri);
};

console.log("etkin kaynak".padEnd(24) + "A1-A2".padEnd(12) + "A1-B".padEnd(11) + "ayri ozet");
satir("dordu birden", hepsi);
for (const k of DORT_KAYNAK) satir("yalniz " + k, yalniz(k));
satir("hicbiri", hicbiri);

const [a1, a2, b] = ozetler(hicbiri);
console.log(`\ndort kaynak kapatildiginda uc kosumun ozeti: ${a1}, ${a2}, ${b}`);
console.log("ayni girdi, ayni cikti: " + (a1 === a2 && a2 === b));
```

```
etkin kaynak            A1-A2       A1-B       ayri ozet
dordu birden            FARKLI      FARKLI     3
yalniz zamanDamgasi     FARKLI      FARKLI     3
yalniz dizinSirasi      AYNI        FARKLI     2
yalniz ortamDegiskeni   AYNI        FARKLI     2
yalniz mutlakYol        AYNI        FARKLI     2
hicbiri                 AYNI        AYNI       1

dort kaynak kapatildiginda uc kosumun ozeti: f8aa0bd8108fe25b, f8aa0bd8108fe25b, f8aa0bd8108fe25b
ayni girdi, ayni cikti: true
```

Tablo iki şeyi ayırır. Birincisi: **dört kaynağın her biri tek başına yeniden
üretilebilirliği bozmaya yeter.** Yalnız biri etkinken bile ikinci makinede üretilen çıktının
özeti farklıdır; aynı kaynaktan üretilmiş iki nesnenin aynı olduğu söylenemez.

İkincisi ve daha önemlisi: dört kaynağın üçü **aynı makinedeki iki koşumda görünmez.** Dizin
okuma sırası, ortam değişkeni ve mutlak yol, tek makinede sabit kaldıkları için `A1` ile `A2`
arasında fark üretmez. Yazan ekip kendi makinesinde yapıyı iki kez çalıştırıp aynı özeti görür
ve yapının yeniden üretilebilir olduğu sonucuna varır. Fark, çıktı ikinci bir makinede
üretildiği anda ortaya çıkar. Önceki dersin ayrımı burada da geçerlidir: bildirilmiş bir fark
değildir bu, yalnız koşumda — hem de başka bir makinedeki koşumda — görünen bir farktır ve
gizlediği hata sınıfının adı **doğrulanmamış ikilidir**: sınamadan geçen nesnenin üretime giden
nesne olduğu ölçülmemiştir.

Dördü de kapatıldığında üç koşum tek özet üretir. Bu, özetin artık koşumu değil **girdiyi**
adlandırdığı anlamına gelir: aynı özet, aynı kaynak. Kaynakların kapatılma yolları da bellidir
ve dördü de yapı adımının içindedir — zaman damgasını çıktıya yazmamak, dosyaları dizin okuma
sırasıyla değil ada göre sıralamak, yapıyı çalıştıranın kimliğini gömmemek ve yolları kaynak
köküne göre göreli tutmak.

Özetin ne söylediği ile ne söylemediği de ayrılmalıdır. Özet **bayt kimliğini** söyler: iki
çıktının özeti aynıysa iki nesne aynıdır, dağıtım kaydında biri diğerinin yerine yazılabilir.
Söylemediği iki şey vardır. Birincisi, çıktının hangi kaynaktan üretildiğini söylemez; özet
çıktının kendisinden hesaplanır, kaynağın kimliğinden değil. Yapı adımı yeniden üretilebilir
olduğunda bu boşluk kapanabilir: aynı kaynaktan yapıyı ikinci kez çalıştıran bir başkası aynı
özeti elde eder ve iddia sınanabilir hâle gelir — yeniden üretilebilirliğin düzenden öte
değeri buradadır. İkincisi, özet farkının **anlamlı** bir davranış farkı olduğunu söylemez;
çıktıya eklenen tek bir açıklama satırı da özeti baştan sona değiştirir. Farklı özet, bir kanıt
değil, incelenmesi gereken bir işarettir.

## Ortam Başına Derleme ile Tek Çıktı

Zincirin her halkası için ayrı derleme yapmanın somut karşılığı vardır: ortama göre değişen
değerler yapı adımında çıktının içine yazılır. **DK11.** Kurgudaki ağda bu değerler dörttür —
ortam adı, fatura kapısının gerçek mi taklit mi olduğu, kütük düzeyi ve gecelik toplu işin
yığın eşiği. **DK12.** Tek çıktı düzeninde aynı dört değer açılışta ortamdan okunur; okumanın
nasıl yapıldığı ve hangi değerin sır sayıldığı sonraki dersin konusudur.

```js
// olcum-agi/ortam-basina.mjs — ortam basina yeniden derleme ile tek ciktiyi karsilastirir
import { kaynagiYaz, yapiYap, DORT_KAYNAK, A1, A2, B } from "./yapi.mjs";

const AYAR = {
  gelistirme: { ortamAdi: "gelistirme", faturaKapisi: "taklit", kutuk: "ayrintili", esik: 500 },
  sinama: { ortamAdi: "sinama", faturaKapisi: "taklit", kutuk: "bilgi", esik: 20000 },
  uretim: { ortamAdi: "uretim", faturaKapisi: "gercek", kutuk: "uyari", esik: 250000 },
};
const ORTAM = Object.keys(AYAR);
const hicbiri = Object.fromEntries(DORT_KAYNAK.map((k) => [k, false]));
const hepsi = Object.fromEntries(DORT_KAYNAK.map((k) => [k, true]));
kaynagiYaz();

console.log("gomulu deger anahtarlari: " + Object.keys(AYAR.uretim).join(", "));
const ayri = ORTAM.map((o) => [o, yapiYap(A1, hicbiri, `paket-${o}.js`, AYAR[o])]);
for (const [o, ozet] of ayri) {
  const fark = Object.keys(AYAR[o]).filter((k) => AYAR[o][k] !== AYAR.uretim[k]);
  console.log(`${o.padEnd(11)} ozet ${ozet}  uretim ikilisinden farkli deger ${fark.length}`);
}
console.log(`ortam basina derleme: ${new Set(ayri.map(([, h]) => h)).size} ayri ikili, ` +
  `ikililere gomulu deger ${ORTAM.length * Object.keys(AYAR.uretim).length}`);

const tek = ORTAM.map((o) => yapiYap(A1, hicbiri, `paket-tek-${o}.js`));
console.log(`tek cikti duzeni    : ${new Set(tek).size} ayri ikili, ozet ${tek[0]}, gomulu deger 0`);

console.log("\nortam basina derleme farkli makinelerde ve anlarda yapilirsa:");
const gurultu = [[A1, "sinama"], [B, "uretim"], [A2, "uretim"]]
  .map(([k, o]) => [`${o} @ ${k.ad}`, yapiYap(k, hepsi, `paket-g-${o}-${k.ad}.js`, AYAR[o])]);
for (const [ad, ozet] of gurultu) console.log("  " + ad.padEnd(14) + "ozet " + ozet);
console.log("  ayni ortam icin iki kosum ayni ozeti verdi: " + (gurultu[1][1] === gurultu[2][1]));
```

```
gomulu deger anahtarlari: ortamAdi, faturaKapisi, kutuk, esik
gelistirme  ozet e9b4bbcaec4a64a4  uretim ikilisinden farkli deger 4
sinama      ozet 3cb0ab9967d14e1a  uretim ikilisinden farkli deger 4
uretim      ozet 440bf8366e892e51  uretim ikilisinden farkli deger 0
ortam basina derleme: 3 ayri ikili, ikililere gomulu deger 12
tek cikti duzeni    : 1 ayri ikili, ozet f8aa0bd8108fe25b, gomulu deger 0

ortam basina derleme farkli makinelerde ve anlarda yapilirsa:
  sinama @ A1   ozet b47af357c8f24d3c
  uretim @ B    ozet 9ee506035c8f4235
  uretim @ A2   ozet 19f12b586982fdc3
  ayni ortam icin iki kosum ayni ozeti verdi: false
```

Ortam başına derleme **üç ayrı ikili** doğurur ve bu üç ikilinin içinde toplam **on iki gömülü
değer** taşınır. Sınamanın onayladığı nesne, üretime giden nesneden dört değerde ayrılır: ortam
adı, fatura kapısı, kütük düzeyi ve toplu iş eşiği. Bu dördü çıktının **içinde** durur; dışarıdan
bakan bir kişi üç dosyanın da aynı adı taşıdığını görür, farkı yalnız özet söyler. Fatura
kapısının sınama ikilisinde taklit, üretim ikilisinde gerçek olması, önceki dersin sözleşme
sapması sınıfını sınamanın hiçbir koşumunda görünmez kılar.

Tek çıktı düzeninde üç ortam **tek ikili** paylaşır: özet üçünde de aynıdır ve gömülü değer
sayısı sıfırdır. On iki değer yok olmaz, yer değiştirir — çıktının içinden ortamın künyesine
geçer. Önceki dersin ölçüsüyle söylenirse: bildirilmemiş, yalnız koşumda görünen on iki fark,
bildirilmiş on iki farka dönüşür. Farkın miktarı aynı kalır, saklandığı yer değişir; ölçülebilir
hâle gelen de budur.

Son üç satır ortam başına derlemenin ikinci bedelini gösterir. Yapılar farklı makinelerde ve
farklı anlarda koştuğunda, aynı ortam için üretilen iki çıktı bile farklı özet taşır. Özet artık
ortamı adlandırmaz; koşumu adlandırır. İki özet farklı olduğunda bunun nedeninin ortam farkı mı
yoksa yapı gürültüsü mü olduğu ayırt edilemez ve "üretimde koşan şey sınamada doğrulanan şey mi"
sorusunun yanıtı ölçülemez hâle gelir.

Çıktının gerçekten diskte durduğu ve özetin o dosyanın özeti olduğu ayrıca doğrulanabilir.

```bash
# dogrula.sh — diskteki cikti gercekten var mi, ozeti node'un bastigiyla ayni mi
ls dagitim/paket-tek-*.js
shasum -a 256 dagitim/paket-tek-uretim.js | cut -c1-16
cat dagitim/paket-tek-uretim.js
```

```
dagitim/paket-tek-gelistirme.js
dagitim/paket-tek-sinama.js
dagitim/paket-tek-uretim.js
f8aa0bd8108fe25b
export const tutar = (m3) => m3 * 7.4;
export const emirAc = (no) => ({ no, tur: 'yerinde' });
export const cozumle = (s) => Number(s.slice(4));
```

Üç ortam için yazılan dosyaların içeriği aynıdır ve kabuktan alınan özet, `node` sürecinin
bastığı `f8aa0bd8108fe25b` değeriyle örtüşür. Dosya adları farklı olsa da nesne tektir; bu,
değişmezlik ilkesinin ölçülmüş hâlidir. Dosyaların birleşme sırasının ada göre olduğu da
görülür — dizin okuma sırası kapatıldığı için.

## Zincirde Kaç Nesne Dolaşıyor

Önceki dersin zinciri bir nesnenin dört durakta ilerlemesiydi. Ölçüm iki düzenin bu zincire ne
verdiğini de sayar. Ortam başına derlemede zincirde üç ayrı nesne dolaşır ve üç ayrı yapı koşumu
yapılır; her koşum, taramada sayılan dört bozucu kaynağın önüne yeniden açıktır. Tek çıktı
düzeninde nesne birdir, koşum birdir ve halkalar arası geçiş bir kopyalama işlemine iner.

Fark, sorulabilecek soruların sayısında görünür. Tek çıktı düzeninde "sınamada doğrulanan nesne
üretime giden nesne mi" sorusu tek bir özet karşılaştırmasıyla yanıtlanır: iki değer eşitse evet,
değilse hayır. Ortam başına derlemede aynı soru sorulamaz bile, çünkü iki ortamın özeti zaten
tanım gereği farklıdır ve farkın hangi kısmının ortam ayarından, hangi kısmının yapı
gürültüsünden geldiği ayrıştırılamaz. Ölçülebilir bir güvence, ölçülemeyen bir alışkanlığa
dönüşür.

Ayrım teslimat ölçütlerine de yazılır. Bir düzeltmenin üretime ulaşması ortam başına derlemede
üç yapı koşumu gerektirir; üçünün de başarılı olması ve doğru ortama gitmesi gerekir, bu da öncü
süreye üç kez yapı ve üç kez dağıtım hazırlığı ekler. Geri dönüş oranı tarafında ise doğrulanmamış
ikili sınıfı vardır: sınamadan geçen nesne üretime gitmediği için, sınamanın yakaladığı hata
sınıflarının bir bölümü üretimde yeniden doğabilir. Tek çıktı düzeni bu iki ölçütü aynı yönde
oynatır — ama bedeli, ortama göre değişen on iki değeri çıktının dışında bir yerde tutmayı
zorunlu kılmasıdır.

## Özet

- Yapı çıktısı kaynaktan üretilen ve dağıtılan tek nesnedir; değişmezlik ilkesi bu nesnenin bir
  kez üretilmesini, yerinde değiştirilmemesini ve her ortama aynısının gitmesini söyler.
- İki çıktının aynı olup olmadığı yalnız içerik özetiyle söylenebilir; dosya adı, tarih ve
  sürüm etiketi bunu söylemez.
- Yeniden üretilebilirliği bozan dört kaynak ölçüldü — zaman damgası, dizin okuma sırası, ortam
  değişkeni, mutlak yol — ve her biri tek başına ikinci makinede farklı özet üretmeye yetti.
  Dördü kapatıldığında üç koşum tek özet verdi.
- Bu dört kaynağın üçü aynı makinedeki iki koşumda görünmez; fark ancak yapı başka bir makinede
  koştuğunda ortaya çıkar. Gizlediği hata sınıfı doğrulanmamış ikilidir.
- Ortam başına yeniden derleme üç ayrı ikili ve on iki gömülü değer üretti; tek çıktı düzeninde
  ikili sayısı bire, gömülü değer sayısı sıfıra indi. Değerler yok olmadı, çıktının içinden
  ortamın künyesine geçti.

## Sonraki Adım

Tek çıktı düzeni bir soruyu yanıtsız bıraktı: üretilen bu tek nesne nerede durur ve nasıl
adlandırılır? Bir özet kimliktir ama ad değildir; on altı onaltılık basamağı dağıtım kayıtlarına
elle yazmak sürdürülebilir bir düzen değildir. Ayrıca çıktı bir kez üretilip her yere
dağıtılacaksa, üretildiği yerden dağıtılacağı yere kadar bir yerde saklanması ve geri alma
gerektiğinde önceki sürümün hâlâ bulunabilmesi gerekir. Sonraki ders bu saklama yerini ölçer:
sürüm nasıl etiketlenir, değişmez sürüm ile taşınabilir etiket arasındaki fark kaç dağıtımda
kendini gösterir ve saklama süresi kuralı geri alma penceresini nerede kapatır?
