---
title: 'Önizleme Dağıtımları'
source: 'https://academia.sh/tr/kurslar/olusturma-stratejileri/onizleme-dagitimlari'
course: 'Oluşturma Stratejileri ve Altyapı'
language: tr
updated: '2026-08-17T18:09:30+00:00'
license: 'CC BY-SA 4.0'
---

# Önizleme Dağıtımları

Her değişiklik için ayrı bir ortam üretmek; içerikten türetilen belirlenimci dağıtım kimliği, blokları paylaşan içerik adresli depo, önizlemelerin yayınla yan yana sunulması, veri ve yan etki yalıtımı ile dağıtımların ömrü.

Önceki iki ders yayına bakıyordu: çıktının nasıl sunulduğuna ve istek anında çalışan kodun
nerede durduğuna. Bir değişikliğin yayına girmeden önce çalıştığının görülmesi ise ayrı bir
sorundur ve Geliştirme Sunucusu dersinin son cümlesi nedenini vermişti — geliştirme kipinde
çalışması, yayın kipinde çalıştığının kanıtı değildir.

Bu ders her değişiklik için ayrı bir ortam kurar: yayın kipiyle derlenmiş, kendi adresi olan,
yayına dokunmayan ve dizinlenmeyen bir dağıtım.

## Önizleme Dağıtımının Karşılaması Gerekenler

Bir önizleme dağıtımı altı koşulu birden sağladığında işe yarar.

**Yayınla aynı hat.** Aynı derleme adımları, aynı kip, aynı denetimler. Farklı bir hatla
üretilen bir önizleme, kanıtlamak istediği şeyi kanıtlamaz.

**Kararlı ve türetilmiş adres.** Dağıtımın kimliği içeriğinden türetilir; aynı değişiklik
her zaman aynı kimliği verir. Önbellek Kırma dersindeki belirlenimcilik koşulu burada bir
kimliğe dönüşür.

**Yayınla aynı anda yaşayabilme.** Önizlemenin varlığı yayının davranışını hiçbir biçimde
değiştirmez; ikisi aynı sunucudan aynı anda sunulabilir.

**Veri ve yan etki yalıtımı.** Önizleme yayın verisine yazmaz, dışa giden yan etkileri
tetiklemez.

**Dizinlenmeme ve erişim denetimi.** Önizleme adresleri arama dizinlerine girmemeli, herkese
açık olmamalıdır.

**Sonlu ömür.** Değişiklik kapandığında dağıtım kaldırılır.

## Dağıtım Kimliği ve Paylaşılan Depo

Aşağıdaki betik üç dalı — yayın ve iki değişiklik — kendi ortamlarıyla derler, dosyaları
içerik adresli bir depoya yazar ve her dağıtım için bir kimlik üretir.

```js
// onizleme.mjs — degisiklik basina dagitim: icerik adresli kimlik ve paylasilan depo.
import { createHash } from "node:crypto";
import { mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
import { join } from "node:path";

const karma = (g) => createHash("sha256").update(g).digest("hex");

// Yayindaki kaynak agac. {{AD}} yer tutuculari 07. dersteki ikameyle doldurulur.
const TABAN = {
  "belge.html":
    '<!doctype html><html lang="tr"><body>' +
    "<h1>Kuzey Yamac Olcum Istasyonu</h1>" +
    "<p>uc: {{OLCUM_UCU}}</p><p>dal: {{DAL}}</p></body></html>\n",
  "varlik/istasyon.css": ".olcum { font-variant-numeric: tabular-nums; }\n",
  "varlik/giris.js": 'import "./panel.js";\n',
  "varlik/panel.js": 'export const ciz = (o) => o.map((x) => x.sicaklik).join(" ");\n',
  "varlik/grafik.js": "export const eksen = (a, b) => [a, b];\n",
};

// Uc dal: yayin ve iki ayri degisiklik. Her dal kendi ortamiyla derlenir.
const DAL = {
  yayin: { degisiklik: {}, ortam: { OLCUM_UCU: "https://olcum.kuzey-yamac.example/v1" } },
  "esik-uyarisi": {
    degisiklik: {
      "varlik/panel.js":
        'export const ciz = (o) =>\n' +
        '  o.map((x) => (x.sicaklik < -20 ? "!" : "") + x.sicaklik).join(" ");\n',
    },
    ortam: { OLCUM_UCU: "https://deneme.olcum.kuzey-yamac.example/v1" },
  },
  "tarih-bicimi": {
    degisiklik: {
      "varlik/grafik.js": "export const eksen = (a, b) => [a, b, \"UTC\"];\n",
      "varlik/istasyon.css":
        ".olcum { font-variant-numeric: tabular-nums; font-feature-settings: \"ss01\"; }\n",
    },
    ortam: { OLCUM_UCU: "https://deneme.olcum.kuzey-yamac.example/v1" },
  },
};

const DEPO = "depo";
const sayac = { yazilan: 0, yeniden: 0 };

function derle(dalAdi) {
  const { degisiklik, ortam } = DAL[dalAdi];
  const tam = { ...ortam, DAL: dalAdi };
  const bildirim = {};
  for (const yol of Object.keys(TABAN).sort()) {
    const kaynak = degisiklik[yol] ?? TABAN[yol];
    const govde = kaynak.replace(/\{\{([A-Z_]+)\}\}/g, (t, ad) => tam[ad] ?? t);
    const k = karma(govde);
    const hedef = join(DEPO, k);
    if (existsSync(hedef)) sayac.yeniden += 1;
    else { writeFileSync(hedef, govde); sayac.yazilan += 1; }
    bildirim[yol] = k;
  }
  // Dagitim kimligi: yol-karma ciftlerinin siralanmis listesinin karmasi.
  const kimlik = karma(Object.entries(bildirim).map(([y, k]) => y + " " + k).join("\n"))
    .slice(0, 12);
  mkdirSync(join("dagitim", kimlik), { recursive: true });
  writeFileSync(join("dagitim", kimlik, "bildirim.json"),
    JSON.stringify({ dal: dalAdi, dosyalar: bildirim }, null, 2) + "\n");
  return { kimlik, bildirim };
}

rmSync(DEPO, { recursive: true, force: true });
rmSync("dagitim", { recursive: true, force: true });
mkdirSync(DEPO, { recursive: true });

const sonuc = {};
for (const dal of Object.keys(DAL)) sonuc[dal] = derle(dal);

console.log("dal".padEnd(16) + "dagitim kimligi".padEnd(18) + "dosya");
for (const [dal, s] of Object.entries(sonuc))
  console.log(dal.padEnd(16) + s.kimlik.padEnd(18) + Object.keys(s.bildirim).length);

console.log("\ndepo: " + sayac.yazilan + " ayri blok yazildi, " +
  sayac.yeniden + " blok yeniden kullanildi (toplam " +
  (sayac.yazilan + sayac.yeniden) + " dosya girisi)");

// Ayni kaynaktan ikinci derleme ayni kimligi vermelidir.
const tekrar = Object.fromEntries(Object.keys(DAL).map((d) => [d, derle(d).kimlik]));
console.log("ikinci derleme ayni kimlikleri verdi: " +
  Object.entries(sonuc).every(([d, s]) => s.kimlik === tekrar[d]));

// Yayin dagitimini isaretle ve kimlikleri sunucunun okuyacagi dosyaya yaz.
writeFileSync("guncel.txt", sonuc.yayin.kimlik + "\n");
writeFileSync("kimlikler.txt",
  Object.entries(sonuc).map(([d, s]) => d + " " + s.kimlik).join("\n") + "\n");

console.log("\ndagitimlar arasi fark (yayin ile karsilastirma):");
for (const dal of ["esik-uyarisi", "tarih-bicimi"]) {
  const fark = Object.keys(TABAN).sort()
    .filter((y) => sonuc[dal].bildirim[y] !== sonuc.yayin.bildirim[y]);
  console.log("  " + dal.padEnd(16) + fark.length + "/" + Object.keys(TABAN).length +
    " dosya farkli: " + fark.join(", "));
}
```

## Yan Yana Sunmak

Üç dağıtım aynı sunucudan sunulur. Yayın kökte, önizlemeler kimliklerinin altında durur.

```js
// onizleme-sunucu.mjs — dagitimlari kimliklerine gore yan yana sunar.
// Kokte yayin dagitimi, /o/<kimlik>/ altinda onizleme dagitimlari durur.
import { createServer } from "node:http";
import { readFileSync, existsSync } from "node:fs";
import { join, extname } from "node:path";

const YAYIN = readFileSync("guncel.txt", "utf8").trim();
const TUR = {
  ".html": "text/html; charset=utf-8",
  ".css": "text/css; charset=utf-8",
  ".js": "text/javascript; charset=utf-8",
};

createServer((istek, yanit) => {
  yanit.sendDate = false;
  const parca = istek.url.split("?")[0].split("/").filter(Boolean);
  const onizleme = parca[0] === "o";
  const kimlik = onizleme ? parca[1] : YAYIN;
  const yol = (onizleme ? parca.slice(2) : parca).join("/") || "belge.html";

  const bildirimYolu = join("dagitim", kimlik, "bildirim.json");
  if (!existsSync(bildirimYolu)) {
    yanit.writeHead(404, { "Content-Type": "text/plain" }).end("dagitim yok\n");
    return;
  }
  const bildirim = JSON.parse(readFileSync(bildirimYolu, "utf8"));
  const blok = bildirim.dosyalar[yol];
  if (!blok) {
    yanit.writeHead(404, { "Content-Type": "text/plain" }).end("dosya yok\n");
    return;
  }
  const govde = readFileSync(join("depo", blok));
  const baslik = {
    "Content-Type": TUR[extname(yol)] ?? "application/octet-stream",
    "Content-Length": govde.length,
    "X-Dagitim": kimlik,
    "X-Dal": bildirim.dal,
  };
  // Onizleme dagitimlari dizinlenmemeli ve yayin adresiyle karistirilmamalidir.
  if (onizleme) baslik["X-Robots-Tag"] = "noindex, nofollow";
  yanit.writeHead(200, baslik).end(govde);
}).listen(8183, "127.0.0.1", () => console.log("sunucu: 127.0.0.1:8183"));
```

Aşağıdaki betik iki dosyayla aynı dizinde çalıştırılır. Bağlantı noktası 8183 keyfîdir ve boş
olmalıdır; kullanımdaysa iki dosyada da değiştirilir.

```bash
#!/usr/bin/env bash
# Uc dagitimi uretir, hepsini ayni sunucudan yan yana sunar.
node onizleme.mjs
ESIK=$(awk '$1 == "esik-uyarisi" { print $2 }' kimlikler.txt)
TARIH=$(awk '$1 == "tarih-bicimi" { print $2 }' kimlikler.txt)

node onizleme-sunucu.mjs > /dev/null &
sunucu=$!
sleep 1

echo "--- yayin (kok) ---"
curl -sS -D - -o /dev/null http://127.0.0.1:8183/ | grep -iE '^(x-dagitim|x-dal|x-robots-tag)'
curl -sS http://127.0.0.1:8183/varlik/panel.js

echo "--- onizleme: esik-uyarisi ---"
curl -sS -D - -o /dev/null "http://127.0.0.1:8183/o/$ESIK/" | grep -iE '^(x-dagitim|x-dal|x-robots-tag)'
curl -sS "http://127.0.0.1:8183/o/$ESIK/varlik/panel.js"
curl -sS "http://127.0.0.1:8183/o/$ESIK/"

echo "--- onizleme: tarih-bicimi ---"
curl -sS "http://127.0.0.1:8183/o/$TARIH/varlik/grafik.js"
curl -sS -o /dev/null -w 'olmayan dagitim kimligi : %{http_code}\n' \
  http://127.0.0.1:8183/o/000000000000/

kill "$sunucu"
```

```
dal             dagitim kimligi   dosya
yayin           4eebc27e9c5c      5
esik-uyarisi    eede80ec039c      5
tarih-bicimi    779bc992a4e2      5

depo: 10 ayri blok yazildi, 5 blok yeniden kullanildi (toplam 15 dosya girisi)
ikinci derleme ayni kimlikleri verdi: true

dagitimlar arasi fark (yayin ile karsilastirma):
  esik-uyarisi    2/5 dosya farkli: belge.html, varlik/panel.js
  tarih-bicimi    3/5 dosya farkli: belge.html, varlik/grafik.js, varlik/istasyon.css
--- yayin (kok) ---
X-Dagitim: 4eebc27e9c5c
X-Dal: yayin
export const ciz = (o) => o.map((x) => x.sicaklik).join(" ");
--- onizleme: esik-uyarisi ---
X-Dagitim: eede80ec039c
X-Dal: esik-uyarisi
X-Robots-Tag: noindex, nofollow
export const ciz = (o) =>
  o.map((x) => (x.sicaklik < -20 ? "!" : "") + x.sicaklik).join(" ");
<!doctype html><html lang="tr"><body><h1>Kuzey Yamac Olcum Istasyonu</h1><p>uc: https://deneme.olcum.kuzey-yamac.example/v1</p><p>dal: esik-uyarisi</p></body></html>
--- onizleme: tarih-bicimi ---
export const eksen = (a, b) => [a, b, "UTC"];
olmayan dagitim kimligi : 404
```

Çıktı dört şey söylüyor.

**Kimlik içerikten gelir.** Aynı kaynaktan ikinci kez üretilen kimlikler özdeştir. Bu, bir
dağıtımın hangi kaynaktan geldiğini adres üzerinden doğrulanabilir kılar ve sonraki dersteki
geri alma işleminin dayanağıdır: geri dönülecek sürüm bir ada değil, bir içeriğe karşılık
gelir.

**Ortam kimliğe girer.** Eşik uyarısı dalında yalnız bir dosya değiştirildiği hâlde yayından
iki dosya farklıdır; ikincisi belgedir ve farkın nedeni ölçüm ucunun değişmesidir. Derleme
zamanı ikamesinin yedinci derste söylenen sonucu burada ölçülür: ortam değiştiğinde çıktı
baytları da değişir, dolayısıyla önizlemede sınanan baytlar yayına giden baytlar değildir.
Bu farkı sıfırlamanın tek yolu, ortama bağlı değerleri çalışma zamanı yapılandırmasına
taşımaktır.

**Depo blokları paylaşır.** On beş dosya girişi on ayrı bloka indi. Oran, dosya sayısı
büyüdükçe iyileşir: bir dağıtımın maliyeti bütün çıktısı değil, yalnız değişen dosyalarıdır.
Yüzlerce dosyalık bir yayında tek satırlık bir değişiklik birkaç yeni blok yazdırır.

**Önizleme yayından ayrı bir kimliktir.** Yanıt başlıkları hangi dağıtımın sunulduğunu
söyler, önizlemeler dizinlenmeye kapalı işaretlenir, tanımsız bir kimlik `404` alır. Kökteki
yayın, önizlemelerin varlığından etkilenmez.

## Veri ve Yan Etki Yalıtımı

Önizlemenin en kolay bölümü baytlardır; zor bölümü, o baytların çalıştığında neye dokunduğudur.

Kural tek cümledir: **önizleme yayın verisine yazamaz.** Uygulanışı ise bir kod kararı değil,
bir yapılandırma kararıdır. Yedinci dersteki kapsam bildirimi burada ikinci işini görür:
veri ucunun adresi, yazma anahtarının hangi hesaba ait olduğu ve dışa giden çağrıların nereye
gittiği ortam değişkenleriyle belirlenir. Önizleme ortamı bu değişkenleri deneme
karşılıklarına bağlar.

Ayrımın kodda `if (onizleme)` biçiminde bir dala dönüşmesi yanlıştır. O dal yayın çıktısında
da bulunur ve koşulunun yanlış değerlendiği her durumda yalıtım kaybolur. Sınırı ortam
çizerse, yayın çıktısında deneme ucuna giden bir yol hiç bulunmaz.

Üç yan etki sınıfı ayrı ayrı ele alınır. **Yazma işlemleri** ayrı bir veri kopyasına gider;
kopya tohumlanmış küçük bir kümedir, yayın verisinin aynası değildir — aynası olsaydı kişisel
veri önizleme adresinden görünür olurdu. **Dışa giden bildirimler** — e-posta, ödeme çağrısı,
üçüncü taraf tetikleyicileri — önizlemede ya kapalıdır ya bir toplayıcıya yönlendirilir.
**Şema değişiklikleri** önizlemenin kendi kopyasında uygulanır; paylaşılan bir şema üzerinde
uygulanan bir göç, henüz yayına girmemiş bir değişikliği yayına sokar.

Erişim de yalıtımın parçasıdır. `X-Robots-Tag` başlığı yalnız dizinleyicilere yöneliktir;
adresi bilen birini engellemez. Kimliğin tahmin edilemez olması bir erişim denetimi
değildir — adres tarayıcı geçmişine, bağlantı önizlemelerine ve yönlendiren başlıklarına
düşer. Erişim denetimi ayrı bir katmandır.

## Ömür, Maliyet ve Temizlik

Değişiklik başına ortam, dağıtım sayısını değişiklik sayısıyla çarpar. Maliyetin iki kalemi
vardır ve ikisi de sınırlanabilir.

**Depolama** paylaşılan blok deposuyla sınırlanır. Bir dağıtımı silmek, bildirimini silmektir;
blokların silinmesi ancak başka hiçbir bildirimin onlara başvurmadığı doğrulandıktan sonra
yapılır. Başvuru sayımı yapılmadan yapılan bir temizlik, yayın dağıtımının dosyalarını da
silebilir.

**Çalışan kod** varsa, ikinci dersteki üç biçimden hangisinin seçildiği doğrudan maliyete
yansır. Önizlemeler seyrek ve düzensiz kullanılır; bu, o dersin tablosunda istek başına
örneğin en iyi göründüğü penceredir.

Ömür kuralı basittir: değişiklik kapandığında önizleme kaldırılır. Bir istisnası vardır —
**yayına giren her dağıtım korunur.** Korunan dağıtımlar bir sürüm geçmişi oluşturur ve
sonraki dersin geri alma işlemi tam olarak bu geçmişe döner.

## Özet

- Önizleme dağıtımı altı koşulu birden sağlamalıdır: yayınla aynı hat, türetilmiş adres,
  yayınla yan yana yaşayabilme, veri yalıtımı, dizinlenmeme ve sonlu ömür.
- Dağıtım kimliği yol-karma çiftlerinin karmasıdır; aynı kaynak aynı kimliği verir ve kimlik
  içeriğin doğrulanabilir adı olur.
- Ortam değişkenleri çıktının baytlarına girdiği için önizleme ile yayın baytları ayrışır;
  ayrımı sıfırlamanın yolu değerleri çalışma zamanı yapılandırmasına taşımaktır.
- İçerik adresli depo blokları paylaştırır: örnekte on beş dosya girişi on bloka indi, bir
  dağıtımın maliyeti yalnız değişen dosyalarıdır.
- Yalıtım bir kod dalıyla değil, ortam yapılandırmasıyla kurulur; yazma, dışa giden bildirim
  ve şema değişikliği ayrı ayrı ele alınır.
- Dizinlenmeme başlığı erişim denetimi değildir; dağıtım silme işlemi blok başvuru sayımına
  bakmalı, yayına giren dağıtımlar ise korunmalıdır.

## Sonraki Adım

Önizlemede sınanmış bir değişiklik, yayına girdiğinde bütün kullanıcılara aynı anda ulaşır.
Bu, önizlemenin yakalayamadığı her kusurun aynı anda herkese ulaşması demektir: yalnız gerçek
trafikte ortaya çıkan yük davranışları, yalnız gerçek veride bulunan uç durumlar ve yalnız
belirli aygıtlarda görülen hatalar. Sonraki ders yayını bir anahtar çevirmesi olmaktan
çıkarır; trafiğin küçük bir payını yeni sürüme yönlendirir, hata oranını bir eşiğe karşı
izler ve eşik aşıldığında geri döner. Geri dönülecek yer, bu derste korunan dağıtımlardır ve
dönüş süresi ölçülebilir bir büyüklüktür.
