---
title: 'Geliştirme Sunucusu'
source: 'https://academia.sh/tr/kurslar/olusturma-stratejileri/gelistirme-sunucusu'
course: 'Oluşturma Stratejileri ve Altyapı'
language: tr
updated: '2026-08-17T18:09:30+00:00'
license: 'CC BY-SA 4.0'
---

# Geliştirme Sunucusu

Derlemenin ikinci kipi; modüllerin istendikçe dönüştürülüp sunulması, doğrulayıcıyla yeniden kullanım, sıcak modül değişiminin geçersiz kılma yayılımı olarak modellenmesi ve kabul sınırının doğruluk koşulları.

Önceki beş ders tek bir kipi kurdu: yayın için derleme. Bu kipte çizge tümüyle taranır,
parçalar birleştirilir, ölü kod atılır, varlıklar işlenir ve adlar içeriğe bağlanır.
Adımların hepsinin karşılığı vardır — ama karşılığı yalnız yayında vardır.

Kuzey Yamaç Ölçüm İstasyonu arayüzünde bir biçimlendirme satırını değiştiren biri için bu
adımların hiçbiri işe yaramaz. Çizgenin tümünü yeniden taramak, karmaları yeniden
hesaplamak ve sayfayı baştan yüklemek iki şey kaybettirir: saniyeler ve ekrandaki durum.
Panelin açık olması, süzgecin ayarlanmış olması, kaydırma konumu — tam yükleme hepsini
siler.

Bu ders derlemenin ikinci kipini kurar. Sorusu şudur: bir dosya değiştiğinde en az ne
yapılabilir?

## Kipler Arasındaki Fark

Geliştirme kipi, yayın kipinin adımlarının çoğunu düşürür.

**Paketleme düşer.** Tarayıcı, standart modülleri kendisi yükleyebilir; çizgeyi dolaşma işi
tarayıcının modül yükleyicisine bırakılır. Sunucunun görevi tek bir modülü istendiğinde
vermeye iner.

**Ağaç sarsma, küçültme ve içerik karması düşer.** Üçü de çıktı boyutunu küçültür; yerel
bağlantıda boyut belirleyici değildir ve üçü de tüm çizgeyi görmeyi gerektirir.

**Kalması gerekenler** ise üçtür: belirteç çözümü — tarayıcı çıplak belirteçleri
çözemez —, hedefin anlamadığı sözdizimin çevrilmesi ve kaynak haritası üretimi. Modüller,
Araçlar ve Ekosistem kursundaki kaynak haritaları dersi bu üçüncüsünün neden vazgeçilmez
olduğunu göstermişti: dönüştürülmüş kodda hata ayıklanamaz.

Bu düşürme, maliyeti çizge boyutundan bağımsız kılar. Yayın kipinde bir değişiklik tüm
çizgeyi ilgilendirir; geliştirme kipinde yalnız değişen dosyayı.

## İstendikçe Sunma

Aşağıdaki sunucu tek bir dönüşüm yapar ve tek bir dosya sunar.

Bu dersin sunucusu, önceki derslerde kurulan `kaynak/` ağacını sunar; o derslerin kurulum blokları bu dizinde önce çalıştırılmalıdır.

```js
// gelistirme-sunucusu.mjs — modulleri istendikce, donusturerek ve dogrulayiciyla sunar.
import { createServer } from "node:http";
import { createHash } from "node:crypto";
import { readFileSync, existsSync } from "node:fs";

// Tek donusum: goreli belirtecler sunucudaki mutlak yollara cevrilir.
// Paketleme yok; her modul kendi dosyasi olarak kalir.
function donustur(yol, kaynak) {
  return "// [gelistirme] " + yol + "\n" + kaynak
    .replace(/from\s+"\.\/([^"]+)"/g, 'from "/kaynak/$1"')
    .replace(/^import\s+"\.\/([^"]+)"/gm, 'import "/kaynak/$1"')
    .replace(/import\(\s*"\.\/([^"]+)"\s*\)/g, 'import("/kaynak/$1")');
}

createServer((istek, yanit) => {
  yanit.sendDate = false;                        // ciktiyi belirlenimci kilmak icin
  const yol = istek.url.split("?")[0];
  const dosya = yol.replace(/^\//, "");
  if (!yol.startsWith("/kaynak/") || !existsSync(dosya)) {
    yanit.writeHead(404).end();
    return;
  }
  const govde = donustur(yol, readFileSync(dosya, "utf8"));
  const etiket = '"' + createHash("sha256").update(govde).digest("hex").slice(0, 12) + '"';
  if (istek.headers["if-none-match"] === etiket) {
    yanit.writeHead(304, { ETag: etiket }).end();
    return;
  }
  yanit.writeHead(200, {
    "Content-Type": "text/javascript; charset=utf-8",
    "Content-Length": Buffer.byteLength(govde),
    "Cache-Control": "no-cache",
    ETag: etiket,
  }).end(govde);
}).listen(8172, "127.0.0.1", () => console.log("dinleniyor: 127.0.0.1:8172"));
```

Çizgeyi dolaşan taraf tarayıcıdır. Aşağıdaki istemci, tarayıcının modül yükleme döngüsünün
küçük bir karşılığıdır: bir modülü alır, içindeki statik belirteçleri okur, onları ister.
Dinamik içe aktarımı izlemez — çünkü tarayıcı da izlemez, çağrı yürütülene kadar bekler.

```js
// istemci.mjs — tarayicinin modul yukleme dongusunun kucuk bir karsiligi.
// Yalniz statik belirtecler izlenir; import() cagrisi yurutulene kadar istenmez.
const KOK = "http://127.0.0.1:8172";
const STATIK = [/from\s*"([^"]+)"/g, /^import\s*"([^"]+)"/gm];

async function yukle(kokler) {
  const alinan = new Map(), kuyruk = [...kokler];
  while (kuyruk.length > 0) {
    const yol = kuyruk.shift();
    if (alinan.has(yol)) continue;
    const govde = await (await fetch(KOK + yol)).text();
    alinan.set(yol, govde.length);
    for (const kalip of STATIK)
      for (const [, b] of govde.matchAll(kalip)) if (b.startsWith("/kaynak/")) kuyruk.push(b);
  }
  return alinan;
}

const kisa = (k) => [...k].map(([y]) => y.replace("/kaynak/", "")).sort().join(", ");
const ilk = await yukle(["/kaynak/giris-tarayici.js"]);
console.log("ilk yuklemede alinan : " + ilk.size + " modul  " + kisa(ilk));
const sonra = await yukle([...ilk.keys(), "/kaynak/panel.js"]);
const ek = new Map([...sonra].filter(([y]) => !ilk.has(y)));
console.log("panel acilinca ek    : " + ek.size + " modul  " + kisa(ek));
console.log("uyari.js henuz istenmedi: " + !sonra.has("/kaynak/uyari.js"));
```

Uzun ömürlü sunucu kendi betiğiyle başlatılır. Bağlantı noktası olarak seçilen 8172
keyfîdir ve boş olmalıdır.

```bash
#!/usr/bin/env bash
# Sunucuyu baslatir, tek modulu ister, dogrulayiciyi sinar, modul dongusunu kosturur.
cp kaynak/sablon.js sablon.ilk
node gelistirme-sunucusu.mjs > /dev/null &
sunucu=$!
sleep 1

echo "--- tek modulun sunulan hali ---"
curl -sS http://127.0.0.1:8172/kaynak/sablon.js

ETIKET=$(curl -sS -o /dev/null -D - http://127.0.0.1:8172/kaynak/sablon.js \
  | awk 'tolower($1) == "etag:" { print $2 }' | tr -d '\r')
echo "--- dogrulayici $ETIKET ---"
curl -sS -o /dev/null -w "degismeden ikinci istek : %{http_code}\n" \
  -H "If-None-Match: $ETIKET" http://127.0.0.1:8172/kaynak/sablon.js
printf '\nexport const dipnot = () => "olcumler UTC";\n' >> kaynak/sablon.js
curl -sS -o /dev/null -w "dosya degistikten sonra : %{http_code}\n" \
  -H "If-None-Match: $ETIKET" http://127.0.0.1:8172/kaynak/sablon.js

echo "--- istendikce yukleme ---"
node istemci.mjs

kill "$sunucu"
mv sablon.ilk kaynak/sablon.js
```

```
--- tek modulun sunulan hali ---
// [gelistirme] /kaynak/sablon.js
import { olcumSatiri } from "/kaynak/bicim.js";

export const liste = (olcumler) =>
  "<ul>" + olcumler.map((o) => "<li>" + olcumSatiri(o) + "</li>").join("") + "</ul>";
export const baslik = (istasyon) => "<h1>" + istasyon + "</h1>";

--- dogrulayici "4e20f52c3876" ---
degismeden ikinci istek : 304
dosya degistikten sonra : 200
--- istendikce yukleme ---
ilk yuklemede alinan : 9 modul  arac.js, bicim.js, birim.js, giris-tarayici.js, kayit.js, olcek-kaydi.js, sablon.js, tahmin.js, tarih.js
panel acilinca ek    : 3 modul  disa-aktarma.js, grafik.js, panel.js
uyari.js henuz istenmedi: true
```

Doğrulayıcı değeri sunulan modülün baytlarından üretilir: aynı bayt dizisi her zaman aynı
değeri, tek karakterlik bir değişiklik başka bir değeri verir. Buradaki değer bu dizinin o
andaki içeriğine bağlıdır — önceki derslerin kurulum blokları aynı dosyayı yeniden yazdığı
için, hangi blokların çalıştırıldığına göre farklı bir doğrulayıcı görülebilir. Anlamlı olan
değerin kendisi değil, değişmeyen modülde **aynı** kalmasıdır.

Çıktının son üç satırı geliştirme kipinin kazancını gösterir. İlk yüklemede on üç modülün
dokuzu istenir; panel açılınca üçü daha gelir; eşik uyarısı modülü hiç istenmez, çünkü onu
isteyen dinamik çağrı henüz yürümemiştir. Hiçbir aşamada tüm çizge taranmaz.

Doğrulayıcı satırları ikinci kazancı verir. İçeriği değişmemiş bir modül, ikinci istekte
gövdesiz yanıtla geçiştirilir; sayfa yenilendiğinde yalnız değişen dosyalar yeniden iner.

## Geçersiz Kılma Yayılımı

Sayfayı yenilemeden güncellemek bundan fazlasını gerektirir. Bir modülün yeni sürümü
alındığında, onu içe aktarmış modüllerin elindeki değerler eskidir: içe aktarılan bağlar
yeniden okunmalı, o modüller de yeniden değerlendirilmelidir. Değişim, çizgede kenar
yönünün tersine — istenenden isteyene doğru — yayılır.

Yayılımın sonsuza kadar sürmemesi için bir durak gerekir. **Kabul sınırı (accept
boundary)**, kendi güncellemesini karşılayabildiğini bildiren modüldür: yeni sürümüyle
değiştirilir, isteyenlerine haber verilmez. Sınır bulunamaz ve yayılım giriş noktasına
ulaşırsa yapılacak tek şey tam yüklemedir.

```js
// sicak.mjs — modul cizgesinde gecersiz kilma yayilimi ve kabul siniri.
import { readFileSync } from "node:fs";
import { dirname, join, relative } from "node:path";

const GIRIS = "kaynak/giris-tarayici.js";
const BAG = [/from\s+"(\.[^"]+)"/g, /^import\s+"(\.[^"]+)"/gm, /import\(\s*"(\.[^"]+)"\s*\)/g];

// Kabul siniri: guncellemesini kendisi karsilayan, importerlarina yaymayan modul.
const KABUL = new Set(["kaynak/sablon.js", "kaynak/panel.js", "kaynak/grafik.js"]);
// Modul duzeyinde durum tutan moduller: yeniden degerlendirilirse durum sifirlanir.
const DURUM = new Set(["kaynak/kayit.js"]);

const cizge = new Map(), kuyruk = [GIRIS];
while (kuyruk.length > 0) {
  const yol = kuyruk.shift();
  if (cizge.has(yol)) continue;
  const kaynak = readFileSync(yol, "utf8");
  const komsu = BAG.flatMap((k) => [...kaynak.matchAll(k)]
    .map(([, b]) => relative(".", join(dirname(yol), b))));
  cizge.set(yol, komsu);
  kuyruk.push(...komsu);
}

const isteyen = new Map([...cizge.keys()].map((y) => [y, []]));
for (const [yol, komsu] of cizge) for (const k of komsu) isteyen.get(k).push(yol);

function yayil(degisen) {
  const gecersiz = [], gorulen = new Set(), kuyruk = [degisen];
  let tamYukleme = false;
  while (kuyruk.length > 0) {
    const yol = kuyruk.shift();
    if (gorulen.has(yol)) continue;
    gorulen.add(yol);
    gecersiz.push(yol);
    if (KABUL.has(yol)) continue;                 // sinir bulundu, yukari yayilmaz
    if (yol === GIRIS) { tamYukleme = true; continue; }
    kuyruk.push(...isteyen.get(yol));
  }
  return { gecersiz, tamYukleme, durum: gecersiz.filter((y) => DURUM.has(y)) };
}

const ad = (y) => y.replace("kaynak/", "");
console.log("cizge: " + cizge.size + " modul, kabul siniri: " + [...KABUL].map(ad).join(", "));
console.log("degisen".padEnd(18) + "sonuc".padEnd(14) + "yeniden degerlendirilen kume");
for (const degisen of ["kaynak/grafik.js", "kaynak/panel.js", "kaynak/bicim.js",
                       "kaynak/birim.js", "kaynak/kayit.js", "kaynak/giris-tarayici.js"]) {
  const r = yayil(degisen);
  console.log(ad(degisen).padEnd(18) +
    (r.tamYukleme ? "tam yukleme" : "sicak degisim").padEnd(14) +
    r.gecersiz.length + "/" + cizge.size + "  " + r.gecersiz.map(ad).join(", ") +
    (r.durum.length ? "   [durum sifirlanir: " + r.durum.map(ad).join(", ") + "]" : ""));
}
```

```
$ node sicak.mjs
cizge: 13 modul, kabul siniri: sablon.js, panel.js, grafik.js
degisen           sonuc         yeniden degerlendirilen kume
grafik.js         sicak degisim 1/13  grafik.js
panel.js          sicak degisim 1/13  panel.js
bicim.js          sicak degisim 4/13  bicim.js, sablon.js, disa-aktarma.js, panel.js
birim.js          sicak degisim 10/13  birim.js, grafik.js, uyari.js, arac.js, tahmin.js, olcek-kaydi.js, panel.js, bicim.js, sablon.js, disa-aktarma.js
kayit.js          sicak degisim 7/13  kayit.js, arac.js, olcek-kaydi.js, bicim.js, sablon.js, disa-aktarma.js, panel.js   [durum sifirlanir: kayit.js]
giris-tarayici.js tam yukleme   1/13  giris-tarayici.js
```

Tablo üç şey söyler.

**Sınırın altındaki değişiklik ucuzdur.** Grafik ve panel modülleri kendi güncellemelerini
kabul ettiği için yalnız kendileri yeniden değerlendirilir; on üç modülün on ikisi
dokunulmadan kalır.

**Yaprak modüldeki değişiklik pahalıdır.** Birim modülü çizgede aşağıdadır ve onu doğrudan
ya da dolaylı olarak isteyen dokuz modül vardır; hepsi yeniden değerlendirilir. Ortak
yardımcı modüllere yapılan küçük düzenlemeler bu yüzden yavaş görünür.

**Kabul sınırının yeri kararı belirler.** Sınırlar görünüm sınırlarına konur, çünkü bir
görünüm kendi çıktısını yeniden üretebilir. Yardımcı modüller sınır olamaz: değerlerini
isteyenlerin elindeki bağları güncelleyemezler.

## Sıcak Değişimin Doğruluk Koşulu

Sıcak değişim bir iyileştirme değil, bir risktir; doğru uygulanmadığında geliştiricinin
gördüğü durumla soğuk yüklemede oluşacak durum ayrışır.

Birinci tehlike **durum sıfırlanmasıdır.** Modül düzeyinde durum tutan bir modül yeniden
değerlendirildiğinde o durum kaybolur. Tablodaki son sütun bunu işaretliyor: kayıt modülü
güncellendiğinde biçimlendirici kaydı boşalır ve onu dolduran yan etkili modül de yeniden
çalışmazsa arayüz eksik davranır. Ağaç sarsma dersindeki sessiz davranış kaybının aynısı,
bu kez geliştirme sırasında ortaya çıkar.

İkinci tehlike **eski kapanışlardır.** Bir modülün eski sürümünde kaydedilmiş bir olay
dinleyicisi ya da zamanlayıcı, modül değiştirildikten sonra da çalışmaya devam eder. Kabul
sınırı, yeni sürüme geçmeden önce eskisinin bıraktıklarını temizlemekle yükümlüdür.

Üçüncü tehlike **birikmiş sapmadır.** Onlarca sıcak değişimden sonraki durum, aynı kaynağın
soğuk yüklemesiyle oluşacak durumdan farklı olabilir. Kural, şüphe duyulan her noktada
sayfayı elle yenilemek ve doğrulamayı soğuk yükleme üzerinde yapmaktır.

Bu üç tehlike, sıcak değişimin uygulanamadığı durumlarda tam yüklemeye düşmenin neden bir
kusur değil bir güvence olduğunu açıklar.

## Geliştirme ile Yayın Arasındaki Uçurum

Geliştirme kipinin düşürdüğü her adım, iki kip arasında bir davranış farkı olasılığı
yaratır. Ağaç sarsma kapalıysa, elenmemesi gereken bir modülün elendiği yayında ortaya
çıkar. Modüller ayrı dosyalar hâlinde sunuluyorsa, paketlemenin ürettiği değerlendirme
sırası farkı yayında ortaya çıkar. Küçültme kapalıysa, ada dayalı bir davranış yayında
kırılır.

Bu farkların hepsinin ortak bir sonucu vardır: **geliştirme kipinde çalışması, yayın
kipinde çalıştığının kanıtı değildir.** Bu yüzden yayın çıktısını yerelde çalıştırıp
denemek geliştirme akışının bir parçasıdır ve dağıtım konusundaki önizleme dağıtımları
dersi bu denemeyi her değişikliğe yayacaktır.

## Özet

- Geliştirme kipi paketleme, ağaç sarsma, küçültme ve içerik karmasını düşürür; belirteç
  çözümü, sözdizim çevirisi ve kaynak haritası kalır.
- Çizgeyi tarayıcının modül yükleyicisi dolaşır; sunucu tek bir modülü dönüştürüp verir,
  maliyet çizge boyutundan bağımsızlaşır.
- Örnekte ilk yükleme on üç modülün dokuzunu ister, panel üç modül daha ekler, dinamik
  sınırın ardındaki modül hiç istenmez; doğrulayıcı değişmeyen modülleri gövdesiz yanıtla
  geçiştirir.
- Sıcak değişim, geçersiz kılmanın isteyenlere doğru yayılmasıdır; kabul sınırı yayılımı
  durdurur, sınır bulunamazsa tam yükleme yapılır.
- Sınırın altındaki değişiklik bir modülü, ortak bir yardımcı modüldeki değişiklik on
  modülü yeniden değerlendirtir; sınırlar görünüm sınırlarına konur.
- Sıcak değişim durum sıfırlanması, eski kapanışlar ve birikmiş sapma riskleri taşır; şüphe
  hâlinde soğuk yükleme doğrulamanın ölçüsüdür.

## Sonraki Adım

Geliştirme ve yayın kipleri yalnız hızda değil, çalıştıkları ortamda da ayrılır. Yerelde
istasyon verisi bir deneme ucundan, yayında gerçek ölçüm hattından gelir; günlük ayrıntısı
yerelde açık, yayında kapalıdır; kimlik doğrulama uçları farklı adreslerdedir. Bu farkların
kaynağa gömülmesi kabul edilemez, çünkü aynı kaynaktan iki ayrı çıktı üretilir. Farkı
taşıyan şey derleme zamanı değişkenleridir. Sonraki ders bu değişkenlerin çıktıya nasıl
girdiğini gösterir ve tek kritik kuralı kurar: istemciye giden bir değişken herkese açıktır.
