---
title: 'Bağımlılık ve Bileşen Taraması'
source: 'https://academia.sh/tr/kurslar/islevsel-olmayan-test/bagimlilik-ve-bilesen-taramasi'
course: 'İşlevsel Olmayan Test'
language: tr
updated: '2026-08-17T18:10:52+00:00'
license: 'CC BY-SA 4.0'
---

# Bağımlılık ve Bileşen Taraması

Bilinen açıkların bağımlılık ağacında aranması: dolaylı bağımlılıkların payı, sürüm aralığı eşleşmesinin ürettiği yanlış alarm, önem eşiğinin taranması ve tek bir düzeltmenin sürüm aralıkları yüzünden kaç pakete birden dokunduğu.

İki tarama da bu takımın yazdığı kodu sınadı. Oysa ödünç servisinin çalıştırdığı satırların
çoğunu bu takım yazmadı: katalog şeması bir doğrulayıcıya, bildirim kanalı bir kuyruk
istemcisine, günlük yazımı bir biçimleyiciye dayanıyor ve bunların her biri kendi
bağımlılıklarını getiriyor.

Bu kodda kusur aramanın yolu taramak değil, **bilinen açıkları** aramaktır: bir bileşenin
hangi sürüm aralığında hangi kusuru taşıdığı kayıtlıdır ve tarama bu kayıtları kurulu
sürümlerle eşleştirir. Yöntem ucuzdur, ama üç yerde yanılır — ders bu üç yeri sayar.

## Bağımlılık Ağacı ve Kilit Dosyası

Ödünç sisteminin dört doğrudan bağımlılığı var. Kilit dosyası, kurulu sürümleri tam
sayılarla sabitler; tarama bu sürümlere bakar, bildirilen aralıklara değil.

```js
// agac.mjs — odunc sisteminin bagimlilik agaci, kilit dosyasi ve surum araligi hesabi
export const KOK = {
  "sema-dogrulayici": "^2.1.0", "kuyruk-istemcisi": "^1.4.0",
  "gunluk-bicimleyici": "^3.0.0", "kimlik-imzalayici": "^1.2.0",
};

// Her paketin her surumunun kendi bildirdigi araliklar.
export const DEPO = {
  "sema-dogrulayici": {
    "2.1.4": { "desen-derleyici": "^1.0.0", "metin-normallestirici": "^0.9.0" },
    "2.2.0": { "desen-derleyici": "^2.0.0", "metin-normallestirici": "^0.9.0" },
  },
  "kuyruk-istemcisi": {
    "1.4.2": { "cerceve-cozucu": "^4.2.0", "yeniden-deneme": "^1.1.0" },
    "1.5.0": { "cerceve-cozucu": "^4.3.0", "yeniden-deneme": "^1.2.0" },
  },
  "gunluk-bicimleyici": { "3.0.1": { "metin-normallestirici": "^0.9.0", "zaman-damgasi": "^2.0.0" } },
  "kimlik-imzalayici": { "1.2.0": { "sayisal-ozet": "^3.1.0" } },
  "desen-derleyici": { "1.0.7": {}, "2.0.0": {} },
  "metin-normallestirici": { "0.9.8": {} },
  "cerceve-cozucu": { "4.2.1": {}, "4.3.0": {} },
  "yeniden-deneme": { "1.1.0": {}, "1.2.0": {} },
  "zaman-damgasi": { "2.0.4": {}, "2.1.0": {} },
  "sayisal-ozet": { "3.1.2": {} },
};

// Kilit dosyasi: kurulu olan surumler.
export const KILIT = {
  "sema-dogrulayici": "2.1.4", "kuyruk-istemcisi": "1.4.2", "gunluk-bicimleyici": "3.0.1",
  "kimlik-imzalayici": "1.2.0", "desen-derleyici": "1.0.7", "metin-normallestirici": "0.9.8",
  "cerceve-cozucu": "4.2.1", "yeniden-deneme": "1.1.0", "zaman-damgasi": "2.0.4",
  "sayisal-ozet": "3.1.2",
};

export const parca = (v) => v.split(".").map(Number);
export const kucuk = (a, b) => {
  const [x, y] = [parca(a), parca(b)];
  for (let i = 0; i < 3; i += 1) if (x[i] !== y[i]) return x[i] < y[i];
  return false;
};
// "^a.b.c": ilk sifir olmayan basamak sabit kalir. "<a.b.c": kucuk olan her surum.
export function uyar(surum, aralik) {
  if (aralik.startsWith("<")) return kucuk(surum, aralik.slice(1));
  const t = parca(aralik.slice(1)), s = parca(surum);
  if (kucuk(surum, aralik.slice(1))) return false;
  const i = t.findIndex((x) => x > 0);
  return s.slice(0, i + 1).join(".") === t.slice(0, i + 1).join(".");
}

// Kilitli surumleri izleyerek her paketin derinligini bulur.
export function agac(kok = KOK, kilit = KILIT) {
  const derinlik = {}, sira = Object.keys(kok).map((a) => [a, 1]);
  while (sira.length) {
    const [ad, d] = sira.shift();
    if (derinlik[ad] !== undefined && derinlik[ad] <= d) continue;
    derinlik[ad] = d;
    for (const alt of Object.keys(DEPO[ad][kilit[ad]])) sira.push([alt, d + 1]);
  }
  return derinlik;
}
```

## Eşleşme ve Yanlış Alarm

Kayıt kümesi kendi kayıtlarımızdır: her kayıt bir paketi, etkilenen bir sürüm aralığını,
bir önem puanını ve düzeltilmiş sürümü taşır. Tarama, kilitli sürümün etkilenen aralığa
düşüp düşmediğine bakar — bu, bir metin karşılaştırmasından biraz karmaşık bir sürüm
karşılaştırmasıdır ve başka hiçbir bilgi kullanmaz.

**IO10 (varsayım):** gerçek risk, kusurlu kodun bu sistemde **çağrılmasıdır**. Bir paket
etkilenen aralıkta olabilir ama kusurlu işlevi bu sistemde hiç çağrılmıyor olabilir; elle
doğrulanmış risk listesi bu ayrımı yapar, tarama yapamaz.

```js
// tarama.mjs — kayit kumesinin kilit dosyasiyla eslesmesi, yanlis alarmlar ve esik taramasi
import { KILIT, uyar, agac } from "./agac.mjs";

const derinlik = agac();
const dogrudan = Object.keys(derinlik).filter((a) => derinlik[a] === 1);
const n = Object.keys(derinlik).length;
console.log(`${n} paket, dogrudan ${dogrudan.length}, dolayli ${n - dogrudan.length}` +
  ` (%${((100 * (n - dogrudan.length)) / n).toFixed(0)}), en buyuk derinlik ${Math.max(...Object.values(derinlik))}`);

// Kendi kayit kumemiz: paket, etkilenen aralik, onem, duzeltilmis surum.
const KAYIT = [
  { no: "K-01", paket: "desen-derleyici", etkilenen: "<2.0.0", onem: 80, duzelten: "2.0.0" },
  { no: "K-02", paket: "metin-normallestirici", etkilenen: "<0.9.8", onem: 60, duzelten: "0.9.8" },
  { no: "K-03", paket: "cerceve-cozucu", etkilenen: "<4.3.0", onem: 70, duzelten: "4.3.0" },
  { no: "K-04", paket: "kuyruk-istemcisi", etkilenen: "<1.5.0", onem: 90, duzelten: "1.5.0" },
  { no: "K-05", paket: "zaman-damgasi", etkilenen: "<2.1.0", onem: 30, duzelten: "2.1.0" },
];

// IO10: elle dogrulanmis gercek risk, kusurlu kodun bu sistemde cagrilmasidir.
const GERCEK = ["desen-derleyici", "kuyruk-istemcisi", "yeniden-deneme"];

const bulgu = KAYIT.filter((k) => uyar(KILIT[k.paket], k.etkilenen));
console.log(`${KAYIT.length} kayit, ${bulgu.length} eslesme, ${GERCEK.length} elle dogrulanmis risk\n`);
console.log(`${"kayit".padEnd(6)}${"paket".padEnd(23)}${"kurulu".padEnd(8)}${"etkilenen".padEnd(11)}onem  derinlik  gercek`);
for (const k of KAYIT) {
  const e = uyar(KILIT[k.paket], k.etkilenen);
  console.log(`${k.no.padEnd(6)}${k.paket.padEnd(23)}${KILIT[k.paket].padEnd(8)}${k.etkilenen.padEnd(11)}` +
    `${String(k.onem).padStart(4)}${String(derinlik[k.paket]).padStart(10)}  ${e ? (GERCEK.includes(k.paket) ? "evet" : "hayir") : "-"}`);
}

console.log(`\n${"esik".padStart(4)}${"kalan".padStart(7)}${"yanlis alarm".padStart(14)}${"kacirma".padStart(9)}${"maliyet w=5".padStart(12)}`);
for (const e of [20, 30, 50, 70, 80, 90]) {
  const k = bulgu.filter((b) => b.onem >= e);
  const ya = k.filter((b) => !GERCEK.includes(b.paket)).length;
  const kac = GERCEK.length - k.filter((b) => GERCEK.includes(b.paket)).length;
  console.log(`${String(e).padStart(4)}${String(k.length).padStart(7)}${String(ya).padStart(14)}${String(kac).padStart(9)}${String(ya + 5 * kac).padStart(12)}`);
}
console.log(`kayit kumesinde hic gecmeyen gercek risk: ${GERCEK.filter((g) => !KAYIT.some((k) => k.paket === g)).join(", ")}`);
```

```
10 paket, dogrudan 4, dolayli 6 (%60), en buyuk derinlik 2
5 kayit, 4 eslesme, 3 elle dogrulanmis risk

kayit paket                  kurulu  etkilenen  onem  derinlik  gercek
K-01  desen-derleyici        1.0.7   <2.0.0       80         2  evet
K-02  metin-normallestirici  0.9.8   <0.9.8       60         2  -
K-03  cerceve-cozucu         4.2.1   <4.3.0       70         2  hayir
K-04  kuyruk-istemcisi       1.4.2   <1.5.0       90         1  evet
K-05  zaman-damgasi          2.0.4   <2.1.0       30         2  hayir

esik  kalan  yanlis alarm  kacirma maliyet w=5
  20      4             2        1           7
  30      4             2        1           7
  50      3             1        1           6
  70      3             1        1           6
  80      2             0        1           5
  90      1             0        2          10
kayit kumesinde hic gecmeyen gercek risk: yeniden-deneme
```

Üç sayı okunmalı. Birincisi **dolaylı bağımlılıkların payı**: on paketin altısı hiçbir
yerde bildirilmedi, dört bildirimin altından geldi. Kilit dosyası olmasa bu altı paketin
hangi sürümde olduğu bile bilinmezdi ve tarama sürüm bilmeden çalışamaz.

İkincisi **yanlış alarm**: dört eşleşmenin ikisi gerçek risk değil. İkisi de sürüm
karşılaştırması açısından doğru — kurulu sürüm etkilenen aralıkta — ama kusurlu kod yolu bu
sistemde çalışmıyor. Sürüm aralığı eşleşmesi paketin varlığını ölçer, kullanımını değil.

Üçüncüsü **kaçırma**: `yeniden-deneme` içindeki kusur elle doğrulanmış risk listesinde var,
kayıt kümesinde yok. Bağımlılık taramasının yapısal sınırı budur — yalnız **bildirilmiş**
kusurları bulur ve hiçbir eşik bildirilmemiş bir kusuru göremez.

**Eşiğin kaynağı ölçümdür:** eşik taraması, önem 80'de iki yanlış alarmın da düştüğünü ve
kalan iki bulgunun ikisinin de gerçek çıktığını gösteriyor; maliyet en düşük değerine
orada iniyor. Ağırlık yine IO8'den geliyor.

## Düzeltmenin Yayılması

Eşiği geçen iki kayıt düzeltilecek. Düzeltme tek bir sürüm numarası değiştirmek gibi
görünür; sürüm aralıkları bunu engeller.

```js
// duzeltme.mjs — bir kaydin duzeltilmesinin kac pakete dokundugu
import { DEPO, KOK, KILIT, uyar, kucuk, agac } from "./agac.mjs";

const surumler = (ad) => Object.keys(DEPO[ad]).sort((a, b) => (kucuk(a, b) ? -1 : 1));
const enDusukUyan = (ad, aralik) => surumler(ad).find((v) => uyar(v, aralik));
const ustler = (ad, kilit) => Object.keys(DEPO).filter((u) => DEPO[u][kilit[u]]?.[ad]);

function yukselt(kilit, paket, hedef) {
  const yeni = { ...kilit }, dokunulan = new Set(), engel = [];
  const sira = [[paket, hedef]];
  while (sira.length) {
    const [ad, s] = sira.shift();
    if (yeni[ad] === s) continue;
    yeni[ad] = s;
    dokunulan.add(ad);
    for (const [alt, aralik] of Object.entries(DEPO[ad][s]))
      if (!uyar(yeni[alt], aralik)) sira.push([alt, enDusukUyan(alt, aralik)]);
    for (const u of ustler(ad, yeni)) {
      if (uyar(s, DEPO[u][yeni[u]][ad])) continue;
      const uygun = surumler(u).find((v) => DEPO[u][v][ad] && uyar(s, DEPO[u][v][ad]));
      uygun ? sira.push([u, uygun]) : engel.push(`${u} icin uygun surum yok`);
    }
    if (KOK[ad] && !uyar(s, KOK[ad])) engel.push(`kok araligi ${KOK[ad]} ${ad}@${s} kabul etmiyor`);
  }
  return { yeni, dokunulan: [...dokunulan], engel };
}

let birlesik = KILIT;
for (const [no, paket, hedef] of [["K-01", "desen-derleyici", "2.0.0"], ["K-04", "kuyruk-istemcisi", "1.5.0"]]) {
  const { yeni, dokunulan, engel } = yukselt(KILIT, paket, hedef);
  console.log(`${no}: ${paket} ${KILIT[paket]} -> ${hedef}; ${dokunulan.length} pakete dokunur`);
  for (const a of dokunulan) console.log(`   ${a.padEnd(23)}${KILIT[a]} -> ${yeni[a]}`);
  if (engel.length) console.log(`   engel: ${engel.join("; ")}`);
  birlesik = yukselt(birlesik, paket, hedef).yeni;
}

const degisen = Object.keys(KILIT).filter((a) => birlesik[a] !== KILIT[a]);
const n = Object.keys(KILIT).length;
console.log(`\niki duzeltme birlikte: ${degisen.length}/${n} paket (%${((100 * degisen.length) / n).toFixed(0)}) yeni surume gecti`);
console.log(`agac derinligi degismedi: ${Math.max(...Object.values(agac(KOK, birlesik)))}`);
console.log(`yan etki: K-03'un isaret ettigi cerceve-cozucu ${KILIT["cerceve-cozucu"]} -> ${birlesik["cerceve-cozucu"]}`);
```

```
K-01: desen-derleyici 1.0.7 -> 2.0.0; 2 pakete dokunur
   desen-derleyici        1.0.7 -> 2.0.0
   sema-dogrulayici       2.1.4 -> 2.2.0
K-04: kuyruk-istemcisi 1.4.2 -> 1.5.0; 3 pakete dokunur
   kuyruk-istemcisi       1.4.2 -> 1.5.0
   cerceve-cozucu         4.2.1 -> 4.3.0
   yeniden-deneme         1.1.0 -> 1.2.0

iki duzeltme birlikte: 5/10 paket (%50) yeni surume gecti
agac derinligi degismedi: 2
yan etki: K-03'un isaret ettigi cerceve-cozucu 4.2.1 -> 4.3.0
```

İlk düzeltme yukarı yayıldı. `desen-derleyici` düzeltilmiş sürümü büyük basamağı
artırdığı için üstündeki paketin bildirdiği aralık onu kabul etmiyor; bu yüzden ara paket
de yükseltilmek zorunda. İkinci düzeltme aşağı yayıldı: yeni sürüm kendi bağımlılıklarından
daha yenisini istiyor. İki düzeltme birlikte on paketin beşini oynattı.

Bu sayı taramanın gerçek bedelidir. Beş paketin yeni sürümü, güvenlik kaydıyla ilgisi
olmayan davranış değişiklikleri de getirir; yükseltmeyi izleyen gerileme testi kaçınılmazdır
ve maliyeti bulgu sayısıyla değil **dokunulan paket sayısıyla** ölçülür. Son satır tersini
de gösteriyor: yanlış alarm sayılan bir kayıt, başka bir düzeltmenin yan etkisiyle bedava
kapandı.

**Kararın sahibi:** önem 80'in üstündeki eşleşme yayımı durdurur ve bir yükseltme işi açar;
altındakiler bir listede birikir. Kayıt kümesi her gün değişebildiği için aynı kilit dosyası
bugün yeşil, yarın kırmızı olabilir — bu tarama, kod değişmeden sonucu değişen tek
denetimdir.

## Özet

- Bağımlılık taraması kilit dosyasındaki sürümleri kayıt kümesindeki etkilenen aralıklarla
  eşleştirir; bu ağaçta on paketin altısı (%60) hiçbir yerde bildirilmemiş dolaylı
  bağımlılıktır.
- Dört eşleşmenin ikisi yanlış alarm çıktı: sürüm doğru eşleşti ama kusurlu kod yolu bu
  sistemde çağrılmıyor. Sürüm aralığı eşleşmesi varlığı ölçer, kullanımı değil.
- Yakalanan sınıf bildirilmiş bir kusurun kurulu sürümde bulunmasıdır; kaçan sınıf henüz
  bildirilmemiş kusurdur — elle doğrulanmış üç riskten biri hiçbir kayıtta geçmiyordu.
- Eşik taraması önem 80'i verdi: 0 yanlış kalma, 1 yanlış geçme. Eşiğin kaynağı bu ölçüm,
  ağırlığın kaynağı IO8'dir.
- Bedel dokunulan paket sayısıdır: iki düzeltme sürüm aralıkları üzerinden yukarı ve aşağı
  yayılarak on paketin beşini yeni sürüme taşıdı ve her biri gerileme testi ister.
- Bu tarama, kod hiç değişmeden sonucu değişebilen tek denetimdir; kayıt kümesi büyüdükçe
  aynı kilit dosyası yeniden kırmızıya döner.

## Sonraki Adım

Üç yöntem de bir kural kümesine bakıyordu: statik tarama desenlere, dinamik test yanıt
imzalarına, bağımlılık taraması kayıtlara. Üçünün ortak varsayımı, aranan şeyin **önceden
adlandırılmış** olmasıdır. Ödünç sisteminin en pahalı kusuru ise hiçbir listede yoktur:
üyelik bitmiş bir okuyucunun ceza kaydını başka bir üyenin üstüne taşıyabilmesi, ayrı ayrı
bakıldığında her adımı yetkili olan bir istek dizisidir. Hiçbir desen bunu adlandırmaz,
çünkü kusur kodda değil kuralların birleşimindedir. Sonraki ders bu sınırı çizer: bir tehdit
modeli üzerinde otomasyonun kapsayabildiği kusur sınıflarını insan muhakemesi gerektiren
sınıflardan ayırır ve ikisinin arasında kalan payı sayar.
