---
title: 'Bağımlılık Ağacı ve Çakışmalar'
source: 'https://academia.sh/tr/kurslar/javascript-ekosistemi/bagimlilik-agaci-ve-cakismalar'
course: 'Modüller, Araçlar ve Ekosistem'
language: tr
updated: '2026-08-17T18:09:45+00:00'
license: 'CC BY-SA 4.0'
---

# Bağımlılık Ağacı ve Çakışmalar

Kesişmeyen sürüm isteklerinin iç içe kurulumla çözülmesi, yinelenen kopyaların ürettiği kimlik ve durum sorunları, ağacın düzleştirilmesi ve tekillik gerektiren bağımlılıklar.

Önceki ders tek bir aralığı inceledi. Bir projede onlarca paket bulunur ve bunların çoğu
kendi bağımlılıklarını taşır. Aynı pakete iki farklı yerden, iki farklı aralıkla istek
gelmesi olağandır.

İstekler kesişiyorsa sorun yoktur: kesişimden bir sürüm seçilir ve herkes onu kullanır.
Kesişmediğinde ne olur? Bu dersin konusu, o durumun çözümü ve çözümün bedelidir.

## Kesişmeyen İstekler

Ölçüm projesi `ortak` adlı bir paketin ikinci ana sürümünü ister. Projenin kullandığı
`ceviri` paketi ise aynı paketin birinci ana sürümüne bağlıdır. `^2.0.0` ile `^1.0.0`
kümelerinin kesişimi boştur.

Sistem paket yöneticilerinde bu durum, Linux'a Giriş kursundaki paket yöneticisi
kavramı dersinde anlatıldığı gibi bir çakışma bildirimiyle sonuçlanır: bir kütüphanenin
sistemde tek bir sürümü kurulabilir. Modül çözümlemesi ise konuma bağlı olduğu için başka
bir çıkış yolu sunar — aynı paketin iki sürümü, ağacın iki ayrı düzeyinde durabilir.

İki bildirim, çakışan istekleri yazılı hâlde gösterir:

```json
{
  "name": "proje", "version": "1.0.0", "type": "module",
  "dependencies": { "ceviri": "^3.1.0", "ortak": "^2.0.0" }
}
```

```json
{
  "name": "ceviri", "version": "3.1.0", "type": "module", "main": "index.mjs",
  "dependencies": { "ortak": "^1.0.0" }
}
```

Kurulmuş ağacın biçimi şudur:

```
$ node agac.mjs
ceviri@3.1.0
  ortak@1.0.0
ortak@2.0.0
yinelenen: ortak -> 1.0.0, 2.0.0
```

Bu çıktıyı üreten betik, önceki derste anlatılan arama kuralını tersinden uygular: her
`node_modules` dizinini gezer ve bulduğu bildirimleri yazar.

```javascript
// dosya: agac.mjs
import { readdir, readFile } from 'node:fs/promises';
import { join } from 'node:path';

async function dolas(dizin, derinlik = 0, bulgular = new Map()) {
  const modulDizini = join(dizin, 'node_modules');
  let girdiler;
  try {
    girdiler = await readdir(modulDizini);
  } catch {
    return bulgular;
  }
  for (const ad of girdiler.sort()) {
    const paketDizini = join(modulDizini, ad);
    const bildirim = JSON.parse(await readFile(join(paketDizini, 'package.json'), 'utf8'));
    console.log(`${'  '.repeat(derinlik)}${bildirim.name}@${bildirim.version}`);
    bulgular.set(bildirim.name, (bulgular.get(bildirim.name) ?? new Set()).add(bildirim.version));
    await dolas(paketDizini, derinlik + 1, bulgular);
  }
  return bulgular;
}

const bulgular = await dolas('.');
for (const [ad, surumler] of bulgular) {
  if (surumler.size > 1) console.log(`yinelenen: ${ad} -> ${[...surumler].join(', ')}`);
}
```

Ağaçtaki yerleşim şudur: `ortak` paketinin ikinci sürümü kök dizinde, birinci sürümü
`ceviri` paketinin kendi `node_modules` dizinindedir. Arama kuralı gereği `ceviri` içinden
yapılan bir içe aktarma önce kendi dizinine bakar ve birinci sürümü bulur; kökten yapılan
bir içe aktarma ikinci sürümü bulur. İki istek de karşılanmıştır.

## İki Kopyanın Bedeli

Çakışma çözüldü, ama sonuç iki ayrı paket örneğidir. Modül sistemleri konusunda kurulan
tek değerlendirme güvencesi **paket başına değil, çözülmüş dosya yolu başına** geçerlidir;
iki farklı dosya, iki farklı modül demektir.

```javascript
// dosya: node_modules/ortak/index.mjs — ikinci sürüm
export const surum = '2.0.0';
export class Kayit {
  constructor(ad) { this.ad = ad; }
}
const kayitlar = [];
export function ekle(kayit) { kayitlar.push(kayit); return kayitlar.length; }
```

```javascript
// dosya: node_modules/ceviri/index.mjs — kendi dizinindeki birinci sürümü kullanır
import { ekle, Kayit, surum } from 'ortak';

export function kullan(ad) {
  const kayit = new Kayit(ad);
  return { gorulenSurum: surum, kayitSayisi: ekle(kayit), kayit };
}
```

```javascript
// dosya: ana.mjs — yukarıdaki iki paketi kullanır
import { ekle, Kayit, surum } from 'ortak';
import { kullan } from 'ceviri';

const bizim = new Kayit('kok');
console.log('kokun gordugu surum:', surum, '| kayit sayisi:', ekle(bizim));

const onunki = kullan('ceviri-ici');
console.log('cevirinin gordugu surum:', onunki.gorulenSurum, '| kayit sayisi:', onunki.kayitSayisi);
console.log('cevirinin urettigi kayit bizim sinifimizdan mi:', onunki.kayit instanceof Kayit);
```

```
$ node ana.mjs
kokun gordugu surum: 2.0.0 | kayit sayisi: 1
cevirinin gordugu surum: 1.0.0 | kayit sayisi: 1
cevirinin urettigi kayit bizim sinifimizdan mi: false
```

Üç satır, üç ayrı sorunu gösterir.

**Sürüm görünürlüğü kullanıcıya bağlıdır.** Aynı programda iki farklı `ortak` sürümü
çalışıyor. Hangisinin çalıştığı, sorunun nereden sorulduğuna göre değişir.

**Durum paylaşılmaz.** İki kayıt listesi ayrıdır; her ikisi de bir eleman içerir. Paketin
tuttuğu kayıt, önbellek veya sayaç türünden her şey bölünmüştür. Bir kütüphane "tüm
kayıtları listele" gibi bir işlev sunuyorsa, yalnızca kendi kopyasının kayıtlarını görür.

**Kimlik denetimi başarısızdır.** `ceviri` paketinin ürettiği nesne, kökteki sınıfın örneği
değildir. Sınıflar aynı kaynaktan gelse bile iki ayrı değerlendirmenin ürünüdür; nesne
kimliği bunu bilmez. Aynı sorun sembollerde, hata sınıflarında ve marka denetimlerinde
ortaya çıkar.

Bu üç sorun, "programım çalışıyor ama bir yerde tuhaf davranıyor" biçimindeki tanısı zor
arızaların sık nedenidir. Bağımlılık ağacında yinelenen sürüm aramak, bu tür bir arızada
ilk adımdır.

## Düzleştirme

İç içe kurulum her zaman gerekli değildir. İstekler kesişiyorsa tek bir kopya hepsini
karşılar ve bu kopya ağacın köküne konur. Bu yerleştirmeye **düzleştirme (flattening)**
denir.

Düzleştirmenin iki gerekçesi vardır. Birincisi yer ve süre: aynı paketin onlarca kopyası
yerine tek kopya kurulur. İkincisi az önce sayılan üç sorunun önlenmesidir.

Düzleştirmenin bir yan etkisi, önceki derste değinilen sinsi duruma zemin hazırlamasıdır.
Kök dizine çıkarılan bir paket, onu bildiriminde yazmayan modüllerden de görünür hâle
gelir. Kod yerelde çalışır; ağaç başka bir biçimde kurulduğunda — örneğin farklı bir
kurulum aracı iç içe yerleşimi yeğlediğinde — paket kaybolur. Bu yüzden kural açıktır:
kullanılan her paket, kullanan paketin bildiriminde yazılı olmalıdır.

## Tekillik Gerektiren Bağımlılıklar

Bazı paketler için iki kopya yalnızca israf değil, doğrudan hatadır. Kayıt tutan, genel
bir durum yöneten ya da nesne kimliğine dayanan kütüphaneler tek örnek olmak zorundadır.

Bunu sağlayan mekanizma, ilk derste tanıtılan **eş bağımlılık** listesidir. Bir eklenti,
genişlettiği kütüphaneyi çalışma bağımlılığı olarak yazarsa kendi dizinine kendi kopyasını
kurar ve ikilik oluşur. Eş bağımlılık olarak yazdığında ise kütüphaneyi kurmaz; yalnızca
"şu aralıktaki bir sürüm ortamda bulunmalı" der. Kurma sorumluluğu uygulamaya geçer ve
ağaçta tek kopya kalır.

Eş bağımlılık aralığı bilinçli seçilmelidir. Fazla dar bir aralık, eklentinin kullanılabildiği
sürüm kümesini gereksiz daraltır ve kesişmeyen istekler üretir; fazla geniş bir aralık,
eklentinin çalışmadığı sürümlerde kurulumun sorunsuz görünmesine yol açar.

## Çakışma Ne Zaman Çözülmez

İç içe kurulum her çakışmayı çözmez. Üç durumda tek kopya zorunludur ve karar verilmesi
gerekir:

**Eş bağımlılık kesişmiyorsa.** İki eklenti aynı kütüphanenin uyumsuz ana sürümlerini
istiyorsa ikisi bir arada kullanılamaz. Çözüm, eklentilerden birini yükseltmek veya
değiştirmektir.

**Değerler paketler arasında geçiyorsa.** Bir paketin ürettiği nesne başka bir pakete
argüman olarak veriliyorsa ve alan taraf kimlik denetimi yapıyorsa, iki kopya çalışmaz.

**Çıktı boyutu belirleyiciyse.** Tarayıcıya gönderilen bir çıktıda aynı kütüphanenin iki
sürümünü taşımak, indirilen baytı iki katına çıkarır.

Bu durumlarda kurulum araçları, geçişli bir bağımlılığın sürümünü dışarıdan sabitlemeye
izin veren bir bildirim alanı sunar. Alan işe yarar ama tehlikelidir: bir paketin
beklediğinden farklı bir sürümle çalışmaya zorlanması, sürüm sözleşmesinin kasıtlı olarak
çiğnenmesidir. Kullanıldığında bir yorumla gerekçelendirilmeli ve geçici sayılmalıdır;
kalıcı çözüm, uyumsuz paketin yükseltilmesidir.

## Özet

- Kesişmeyen sürüm istekleri, aynı paketin iki kopyasının ağacın farklı düzeylerine
  kurulmasıyla karşılanır; arama kuralı her tüketiciye kendi kopyasını bulduran şeydir.
- Tek değerlendirme güvencesi paket başına değil, çözülmüş dosya yolu başınadır; iki kopya
  ayrı durum tutar ve nesne kimliği denetimlerini başarısız kılar.
- Kesişen istekler tek kopyayla karşılanır ve kopya köke çıkarılır; bu düzleştirme,
  bildirimde yazılmayan paketlerin görünür olmasına da yol açar.
- Tekillik gerektiren kütüphaneler eş bağımlılık olarak bildirilir; kurma sorumluluğu
  uygulamaya geçer ve ağaçta tek kopya kalır.
- Geçişli bir bağımlılığın sürümünü dışarıdan sabitlemek sözleşmeyi çiğner; geçici çözüm
  sayılmalıdır.

## Sonraki Adım

Buraya kadar tüm paketler dışarıdan geldi. Bir ekibin kendi yazdığı paketler de aynı
ağaçta yer alır ve aralarındaki bağımlılıklar her değişiklikte yayımlanmayı bekleyemez.
Sonraki ders, birden çok paketin tek bir depoda tutulduğu düzeni, bu düzenin çözümlemeyi
nasıl değiştirdiğini ve paketler arası derleme sırasının nasıl belirlendiğini ele alır.
