---
title: 'Kod Bölme'
source: 'https://academia.sh/tr/kurslar/olusturma-stratejileri/kod-bolme'
course: 'Oluşturma Stratejileri ve Altyapı'
language: tr
updated: '2026-08-17T18:09:30+00:00'
license: 'CC BY-SA 4.0'
---

# Kod Bölme

Statik ve dinamik kenarların ayrılması; parça köklerinin belirlenmesi, ilk yüklemede inen ile sonradan inenin hesaplanması, istek başına maliyet ve aşırı bölünmüş parçaların birleştirilmesi.

Bir önceki dersteki parçalar giriş noktalarından doğdu ve hepsi ilk yüklemede gerekliydi.
Tarayıcı girişinin kapanışındaki altı modülün altısı da, kullanıcı henüz hiçbir şeye
dokunmadan indi. Oysa Kuzey Yamaç Ölçüm İstasyonu uygulamasında bu doğru değildir: ölçüm
listesi her ziyarette görünür, istasyon sorumlusunun paneli ise yalnız o panel açıldığında
gerekir. Panelin grafik çizimi ve dışa aktarma kodu, sayfayı okumaya gelen ziyaretçinin
indirdiği baytlar arasında bulunmamalıdır.

Bu ayrımı kaynakta işaretleyen bir yazım vardır. `import()` çağrısı bir bağımlılığı
çizgeye ekler ama onu ilk yüklemeden ayırır. Bu dersin sorusu, o işaretin çıktı parçalarını
nasıl değiştirdiği ve bölmenin nerede kâr getirip nerede zarara döndüğüdür.

## İki Kenar Türü

Çizgede iki tür kenar vardır ve ayrım zamandadır.

**Statik kenar**, kaynağın en üstündeki içe aktarım bildirimidir. Modül sistemleri
konusunda kurulan kural gereği bu bildirimler modül değerlendirilmeden önce çözülür:
bağımlılık, kendisine bağlı olan modülden önce hazır olmak zorundadır. Statik kenarla
bağlanan iki modül aynı anda gerekir.

**Dinamik kenar**, `import()` çağrısıdır. Çağrı bir söz döndürür; bağımlılık, çağrı
yürütülene kadar yüklenmez. Dinamik kenarla bağlanan iki modül aynı anda gerekmez ve
aralarında bir zaman sınırı vardır.

Bu zaman sınırı, çıktı tarafında bir dosya sınırına dönüşür. Dinamik içe aktarımın hedefi,
kendi bağımlılıklarıyla birlikte ayrı bir dosyaya konabilir; o dosya, çağrı yürütüldüğünde
indirilir. Böyle bir hedefe **parça kökü (chunk root)** denir. Bir derlemede parça kökleri
kümesi, giriş noktaları ile dinamik içe aktarım hedeflerinin birleşimidir.

## Panelin Ayrılması

Kaynak ağaca panel ve bağımlılıkları eklenir; tarayıcı girişi grafiği doğrudan içe
aktarmak yerine paneli dinamik olarak ister. Aşağıdaki blok, bir önceki dersin kaynak
ağacının üzerine yazar.

Aşağıdaki blok, Modül Paketleme dersinde kurulan `kaynak/` ağacının üzerine yazar; betiği çalıştırmadan önce o dersin kurulum bloğu bu dizinde çalıştırılmış olmalıdır.

```bash
mkdir -p kaynak

cat > kaynak/uyari.js <<'EOF'
import { SICAKLIK_ALT, SICAKLIK_UST } from "./birim.js";

export const esikAsimi = (olcumler) =>
  olcumler.filter((o) => o.sicaklik < SICAKLIK_ALT || o.sicaklik > SICAKLIK_UST);
EOF

cat > kaynak/disa-aktarma.js <<'EOF'
import { ikiliBirim } from "./bicim.js";
import { gunAnahtari } from "./tarih.js";

const kacir = (a) => (a.includes(",") ? "\"" + a + "\"" : a);

export function csvUret(olcumler) {
  const basliklar = ["gun", "damga", "sicaklik", "nem"];
  const satirlar = olcumler.map((o) =>
    [gunAnahtari(o.damga), o.damga, ikiliBirim(o), o.nem].map(kacir).join(","));
  return [basliklar.join(","), ...satirlar].join("\n");
}
EOF

cat > kaynak/panel.js <<'EOF'
import { sicaklikYolu } from "./grafik.js";
import { csvUret } from "./disa-aktarma.js";
import { baslik } from "./sablon.js";

export async function paneliCiz(kok, istasyon, olcumler) {
  const { esikAsimi } = await import("./uyari.js");
  const asim = esikAsimi(olcumler);
  kok.innerHTML = baslik(istasyon + " paneli") +
    "<svg><path d=\"" + sicaklikYolu(olcumler, 320, 120) + "\"/></svg>" +
    "<p>" + asim.length + " asim</p>";
  return { csv: () => csvUret(olcumler) };
}
EOF

cat > kaynak/giris-tarayici.js <<'EOF'
import { liste, baslik } from "./sablon.js";

export function baslat(kok, istasyon, olcumler) {
  kok.innerHTML = baslik(istasyon) + liste(olcumler);
  return {
    async paneliAc(panelKok) {
      const { paneliCiz } = await import("./panel.js");
      return paneliCiz(panelKok, istasyon, olcumler);
    },
  };
}
EOF
```

İki dinamik sınır oluştu: giriş noktası paneli, panel de eşik uyarısı modülünü dinamik
olarak istiyor. İkincisi iç içe bir sınırdır ve maliyeti ayrıca ölçülecek.

## Parça Köklerinden Parçalara

Bir önceki dersin imza ölçütü aynen geçerlidir, yalnız imzayı üreten küme değişir: giriş
noktaları yerine parça kökleri. Bir modülün imzası, onu **statik olarak** erişebilen
kökler kümesidir. İmzasında giriş noktası bulunan modül ilk yüklemede iner; bulunmayan
modül, imzasındaki köklerden biri istendiğinde iner.

```js
// bolme.mjs — statik ve dinamik kenarlardan parca cizgesi, maliyet ve birlestirme.
import { readFileSync, statSync } from "node:fs";
import { dirname, join, relative } from "node:path";

const GIRIS = "kaynak/giris-tarayici.js";
const STATIK = /^\s*(?:import|export)[^"']*from\s*["']([^"']+)["']/gm;
const DINAMIK = /\bimport\(\s*["']([^"']+)["']\s*\)/g;

const cizge = new Map();                        // yol -> { boyut, statik[], dinamik[] }
const kuyruk = [GIRIS];
while (kuyruk.length > 0) {
  const yol = kuyruk.shift();
  if (cizge.has(yol)) continue;
  const kaynak = readFileSync(yol, "utf8");
  const coz = (e) => [...kaynak.matchAll(e)].map(([, b]) => relative(".", join(dirname(yol), b)));
  const statik = coz(STATIK), dinamik = coz(DINAMIK);
  cizge.set(yol, { boyut: statSync(yol).size, statik, dinamik });
  kuyruk.push(...statik, ...dinamik);
}

// Parca koku: giris noktasi ve her dinamik ice aktarim hedefi.
const kokler = [GIRIS, ...[...cizge.values()].flatMap((d) => d.dinamik)];
const kapanis = new Map(kokler.map((kok) => {
  const gorulen = new Set(), yigit = [kok];
  while (yigit.length > 0) {
    const y = yigit.pop();
    if (gorulen.has(y)) continue;
    gorulen.add(y);
    yigit.push(...cizge.get(y).statik);
  }
  return [kok, gorulen];
}));

// Imza: modulu statik olarak isteyen kokler kumesi. Ayni imza = ayni parca.
const gruplar = new Map();
for (const yol of cizge.keys()) {
  const anahtar = kokler.filter((k) => kapanis.get(k).has(yol)).join("|");
  if (!gruplar.has(anahtar)) gruplar.set(anahtar, []);
  gruplar.get(anahtar).push(yol);
}

const ad = (y) => y.replace("kaynak/", "").replace(".js", "");
const bayt = (ms) => ms.reduce((t, y) => t + cizge.get(y).boyut, 0);
let parcalar = [...gruplar].map(([anahtar, moduller], i) => ({
  ad: "P" + (i + 1),
  kokler: anahtar.split("|"),
  moduller,
  boyut: bayt(moduller),
  ilk: anahtar.split("|").includes(GIRIS),
}));

const RTT = 60, BANT = 192;                     // ms; B/ms (1.5 Mbit/s)
const maliyet = (liste) => liste.length * RTT + liste.reduce((t, p) => t + p.boyut, 0) / BANT;
const eszamansiz = (liste, kok) => liste.filter((p) => p.kokler.includes(kok) && !p.ilk);

function rapor(baslik, liste) {
  console.log(baslik);
  for (const p of liste.sort((a, b) => a.ad.localeCompare(b.ad))) {
    console.log("  " + p.ad + "  " + String(p.boyut).padStart(4) + " B  " +
      (p.ilk ? "ilk        " : "eszamansiz ") + p.moduller.map(ad).sort().join(", "));
  }
  const ilkListe = liste.filter((p) => p.ilk);
  console.log("  ilk yukleme      " + ilkListe.length + " istek  " +
    String(ilkListe.reduce((t, p) => t + p.boyut, 0)).padStart(4) + " B  " +
    maliyet(ilkListe).toFixed(1).padStart(6) + " ms");
  for (const kok of kokler.slice(1)) {
    const g = eszamansiz(liste, kok);
    if (g.length === 0) continue;
    console.log("  " + (ad(kok) + " siniri").padEnd(17) + g.length + " istek  " +
      String(g.reduce((t, p) => t + p.boyut, 0)).padStart(4) + " B  " +
      maliyet(g).toFixed(1).padStart(6) + " ms");
  }
}

console.log("-- cizge --");
for (const [yol, d] of [...cizge].sort()) {
  console.log("  " + ad(yol).padEnd(16) + String(d.boyut).padStart(4) + " B  statik: " +
    (d.statik.map(ad).join(", ") || "-") + "  dinamik: " + (d.dinamik.map(ad).join(", ") || "-"));
}
console.log("  parca koku: " + kokler.map(ad).join(", "));

rapor("-- imzaya gore parcalar (RTT " + RTT + " ms, bant 1.5 Mbit/s) --", parcalar);

// 1. Girisle birlikte inen parcalar ayri tutulmaz: hepsi tek ilk parcada birlesir.
const ilkler = parcalar.filter((p) => p.ilk);
if (ilkler.length > 1) {
  const birlesik = {
    ad: "I1", kokler: [...new Set(ilkler.flatMap((p) => p.kokler))],
    moduller: ilkler.flatMap((p) => p.moduller), boyut: bayt(ilkler.flatMap((p) => p.moduller)),
    ilk: true,
  };
  parcalar = [birlesik, ...parcalar.filter((p) => !p.ilk)];
  console.log("-- birlestirme: " + ilkler.map((p) => p.ad).join(" + ") + " -> I1 (hepsi ilk yuklemede iniyor)");
}

// 2. Esigin altinda kalan ve tek yerden istenen eszamansiz parca, isteyenin parcasina girer.
const ESIK = 300;
for (const p of [...parcalar]) {
  if (p.ilk || p.boyut >= ESIK || p.kokler.length !== 1) continue;
  const isteyen = [...cizge].filter(([, d]) => d.dinamik.includes(p.kokler[0])).map(([y]) => y);
  const hedefler = [...new Set(isteyen.map((y) => parcalar.find((q) => q.moduller.includes(y))))];
  if (hedefler.length !== 1 || hedefler[0].ilk) continue;
  const hedef = hedefler[0];
  hedef.moduller.push(...p.moduller);
  hedef.boyut += p.boyut;
  parcalar = parcalar.filter((q) => q !== p);
  console.log("-- birlestirme: " + p.ad + " (" + p.boyut + " B < " + ESIK +
    " B, tek isteyen) -> " + hedef.ad);
}

rapor("-- birlestirme sonrasi --", parcalar);
```

```
$ node bolme.mjs
-- cizge --
  bicim            387 B  statik: tarih, birim  dinamik: -
  birim            186 B  statik: -  dinamik: -
  disa-aktarma     418 B  statik: bicim, tarih  dinamik: -
  giris-tarayici   316 B  statik: sablon  dinamik: panel
  grafik           458 B  statik: birim  dinamik: -
  panel            482 B  statik: grafik, disa-aktarma, sablon  dinamik: uyari
  sablon           229 B  statik: bicim  dinamik: -
  tarih            227 B  statik: -  dinamik: -
  uyari            179 B  statik: birim  dinamik: -
  parca koku: giris-tarayici, panel, uyari
-- imzaya gore parcalar (RTT 60 ms, bant 1.5 Mbit/s) --
  P1   316 B  ilk        giris-tarayici
  P2   843 B  ilk        bicim, sablon, tarih
  P3  1358 B  eszamansiz disa-aktarma, grafik, panel
  P4   179 B  eszamansiz uyari
  P5   186 B  ilk        birim
  ilk yukleme      3 istek  1345 B   187.0 ms
  panel siniri     1 istek  1358 B    67.1 ms
  uyari siniri     1 istek   179 B    60.9 ms
-- birlestirme: P1 + P2 + P5 -> I1 (hepsi ilk yuklemede iniyor)
-- birlestirme: P4 (179 B < 300 B, tek isteyen) -> P3
-- birlestirme sonrasi --
  I1  1345 B  ilk        bicim, birim, giris-tarayici, sablon, tarih
  P3  1537 B  eszamansiz disa-aktarma, grafik, panel, uyari
  ilk yukleme      1 istek  1345 B    67.0 ms
  panel siniri     1 istek  1537 B    68.0 ms
```

## Çıktının Okunması

İlk tablo ham imza gruplamasını verir. Panelin kendisi, grafiği ve dışa aktarma modülü tek
bir eşzamansız parçada toplanmıştır: üçünü de yalnız panel kökü ister. Şablon, biçim ve
tarih modülleri hem giriş noktasından hem panelden erişilebildiği için ilk yüklemededir —
paneli bekletmek için bir gerekçe yoktur, zaten inmişlerdir.

Bölmenin kazancı ilk satırdadır: toplam 2882 baytın 1345'i ilk yüklemede iner. Panelin
1537 baytı, o panel hiç açılmazsa hiç inmez. Oran küçük bir örnekte gösteriştir; gerçek bir
uygulamada grafik çizimi ve dışa aktarma gibi modüllerin ağırlığı, ilk yüklemenin birkaç
katı olabilir.

`birim.js` modülünün ayrı bir parçaya düşmesi imza ölçütünün doğrudan sonucudur: onu üç
kök de ister, dolayısıyla imzası diğerlerinden farklıdır. Ölçüt bu ayrımı doğru yapar ama
sonucu her zaman yararlı değildir; bir sonraki bölüm bunu düzeltir.

## Aşırı Bölmenin Maliyeti

Her parça bir istektir. İstek başına gecikme, taşınan baytın süresinden büyük olabilir;
maliyet satırları bu iki kalemi ayrı ayrı toplar. İlk yüklemede üç parça 187 milisaniye
tutar, oysa aynı baytlar tek dosyada 67 milisaniyede iner. Aradaki 120 milisaniye, hiçbir
şey kazandırmayan iki fazladan istektir.

Buradan iki birleştirme kuralı çıkar.

**Birlikte inen parçalar ayrı tutulmaz.** İmzasında giriş noktası bulunan her parça ilk
yüklemede iner; bunları ayrı dosyalarda tutmanın tek etkisi istek sayısını artırmaktır. Üç
parça tek bir ilk parçada birleşir ve ilk yükleme 187 milisaniyeden 67 milisaniyeye iner.
Bu kuralın bir istisnası vardır ve önbelleklemeyle ilgilidir: iki farklı giriş noktasının
paylaştığı bir parça — bir önceki dersteki tarayıcı ve sunucu ortaklığı gibi — ayrı
tutulduğunda iki hedefte de bir kez indirilir. Aynı gerekçe sürümler arasında da geçerlidir
ve beşinci derste ölçülecektir.

**Eşiğin altında kalan parça ayrılmaz.** Eşik uyarısı modülü 179 bayttır ve tek bir yerden
istenir. Ayrı bir dosya olarak tutulduğunda kendisine 60,9 milisaniyelik bir istek
maliyeti ödetir; panelin parçasına katıldığında ise panelin indirmesini 67,1 milisaniyeden
68,0 milisaniyeye çıkarır. Fark tek yönlüdür: bölme, kazandırdığından fazlasını götürür.

İç içe dinamik sınırın gizli bir maliyeti daha vardır. Eşik uyarısı, panel yüklendikten
**sonra** istenir; iki istek sıralıdır. Kullanıcı paneli açtığında bekleme 67,1 + 60,9 =
128 milisaniyedir, birleştirmeden sonra 68,0 milisaniye. Sıralı istek zinciri, oluşturma
modelleri konusunda istemcide oluşturmanın maliyeti olarak ölçülen aynı olgudur; parça
düzeyinde de aynı biçimde çalışır.

## Sınır Nereye Konur

Bölme sınırı koymanın ölçütü dört sorudur ve dördü de ölçülebilir.

**İlk ekranda gerekli mi?** Gerekliyse bölünmez; bölme yalnız gecikme ekler. Panel için
yanıt hayırdır, ölçüm listesi için evet.

**Kaçınılan bayt, eklenen isteği karşılıyor mu?** Karşılamıyorsa bölünmez. Örnekteki eşik
300 bayttır ve gösterim içindir; gerçek bir eşik, ölçülen istek gecikmesi ile bant
genişliğinden hesaplanır.

**Kullanıcı eyleminin doğal bir beklemesi var mı?** Bir düğmeye basıldıktan sonra kısa bir
bekleme kabul edilebilir; sayfa ilk açılırken beklemek kabul edilmez. Bölme sınırları
eylem sınırlarına yerleştirilir.

**Kod ne sıklıkla isteniyor?** Ziyaretçilerin çoğunun açtığı bir ekranın kodunu
geciktirmek, ilk yüklemeden kazanılanı gezinmede fazlasıyla geri verir. Uygulama Mimarisi
kursundaki kod bölme dersi bu takası rota sınırlarında ele almış ve önceden getirmeyi
çözüm olarak koymuştu; ölçüt aynıdır, sınırın kaynağı farklıdır — orada rota tanımı, burada
`import()` çağrısının bulunduğu yer.

## Bölmenin Getirdiği Yükümlülükler

Bölünmüş bir çıktı, tek dosyalı bir çıktının taşımadığı üç yükümlülük getirir.

**Parça adları çalışma zamanında çözülmelidir.** Giriş parçası, `import()` çağrısı
yürütüldüğünde hangi dosyayı isteyeceğini bilmek zorundadır. Bu bilgi çıktıya bir eşleme
olarak yazılır; beşinci ders bu eşlemeyi içerik karmalı adlarla birlikte kuracak.

**Bir parçanın yokluğu çalışma zamanı hatasıdır.** Kullanıcı sayfayı açıkken yeni bir
sürüm yayımlanırsa, eski sayfanın isteyeceği parça adları sunucuda bulunmayabilir. Bu,
dağıtım konusundaki geri alma dersinin doğrudan konusudur ve çözümü eski sürümün
dosyalarını bir süre daha yayında tutmaktır.

**Yükleme sırası korunmalıdır.** Bir eşzamansız parça, bağımlı olduğu ilk parça
değerlendirilmeden çalıştırılamaz. Çıktıdaki yükleyici bu bağı izler; parça sayısı
arttıkça izlenecek bağ sayısı da artar.

## Özet

- Çizgedeki iki kenar türü zamanla ayrılır: statik içe aktarım aynı anda gerekliliği,
  `import()` ise zaman sınırı olan bir bağımlılığı bildirir.
- Parça kökleri kümesi giriş noktaları ile dinamik içe aktarım hedeflerinin birleşimidir;
  imzasında giriş noktası bulunan modül ilk yüklemede, bulunmayan modül kökü istendiğinde
  iner.
- Örnekte toplam 2882 baytın 1345'i ilk yüklemede iniyor; panelin 1537 baytı yalnız panel
  açılırsa indiriliyor.
- Her parça bir istektir: birlikte inen parçaları ayrı tutmak ilk yüklemeyi 67
  milisaniyeden 187 milisaniyeye çıkarır, eşiğin altındaki bir parçayı ayırmak ise
  kazandırdığından fazlasını götürür.
- İç içe dinamik sınırlar istekleri sıralı hâle getirir; iki sınır tek parçada
  birleştirildiğinde bekleme 128 milisaniyeden 68 milisaniyeye iner.
- Bölme sınırı dört ölçütle konur: ilk ekranda gereklilik, kaçınılan baytın istek
  maliyetini karşılaması, eylemin doğal beklemesi ve kodun istenme sıklığı.

## Sonraki Adım

Parçalar artık hem sayıca hem zamanca doğru ayrılmış durumda. Ama parçaların içeriği hâlâ
modül düzeyinde belirleniyor: çizge bir modülün *gerekli* olduğunu söylüyor, içindeki
hangi adın kullanıldığını söylemiyor. Paylaşılan parçadaki birim modülü dört ad dışa
aktarıyor ve bunların hepsi kullanılmıyor; biçimlendirme modülünün ikinci işlevi yalnız
dışa aktarma yolunda çağrılıyor. Sonraki ders çizgeyi modül düzeyinden ad düzeyine indirir:
kullanılmayan dışa aktarımların elenmesi, bu elemenin hangi durumlarda yapılamadığı ve
paketin bunu bildirimle nasıl mümkün kıldığı.
