---
title: 'Bağımlılık Yönetimi'
source: 'https://academia.sh/tr/kurslar/ruby-uretim/bagimlilik-yonetimi'
course: 'Ruby Ekosistemi ve Üretim'
language: tr
updated: '2026-10-21T05:03:44+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Karamsar kısıtın, aralık kısıtının ve tam sürüm kısıtının hangi sürümleri kabul ettiğini ölçer'
    - 'Kilitsiz çözümün deponun içeriğine, kilitli çözümün kilit dosyasına bağlı olduğunu gösterir'
    - 'Kilit dosyasındaki sürüm değişince kurulan sürümün değiştiğini ve bunun bağımlılık belirleyeni olduğunu ayırır'
    - 'Kuralın kısıtı karşılamayan bir kilit karşısında sınıflandırma yapmadan hata verdiğini ölçer'
---

# Bağımlılık Yönetimi

Kilit dosyası aynıyken kurulan sürüm neden değişmez? Ders sürüm kısıtlarının hangi sürümleri kabul ettiğini, kilitsiz çözümün depoya göre değiştiğini ve bağımlılığın belirleyen olduğunu ölçer.

Önceki ders paketin dosya adının ad ve sürümden geldiğini gösterdi: o sonuç koddu. Paket tanımında ayrıca bir bağımlılık yazılıydı, ama hangi sürümünün kurulacağı yazılı değildi. Bu dersin sorusu bu yüzden bir adım ileridedir: kilit dosyası aynı kaldığı sürece kurulan sürüm neden değişmiyor, ve kilit değişince ne oluyor?

Kursun sorusu aynıdır: **bu sonucu ne belirliyor?** Bu dersin gözlemi `kilitli surum` satırıdır. M12/K02'de "bu değişiklik nereye ulaşıyor" sorulmuştu; orada bir değişikliğin hangi katmanlara yayıldığı ölçüldü. Burada soru tersine döner: kurulan sürüm hangi girdiye bağlı? Çekirdeğin `belirleyen.rb` dosyası birebir yeniden verilir; blok kaynaktır ve `require_relative` ile çekilir.

```ruby
# belirleyen.rb — bir kosum modeli ve bir sonucu neyin belirledigini siniflandiran yardimci

# Kosum: sonucun bagli olabilecegi uc girdi. Bu uc girdinin disinda kalan her sey koddur.
Kosum = Data.define(:kilit, :tohum, :ortam)

TABAN = Kosum.new(
  kilit: { "bicimleyici" => "1.4.2", "ayristirici" => "2.0.1" },
  tohum: 1,
  ortam: { saat: Time.utc(2026, 10, 16, 9, 0), cekirdek: 4, degiskenler: {} }
)

# Her degisken tek bir girdiyi degistirir; otekiler tabandaki gibi kalir.
DEGISKENLER = {
  bagimlilik: TABAN.with(kilit: { "bicimleyici" => "1.5.0", "ayristirici" => "2.1.0" }),
  sira: TABAN.with(tohum: 2),
  ortam: TABAN.with(ortam: { saat: Time.utc(2027, 3, 1, 9, 0), cekirdek: 8,
                             degiskenler: { "ISCI_SAYISI" => "3" } })
}

# Kural: hesap once tabanda, sonra her degiskende bir kez kosar. Sonucu degistiren ilk
# degisken (bagimlilik, sira, ortam sirasiyla) belirleyendir; hicbiri degistirmiyorsa kod.
def belirleyen(hesap)
  taban = hesap.call(TABAN)
  degisti = DEGISKENLER.to_h { |ad, kosum| [ad, hesap.call(kosum) != taban] }
  tur = degisti.key(true)&.to_s || "kod"
  kanit = degisti.map { |ad, d| "#{ad[0].upcase}#{d ? '+' : '-'}" }.join(" ")
  [tur, kanit, taban]
end
```

## Sürüm Kısıtı

**Bağımlılık yöneticisi**, bir paketin hangi sürümlerle çalışacağını **sürüm kısıtı** üzerinden çözer. Kısıt bir sürüm değil, bir kabul aralığıdır. Üç biçim görülür. Tam sürüm kısıtı tek bir sürümü kabul eder. Aralık kısıtı iki sınır arasını kabul eder: `>= 1.4` ve `< 3` birlikte yazılır. Üçüncüsü **karamsar kısıttır** (pessimistic constraint): `~> 1.4` yazısı, "1.4'ten küçük olmayan, ama ana sürümü değişmeyen" anlamına gelir. Kısıt, yazılan son basamağın yükselmesine izin verir, ondan önceki basamağın yükselmesine izin vermez; `~> 1.4` için üst sınır `2`'dir.

Aşağıdaki dosya üç biçimi aynı sürüm listesine uygular. `Gem::Requirement` kısıtı kurar, `satisfied_by?` bir sürümün kabul edilip edilmediğini söyler.

```ruby
# kisitlar.rb — surum kisitlari hangi surumleri kabul eder
def kabul_edilen(kisitlar, surumler)
  gerek = Gem::Requirement.new(*kisitlar)
  surumler.select { |s| gerek.satisfied_by?(Gem::Version.new(s)) }
end

liste = %w[1.3.9 1.4.0 1.4.2 1.5.0 1.99 2.0.0]
p kabul_edilen(["~> 1.4"], liste)
p kabul_edilen(["~> 1.4.0"], liste)
p kabul_edilen([">= 1.4", "< 3"], %w[1.3.9 1.4.0 2.9 3.0])

bagimlilik = Gem::Dependency.new("bicimleyici", "~> 1.4")
p bagimlilik.match?("bicimleyici", "1.9.0")
p bagimlilik.match?("bicimleyici", "2.0.0")
```
```
["1.4.0", "1.4.2", "1.5.0", "1.99"]
["1.4.0", "1.4.2"]
["1.4.0", "2.9"]
true
false
```

`~> 1.4` ana sürümü 1'de tutar ve `1.99`'a kadar her şeyi kabul eder: alt sürüm yükselebilir. `~> 1.4.0` bir basamak daha uzundur ve kabul aralığı daralır: yalnız `1.4.0` ile `1.4.2`. **Son basamak kısıtın genişliğini belirler.** Aralık kısıtı iki sınırın kesişimidir ve `3.0`'ı dışarıda bırakır. `Gem::Dependency` ise adı ve kısıtı birlikte taşır; `1.9.0`'ı kabul eder, `2.0.0`'ı reddeder. Anlamlı sürümleme sözleşmesi doğruysa, ana sürüm değişmediği sürece uyumsuz bir değişiklik gelmez; ama bu sözleşmeyi paketin yazarı verir, kısıt onu denetlemez. **RK6.** Bu ölçüm kısıtın hangi sürümleri kabul ettiğini gösterir; kabul edilen bir sürümün gerçekten uyumlu olduğunu göstermez.

## Kilitsiz Çözüm

Kısıt birden çok sürümü kabul ettiğinde yöneticinin bir tanesini seçmesi gerekir. En basit kural en yüksek uygun sürümü almaktır. Aşağıdaki saf Ruby yardımcı bu çözümün küçük bir modelidir.

```ruby
# cozum.rb — kilit yokken depodan en yuksek uygun surum
def en_yuksek_uygun(depo, kisit)
  gerek = Gem::Requirement.new(kisit)
  depo.map { |s| Gem::Version.new(s) }.select { |s| gerek.satisfied_by?(s) }.max.to_s
end
```

Yardımcı kaynaktır. Aşağıdaki dosya onu önce üç sürümlük bir depoya, sonra bir sürüm eklenmiş depoya uygular.

```ruby
# depo.rb — ayni kisit, degisen depo
require_relative "cozum"

depo = %w[1.4.2 1.5.0 2.0.0]
p en_yuksek_uygun(depo, "~> 1.4")

depo << "1.6.0"
p en_yuksek_uygun(depo, "~> 1.4")
```
```
"1.5.0"
"1.6.0"
```

Kısıt aynı kaldı, depo değişti, sonuç `"1.5.0"`'dan `"1.6.0"`'a geçti. Kilitsiz çözüm **deponun o anki içeriğine** bağlıdır. Dün kurulan makine ile bugün kurulan makine aynı kısıtla farklı sürüm alabilir. Bu, "benim makinemde çalışıyordu" sorusunun kaynaklarından biridir.

## Kilit Dosyası

Bunun çaresi, çözümün sonucunu bir **kilit dosyası**na yazmaktır. Kilit dosyası bildirim dosyasındaki kısıtlardan çözülmüş tam sürümleri tutar. Sonraki her kurulum çözüm yapmaz, kilitteki sürümü okur. Kilit dosyası depoya işlenir, çünkü bütün makinelerin aynı sürümleri alması ancak böyle sağlanır.

Çekirdekteki `kurulan_surum` bir bağımlılık çözücü değil, kilit dosyasındaki sürümü okuyan bir modeldir. Aşağıdaki dosya onun tanımını birebir alır.

```ruby
# bagimlilik.rb — kilitteki surumu okuyan model
# Bagimlilik cozumu: kilitteki surum kisiti karsiliyorsa o surum kurulur.
def kurulan_surum(kosum, ad, kisit)
  surum = Gem::Version.new(kosum.kilit.fetch(ad))
  raise ArgumentError, "kilit kisiti karsilamiyor" unless Gem::Requirement.new(kisit).satisfied_by?(surum)

  surum.to_s
end
```

Model üç yerde ölçülür: tam sürüm kısıtında, geniş kısıtta ve kilidi değişmiş koşumda. Son satır kuralın bu gözlemi nasıl sınıflandırdığını gösterir.

```ruby
# kilit.rb — kilit kurulan surumu sabitler
require_relative "belirleyen"
require_relative "bagimlilik"

p kurulan_surum(TABAN, "bicimleyici", "~> 1.4.0")
p kurulan_surum(TABAN, "bicimleyici", "~> 1.4")
p kurulan_surum(DEGISKENLER[:bagimlilik], "bicimleyici", "~> 1.4")
p belirleyen(->(k) { kurulan_surum(k, "bicimleyici", "~> 1.4") })
```
```
"1.4.2"
"1.4.2"
"1.5.0"
["bagimlilik", "B+ S- O-", "1.4.2"]
```

Kısıt `~> 1.4` geniş olduğu hâlde, kilit `1.4.2` dediği için kurulan `"1.4.2"` kaldı; kısıt `1.5.0`'ı kabul eder, ama kilit onu seçmedi. Kilit `1.5.0` olunca kurulan sürüm de `"1.5.0"` oldu. Dördüncü satır sınıflandırmayı verir: `bagimlilik`, kanıt `B+ S- O-`. Bağımlılık değişkeninde sonuç değişti, sıra ve ortam değişkenlerinde değişmedi. **Kısıt sürümü sabitlemez, yalnız sınırlar; sabitleyen kilittir.** Bu dersin belirleyeni **bağımlılık**tır ve ders boyunca bu kelime kilit dosyasının içeriği demektir. **RK7.** Ölçüm, kilit dosyasındaki sürüm listesinin çözümden geldiğini varsayar; bu listenin gerçekten bir çözücü tarafından üretildiği modele dahil değildir.

## Kilidi Güncellemek

Kilit dosyası kendiliğinden değişmez. Depoya yeni sürüm gelmesi, kilidi yenilemek için yeterli değildir; kilit yalnız yönetici yeniden çözüm yaptığında yazılır. Bu bir açık işlemdir ve çekirdekte `TABAN.with` ile temsil edilir: kilit dosyasının içeriği yeni bir değerle değiştirilir, eski değer olduğu gibi kalır.

```ruby
# guncelle.rb — kilit yalniz acikca guncellenince degisir
require_relative "belirleyen"
require_relative "bagimlilik"
require_relative "cozum"

depo = %w[1.4.2 1.5.0 1.6.0]
yeni = en_yuksek_uygun(depo, "~> 1.4")
guncel = TABAN.with(kilit: TABAN.kilit.merge("bicimleyici" => yeni))

p kurulan_surum(TABAN, "bicimleyici", "~> 1.4")
p kurulan_surum(guncel, "bicimleyici", "~> 1.4")
p TABAN.kilit
p guncel.kilit
```
```
"1.4.2"
"1.6.0"
{"bicimleyici" => "1.4.2", "ayristirici" => "2.0.1"}
{"bicimleyici" => "1.6.0", "ayristirici" => "2.0.1"}
```

Depoda `1.6.0` bulunmasına rağmen `TABAN`'ın kilidi `1.4.2` kaldı; `kurulan_surum` bu koşumda `"1.4.2"` verdi. Kilidi yenilenmiş koşum `"1.6.0"` verdi, ve yalnız `bicimleyici` değişti: `ayristirici` kilitte `2.0.1` olarak kaldı. Bir güncelleme yalnız istenen bağımlılığı oynatabilir, ötekilere dokunmaz. Bu, bağımlılığın neden bir belirleyen olduğunu da açıklar: sonucu değiştiren şey kod değildir, kilit dosyasına yapılan bir yazıdır. Kod ve kısıt aynı kalır; kilit değişince sonuç değişir. **RK8.** Güncelleme burada tek bir bağımlılık için ve saf bir depoyla modellendi; birbirine bağlı bağımlılıkların birlikte güncellenmesi ölçülmedi.

## İki Paketin Kısıtı

Bir uygulama birden çok paket kurar ve her paket aynı bağımlılık için kendi kısıtını yazar. Yönetici bütün kısıtları birlikte karşılayan bir sürüm arar: kabul aralıklarının kesişimi. Kesişim boşsa çözüm yoktur. Aşağıdaki dosya `kabul_edilen`'i iki paketin kısıtıyla çağırır.

```ruby
# kesisim.rb — iki paketin kisitinin kesisimi
def kabul_edilen(kisitlar, surumler)
  gerek = Gem::Requirement.new(*kisitlar)
  surumler.select { |s| gerek.satisfied_by?(Gem::Version.new(s)) }
end

depo = %w[1.4.0 1.4.2 1.5.0 1.99 2.0.0]
p kabul_edilen(["~> 1.4", ">= 1.5"], depo)
p kabul_edilen(["~> 1.4.0", ">= 1.5"], depo)
p kabul_edilen(["~> 1.4", "~> 2.0"], depo)

onsurum = Gem::Dependency.new("bicimleyici", "~> 1.4")
p Gem::Requirement.new("~> 1.4").satisfied_by?(Gem::Version.new("1.5.0.pre1"))
p onsurum.match?("bicimleyici", "1.5.0.pre1")
```
```
["1.5.0", "1.99"]
[]
[]
true
false
```

Birinci paket `~> 1.4`, ikincisi `>= 1.5` yazınca depodaki `1.5.0` ile `1.99` kalır. İkincisi `~> 1.4.0` yazarsa kesişim boşalır, çünkü `1.4.x` aralığı ile `1.5` ve sonrası hiçbir sürümde buluşmaz. Üçüncü satırda iki karamsar kısıt ana sürümde ayrılır ve yine hiçbir sürüm kalmaz. Boş küme bir çözüm değildir: yönetici burada kurulum yapmaz, çakışmayı bildirir. Çözüm, bütün kısıtların kesişiminde kalan bir sürümü aramaktır. Bu, kilit dosyasının neyi saklamadığını da gösterir: kilit çözümün sonucunu saklar, ama çözümün var olması kısıtların kesişimine bağlıdır.

Son iki satır ön sürümü ayırır. Kısıt nesnesi `1.5.0.pre1`'i yalnız sayılara bakarak kabul eder. Bağımlılık nesnesi ise ön sürümü varsayılan olarak aday saymaz. Aynı kısıt iki nesnede iki sonuç verdi, bu yüzden bir ön sürümün kurulup kurulmayacağı kısıta değil, soruyu soran nesneye bağlıdır. **RK9.** Bu dersin modeli yalnız `Gem` modülünün nesnelerini ölçer; gerçek bir yöneticinin ön sürümü hangi kuralla aday saydığı sınanmadı.

## İki Makine, Bir Kilit Dosyası

Kilit dosyasının depoya işlenme gerekçesi iki makineyle ölçülür. Birinci makine kurulumu bugün yapar, ikincisi deponun bir sürüm daha kazandığı ertesi gün. Kilitsiz kurulum çözümü her makinede yeniden yapar. Kilitli kurulum çözüm yapmaz, kilidi okur.

```ruby
# makineler.rb — kilitsiz ve kilitli kurulum iki makinede
require_relative "belirleyen"
require_relative "bagimlilik"
require_relative "cozum"

bugun = %w[1.4.2 1.5.0]
yarin = bugun + ["1.6.0"]

p [en_yuksek_uygun(bugun, "~> 1.4"), en_yuksek_uygun(yarin, "~> 1.4")]
p [kurulan_surum(TABAN, "bicimleyici", "~> 1.4"), kurulan_surum(TABAN, "bicimleyici", "~> 1.4")]
```
```
["1.5.0", "1.6.0"]
["1.4.2", "1.4.2"]
```

Kilitsiz çiftte iki makine iki farklı sürüm kurdu; kilitli çiftte ikisi de aynısını kurdu. İkinci çiftte depo hiç kullanılmadı: `kurulan_surum` depoya bakmaz, bu yüzden deponun ertesi günkü içeriği sonucu etkilemez. Dün ile bugün arasındaki fark deponun değişmesidir; kilit dosyası bu farkı kurulumdan ayırır. Tutarlılığı kilit dosyası sağlar, ve bu ancak herkes aynı kilit dosyasını okuyorsa doğrudur. Kilit dosyası yalnız bir makinede duruyorsa öteki makinenin kurduğu sürüm bu ölçüm hakkında hiçbir şey söylemez. **RK10.** Model iki makineyi aynı kilit nesnesiyle temsil eder; gerçek bir ekipte kilit dosyasının eşitlenmesi ayrı bir iştir ve burada ölçülmedi.

## Bağımlılık Türü ve Kısıt

Çalışma ve geliştirme bağımlılığı ayrımı bir önceki derste paket tanımı üzerinden kuruldu. Burada eklenen, türün kısıtla ilişkisidir: tür kurulan sürümü değil, nerede kurulacağını belirler, ve iki tür de aynı biçimde bir kısıt taşır.

```ruby
# tur.rb — bagimlilik turu ve kisit
bicimleyici = Gem::Dependency.new("bicimleyici", "~> 1.4", :runtime)
sinayici = Gem::Dependency.new("sinayici", ">= 2", :development)

p [bicimleyici.type, bicimleyici.runtime?]
p [sinayici.type, sinayici.runtime?]
p bicimleyici.requirement.to_s
p sinayici.requirement.to_s
```
```
[:runtime, true]
[:development, false]
"~> 1.4"
">= 2"
```

Geliştirme bağımlılığının `type` değeri `:development` döndü ve `runtime?` yanlış oldu; çalışma bağımlılığında `:runtime` ve doğru. İki kısıt metni de verildiği gibi geri okundu: `"~> 1.4"` ve `">= 2"`. Her bağımlılığın türü ayrı okunur ve `runtime?` yalnız çalışma türünde doğrudur. Kısıtın metni türden bağımsızdır. Bu ayrım, kilit dosyasının neden her ikisini de içerebildiğini açıklar: kilit bütün çözümü tutar, tür ise o çözümün hangi bölümünün kimin makinesine ineceğini söyler. **RK11.** `type` ve `runtime?` yalnız `Gem::Dependency` nesnesinin ne dediğini gösterir; bir yöneticinin geliştirme bağımlılığını kurulum sırasında atlaması bu ölçümün dışındadır.

## Sınırlayıcı Ölçüm

Kural bir hesabı üç değişkende koşturur. Hesap herhangi birinde hata verirse sınıflandırma yapamaz. Bu dersin kendi hatası bunu gösterir: kısıt `~> 1.4.0` iken tabandaki kilit (`1.4.2`) kısıtı karşılar, ama bağımlılık değişkeninin kilidi (`1.5.0`) karşılamaz.

```ruby
# sinir.rb — kilit kisiti karsilamayinca kural siniflandirmaz
require_relative "belirleyen"
require_relative "bagimlilik"

begin
  belirleyen(->(k) { kurulan_surum(k, "bicimleyici", "~> 1.4.0") })
rescue => e
  p e.class
  puts e.message
end
```
```
ArgumentError
kilit kisiti karsilamiyor
```

Kural bir sınıf vermedi, `ArgumentError` yükseldi. Bu hata kuralın değil, hesabın hatasıdır: kilit dosyası kısıtla çelişiyor. Kurulumda görülen sorun budur; kural onu "bağımlılık" diye adlandırmaz çünkü hesap sonuç üretemedi. **RK12.** Bu ölçüm yalnız kilit ile kısıt çeliştiğinde olan şeyi gösterir; gerçek bir yönetici çelişkiyi kilidi yeniden çözerek gidermeyi seçebilir, bu modelin dışındadır.

## Özet

- Sürüm kısıtı bir sürüm değil bir kabul aralığıdır: `~> 1.4` ana sürümü sabit tutar, `~> 1.4.0` yalnız yama sürümünü serbest bırakır, aralık kısıtı iki sınırın kesişimidir.
- Kilitsiz çözüm deponun içeriğine bağlıdır: depoya `1.6.0` gelince aynı kısıt başka sürüm verir.
- Bu dersin belirleyeni **bağımlılık**tır: `kilitli surum` satırında `B+ S- O-` görünür ve kilit `1.5.0` olunca kurulan sürüm de değişir.
- Kısıt sınırlar, kilit dosyası sabitler; kilit dosyası bu yüzden depoya işlenir.
- Kilit kısıtı karşılamazsa kural sınıflandırma yapmaz, hata verir.

## Sonraki Adım

Kurulan sürümü artık kilit dosyası belirliyor. Ama aynı kod ve aynı kilitle bile bir sonuç değişebilir. Bir sonraki ders, aynı testlerin neden bazen düştüğünü ve bunu sıranın nasıl belirlediğini ölçer.
