---
title: 'Modüller ve Bağımlılıklar'
source: 'https://academia.sh/tr/kurslar/go-temelleri/moduller-ve-bagimliliklar'
course: 'Go Temelleri'
language: tr
updated: '2026-08-23T14:25:01+00:00'
license: 'CC BY-SA 4.0'
---

# Modüller ve Bağımlılıklar

Bir bağımlılığın sürümü kaynakta çoğu zaman hiç yazılmaz; altı istekten üçü ana modülün kendi kaynağında yazılı, üçü bir bağımlılığın kendi bağımlılığından türetilmiştir. Modül dosyasının yazılmayan sürümü doldurma kuralı — en düşük sürüm seçimi — kaynak içinde modellenerek koşturulur; hiçbir ağ çağrısı yapılmaz. Sınırlayıcı ölçüm, seçilen sürümün bir modülün kataloğundaki en yeni sürüm olmadığını, yalnız yazılan isteklerin en büyüğü olduğunu gösterir.

Önceki ders bir paketin sınırının pakette durduğunu, dosyada durmadığını gösterdi. Bu ders
aynı soruyu bir düzey yukarı taşıyor: bir paket başka bir pakete bağımlı olduğunda, o
bağımlılığın **hangi sürümü** kastettiği kaynakta nerede yazılı? Çoğu zaman hiçbir yerde.
Bir modül dosyası doğrudan bağımlı olduğu birkaç modülün sürümünü yazar; o modüllerin
kendi bağımlılıklarının sürümü ise hiç yazılmaz, modüllerin kendi dosyalarından türer.
Bu dersin sorusu kursun genel sorusunun aynısı: sürüm yazılmadığında modül dosyası onun
yerine neyi koyuyor?

Bir bağımlılık grafiğinin sürüm çözmesi gereken bir sorunu var: aynı modül, grafikte
birden fazla yoldan isteniyorsa, o modülün **hangi** sürümüyle derleneceği kaynağın hiçbir
tek satırında yazmıyor. İki ayrı bağımlılık aynı üçüncü modülü farklı en düşük sürümlerle
istediğinde, ana modül ikisi arasında seçim yapmak zorunda kalıyor — ama bu seçim ana
modülün kaynağında görünmüyor, çünkü ana modül o üçüncü modülü hiç doğrudan anmıyor.
Bu durumun adı bağımlılık grafiklerinde sık geçer: iki ayrı yol aynı üçüncü noktaya
varıyor ve o noktada iki farklı istek çarpışıyor. Çarpışma bir hata değil — iki istek de
"en az şu sürüm" diyor, ikisi de yalnız bir alt sınır çiziyor, üst sınır çizmiyor. Seçimin
görevi bu iki alt sınırı **tek bir sürümde** birleştirmek, ve birleştirilen sürümün her iki
isteği de karşıladığından emin olmak. Aşağıdaki ölçüm bu durumu en küçük hâliyle kuruyor ve
seçim algoritmasını gerçekten çalıştırarak sonucu okuyor.

## Altı İstek, İki Kaynak

Aşağıdaki ölçüm gerçek bir modül kaydına hiç bağlanmıyor; bağımlılık grafiği kaynak içinde
elle kuruluyor ve **modellendiği açıkça burada yazılıyor**. Dört modül var: `kutuphaneA`,
`kutuphaneB`, `kutuphaneC`, `kutuphaneD`. Ana modül `kutuphaneA`, `kutuphaneB` ve
`kutuphaneD`'yi doğrudan istiyor — üçünün sürümü de kaynakta yazılı. `kutuphaneA`,
`kutuphaneC`'nin bir sürümünü istiyor; `kutuphaneB` da `kutuphaneC`'nin başka bir sürümünü.
`kutuphaneC` da kendi payına `kutuphaneD`'nin bir sürümünü istiyor. Bu üç istek ana
modülün kaynağında hiç yazılı değil — ana modülün derlemesi sırasında, bağımlı olunan
modüllerin kendi dosyalarından **türetiliyor**.

```go
// main.go — en dusuk surum secimi, bagimlilik grafigi kaynak icinde modelleniyor (ag cagrisi yok)
package main

import "fmt"

// Surum uc parcali bir surum numarasini tasir; bu ders gercek bir modul kaydina
// baglanmiyor, grafigin tamami asagida elle kuruluyor.
type Surum struct {
	Ana, Ikincil, Yama int
}

func (s Surum) metin() string {
	return fmt.Sprintf("%d.%d.%d", s.Ana, s.Ikincil, s.Yama)
}

// buyukMu s'nin digerinden daha yeni bir surum oldugunu soyler.
func (s Surum) buyukMu(diger Surum) bool {
	if s.Ana != diger.Ana {
		return s.Ana > diger.Ana
	}
	if s.Ikincil != diger.Ikincil {
		return s.Ikincil > diger.Ikincil
	}
	return s.Yama > diger.Yama
}

// Istek, bir modulun bagimliliginin istedigi en dusuk surumu tasir. Yazili true ise
// istek dogrudan ana modulun kendi kaynaginda duruyor; degilse baska bir bagimliligin
// kendi modul dosyasindan turedi.
type Istek struct {
	Modul   string
	Kimden  string
	Istenen Surum
	Yazili  bool
}

func main() {
	istekler := []Istek{
		{Modul: "kutuphaneA", Kimden: "ana modul", Istenen: Surum{1, 2, 0}, Yazili: true},
		{Modul: "kutuphaneB", Kimden: "ana modul", Istenen: Surum{1, 1, 0}, Yazili: true},
		{Modul: "kutuphaneD", Kimden: "ana modul", Istenen: Surum{2, 1, 0}, Yazili: true},
		{Modul: "kutuphaneC", Kimden: "kutuphaneA", Istenen: Surum{1, 4, 0}, Yazili: false},
		{Modul: "kutuphaneC", Kimden: "kutuphaneB", Istenen: Surum{1, 6, 0}, Yazili: false},
		{Modul: "kutuphaneD", Kimden: "kutuphaneC", Istenen: Surum{2, 0, 0}, Yazili: false},
	}

	fmt.Println("-- alti istek, kaynagi yazili mi turetilmis mi --")
	yaziliSayisi, turetilenSayisi := 0, 0
	for _, ist := range istekler {
		kaynak := "turetildi"
		if ist.Yazili {
			kaynak = "yazili"
			yaziliSayisi++
		} else {
			turetilenSayisi++
		}
		fmt.Printf("%-12s <- %-12s %-9s istenen=%s\n", ist.Modul, ist.Kimden, kaynak, ist.Istenen.metin())
	}
	fmt.Printf("toplam: %d yazili, %d turetildi\n", yaziliSayisi, turetilenSayisi)

	fmt.Println()
	fmt.Println("-- secilen surum: her modulun tum isteklerinin en buyugu --")
	siraliModuller := []string{"kutuphaneA", "kutuphaneB", "kutuphaneC", "kutuphaneD"}
	secilen := map[string]Surum{}
	for _, ist := range istekler {
		mevcut, varMi := secilen[ist.Modul]
		if !varMi || ist.Istenen.buyukMu(mevcut) {
			secilen[ist.Modul] = ist.Istenen
		}
	}
	for _, ad := range siraliModuller {
		fmt.Printf("%-12s secilen=%s\n", ad, secilen[ad].metin())
	}

	fmt.Println()
	fmt.Println("-- sinirlayici olcum: secim en yeniyi degil, yazilan en dusugu aliyor --")
	kataloktakiEnYeniC := Surum{1, 9, 0}
	fmt.Printf("kutuphaneC icin katalogtaki en yeni surum: %s\n", kataloktakiEnYeniC.metin())
	fmt.Printf("secilen surum: %s\n", secilen["kutuphaneC"].metin())
	fmt.Printf("secilen, katalogtaki en yeniyle ayni mi: %v\n", secilen["kutuphaneC"] == kataloktakiEnYeniC)
}
```

```
-- alti istek, kaynagi yazili mi turetilmis mi --
kutuphaneA   <- ana modul    yazili    istenen=1.2.0
kutuphaneB   <- ana modul    yazili    istenen=1.1.0
kutuphaneD   <- ana modul    yazili    istenen=2.1.0
kutuphaneC   <- kutuphaneA   turetildi istenen=1.4.0
kutuphaneC   <- kutuphaneB   turetildi istenen=1.6.0
kutuphaneD   <- kutuphaneC   turetildi istenen=2.0.0
toplam: 3 yazili, 3 turetildi

-- secilen surum: her modulun tum isteklerinin en buyugu --
kutuphaneA   secilen=1.2.0
kutuphaneB   secilen=1.1.0
kutuphaneC   secilen=1.6.0
kutuphaneD   secilen=2.1.0

-- sinirlayici olcum: secim en yeniyi degil, yazilan en dusugu aliyor --
kutuphaneC icin katalogtaki en yeni surum: 1.9.0
secilen surum: 1.6.0
secilen, katalogtaki en yeniyle ayni mi: false
```

**PK8.** Altı istekten üçü kaynakta yazılı, üçü türetilmiştir; hangi üçünün yazılı olduğu,
o modülün ana modülün **doğrudan** bağımlılığı olup olmamasına bağlıdır. **PK9.**
`kutuphaneC`'nin iki ayrı isteği var — biri `kutuphaneA`'dan, biri `kutuphaneB`'den — ve
ikisi de kaynakta hiç görünmeden, yalnız bu iki modülün kendi dosyalarından türüyor.

## En Düşük Sürüm Seçimi

**PK10.** Bir modülün seçilen sürümü, o modül için yazılan bütün isteklerin en büyüğüdür;
`kutuphaneC` için hem `1.4.0` hem `1.6.0` istendiğinde seçilen `1.6.0` oluyor, `kutuphaneD`
için hem `2.0.0` hem `2.1.0` istendiğinde seçilen `2.1.0` oluyor. Kuralın adı **en düşük
sürüm seçimi**, ama seçilen sayı isteklerin en küçüğü değil en büyüğü — çelişki gibi
görünen bu adlandırma, kuralın neyi garanti ettiğine bakıldığında çözülüyor. Kural şunu
söylüyor: seçilecek sürüm, **hiçbir isteği ihlal etmeyen en düşük sürüm** olsun. `1.4.0`
isteyen bir bağımlılık `1.6.0` ile de çalışır (sürümler geriye uyumlu kabul edilir), ama
`1.6.0` isteyen bir bağımlılık `1.4.0` ile çalışacağının garantisini vermez. Bu yüzden
bütün isteklerin **hepsini** karşılayan en düşük sürüm, isteklerin en büyüğüdür — "en
düşük" sözcüğü tek bir isteğe değil, bütün istekleri aynı anda karşılayan noktaya işaret
ediyor.

Bu, geriye uyumluluğun bir varsayım olarak alındığı bir hesap; kural kendisi bunu
denetlemiyor, yalnız kabul ediyor. Yukarıdaki `buyukMu` karşılaştırması üç sayıyı sırayla
kıyaslıyor ve hiçbir yerde iki sürümün gerçekten uyumlu olup olmadığını sınamıyor —
uyumluluk, sürüm numarasının kendisine bağlanmış bir söz olarak kabul ediliyor, ayrıca
doğrulanmıyor. Seçim algoritması bu sözün tutulduğu bir dünyada doğru sonuç veriyor;
söz tutulmadığında (yeni sürüm eskisiyle aynı arayüzü sunmadığında) algoritma hâlâ aynı
sayıyı seçiyor, ama seçilen sürümün gerçekten çalışıp çalışmadığı ayrı bir sorudur — bu
sorunun yanıtı modül dosyasında değil, kaynağın kendisinin derlenip derlenmediğinde
bulunuyor.

Bu hesap `kutuphaneD` için iki katmanlı bir zincir üzerinden de çalışıyor: `kutuphaneD`
doğrudan ana modülden bir istek alıyor (`2.1.0`, yazılı) ve dolaylı olarak
`kutuphaneC` üzerinden bir istek daha alıyor (`2.0.0`, türetilmiş). Seçim bu ikisi arasında
hiçbir ayrım yapmıyor — istek nereden geldiğine değil, hangi sayıyı taşıdığına göre
karşılaştırılıyor, ve büyük olan (`2.1.0`) kazanıyor. Bir isteğin yazılı ya da türetilmiş
olması, seçim algoritmasının **girdisi** değil; yalnız bu dersin ölçtüğü, isteğin kaynağını
sınıflandıran ayrı bir gözlem.

Bu ayrımın önemi isteklerin sayısı arttıkça büyüyor. Yukarıdaki ölçümde dört modül ve altı
istek var; gerçek bir bağımlılık grafiğinde bu sayı yüzlerle ölçülebilir ve aynı modülün
onlarca farklı yoldan istenmesi sıradan bir durumdur. Seçim kuralı bu ölçekte de
değişmiyor: her modül için, o modüle giden bütün yolların istediği sürümlerin en büyüğü
alınıyor. Algoritmanın kendisi grafiğin derinliğine duyarsız — `kutuphaneD`'ye giden istek
`kutuphaneC` üzerinden iki basamak uzaklıktan gelse de, ana modülden doğrudan gelse de,
karşılaştırma aynı iki sayıyı yan yana koyup büyüğünü seçmekten ibaret. Karmaşıklık
grafiğin büyüklüğünde duruyor, kuralın kendisinde değil.

## Sınırlayıcı Ölçüm: En Yeni Değil, Yazılan En Düşük

Yukarıdaki koşum bir katalog değeri daha taşıyor: `kutuphaneC` için "mevcut en yeni sürüm"
`1.9.0` olarak veriliyor, ama seçilen sürüm `1.6.0`. **PK11.** Seçim bir modülün elinde
bulunan en yeni sürümü hiç sormuyor; yalnız gerçekten yazılan (ya da türetilen) isteklere
bakıyor ve onların en büyüğünü alıyor. Bu, en düşük sürüm seçiminin en çok yanlış anlaşılan
tarafı: kural bir bağımlılığı **güncel tutmuyor**, yalnız kaynakta bir yerde (doğrudan ya
da dolaylı) istenmiş olanı karşılıyor. `kutuphaneC`'nin `1.9.0` sürümü var olabilir, ama
hiçbir istek onu istemediği sürece seçilen sürüme hiç girmiyor.

Bunun bilinçli bir tercih olduğu, neyi güvence altına aldığına bakılınca görülüyor: bir
bağımlılık grafiğinin sonucu, o grafiği kimin, ne zaman derlediğinden bağımsız olsun
isteniyor. Eğer seçim "her zaman en yeniyi al" olsaydı, aynı kaynak metin farklı
zamanlarda derlendiğinde farklı bir bağımlılık kümesiyle derlenebilirdi — üstelik
kaynağın kendisi hiç değişmeden. En düşük sürüm seçimi bunun yerine yalnız kaynakta
(dolaylı olarak da olsa) yazılana bakıyor; aynı istek kümesi, ne zaman ve nerede
çalıştırılırsa çalıştırılsın, aynı sürüm kümesini seçiyor. Bedeli açık: bir bağımlılığın
yeni bir sürümünden yararlanmak için o sürümü **isteyen bir satırın** bir yerde
yazılması gerekiyor; hiçbir istek kendiliğinden yükselmiyor.

Bunun bir sonucu daha var: iki ayrı geliştirici, aynı bağımlılık grafiğini aynı anda,
birbirinden habersiz çalıştırsa bile aynı sürüm kümesine varır — çünkü seçim hiçbir dış
duruma (kataloğun o anki içeriğine, hangi sürümlerin "en yeni" sayıldığına) bakmıyor,
yalnız kaynakta yazılana bakıyor. Bir bağımlılığı yükseltmek istendiğinde bu yüzden ayrı
ve bilinçli bir adım gerekiyor: ya doğrudan istenen sürüm kaynakta değiştiriliyor, ya da
zincirdeki bir ara bağımlılığın kendi isteği yükseltiliyor. Her iki durumda da yükselme,
bir yerde yeni bir sayının **yazılmasıyla** oluyor; yazılmadığı sürece seçim hep aynı
sonuca dönüyor, çünkü hesabın kendisi belirlenimci.

## Yazılı, Türetilmiş, Seçilmiş

Bir modül dosyasının gerçek kaynağında bu üç kavram üç ayrı satıra dağılır. Ana modülün
doğrudan istediği sürümler modül dosyasının kendi bildirim bloğunda, açıkça yazılı durur.
Yalnızca türetilmiş bir sürümü taşıyan modüller de aynı bloğa düşer, ama bu satırlar
"dolaylı" olarak işaretlenir — kaynağın hiçbir yerinde `import` edilmeyen, yalnız seçim
hesabının bir girdisi olan modülleri gösterirler. Seçilen sürüm ise bu iki kümenin
birleşiminden, yukarıdaki hesapla çıkar; modül dosyası bu üçünü ayrı ayrı taşıdığı için bir
modülün neden o sürümde durduğu — kendi isteği mi yoksa başka bir bağımlılığın isteği mi —
kaynaktan geriye doğru izlenebilir kalır.

Gerçek bir modül dosyasında bu bildirim bloğu satır satır okunabilir: her satır bir modül
adı ve o modül için seçilmiş sürümü taşır, ve doğrudan istenmeyip yalnız türetilmiş olan
bir modülün satırının sonuna bunu belirten kısa bir not eklenir. Bu notun kendisi bir sürüm
bilgisi değil, bir **köken** bilgisidir — o modülün ana modülün kaynağında hiç
`import` edilmediğini, yalnız seçim hesabına başka bir modül üzerinden girdiğini söyler.
Yukarıdaki ölçümdeki `Yazili` alanı tam olarak bu notun karşılığı: `kutuphaneA`,
`kutuphaneB`, `kutuphaneD` için `Yazili` alanı `true` (bunlar ana modülün kendi kaynağında
`import` ediliyor), `kutuphaneC`'nin iki isteği için `false` (hiçbiri ana modülün
kaynağında hiç anılmıyor). Bildirim bloğunun kendisi seçim hesabından **sonra** yazılır —
önce bütün istekler toplanır, en düşük sürüm seçimi çalıştırılır, sonra yalnız sonuç
(seçilen sürüm) bloğa düşer. Ara adımlar — hangi isteğin hangi modülden geldiği — modül
dosyasının kendisinde hiç görünmez; bu bilgi yalnız seçim hesabı çalışırken bir an için
vardır, tıpkı yukarıdaki ölçümün `istekler` diliminde durduğu gibi.

Bu üç ayrımın (yazılı, türetilmiş, seçilmiş) pratik bir sonucu var: bir modül dosyasını
okuyan biri, bir bağımlılığın neden orada durduğunu iki farklı soruyla ayırt edebiliyor.
"Bu modülü ben mi istedim?" sorusunun yanıtı doğrudan/dolaylı ayrımında duruyor. "Hangi
sürümü aldık?" sorusunun yanıtı ise seçilen sürüm satırında duruyor — ama bu ikinci soru
kaynağın **hiçbir** satırından tek başına çıkmıyor, çünkü seçilen sürüm birden fazla
isteğin karşılaştırılmasının sonucu. Modül dosyası bu yüzden bir bildirim listesinden çok
bir **hesabın son hâli**: her satır, kaynakta görünmeyen bir karşılaştırmanın kazananını
taşıyor.

Bağımlılık sürümünün nereden geldiğini bildirim satırından okumak (**M06/K04 Modüller,
Araçlar ve Ekosistem**) ve bir paketin hangi sürümle yayımlanacağını belirlemek
(**M07/K05 Python Projeleri: Paketleme ve Test**) başka dillerin kendi düzeneğinde daha
önce kuruldu; bu ders o modeli tekrarlamıyor, yalnız Go'nun kendi seçim kuralını —
isteklerin en büyüğünü almayı — ölçüyor.

## Özet

- Bir bağımlılık grafiğindeki istekler iki kaynaktan gelir: ana modülün kendi kaynağında
  yazılı olanlar ve bir bağımlılığın kendi modül dosyasından türeyenler.
- En düşük sürüm seçimi, bir modül için yazılan bütün isteklerin **en büyüğünü** seçer;
  bu, hiçbir isteği ihlal etmeyen en düşük ortak nokta olduğu için "en düşük" adını taşır.
- İsteğin yazılı ya da türetilmiş olması seçim hesabını etkilemez; yalnız hangi sayının
  taşındığı karşılaştırılır.
- Sınırlayıcı ölçüm: seçilen sürüm bir modülün kataloğundaki en yeni sürüm değildir,
  yalnız gerçekten istenmiş olanların en büyüğüdür — hiçbir istek kendiliğinden yükselmez.
- Bu ölçüm gerçek bir ağ çağrısı yapmadan, bağımlılık grafiğini kaynak içinde modelleyerek
  koşturuldu.

## Sonraki Adım

Bu derste bir modülün sürümünü kimin seçtiği ölçüldü: seçimi ne bir kişi ne ayrı bir araç
belirliyor, isteklerin kendisi belirliyor — kaynakta yazılı olsun ya da bir bağımlılıktan
türesin, hesap ikisine de aynı kuralı uyguluyor. Bir modülün sürümü bu yüzden kaynağın
dışında, seçim algoritmasının bir çıktısı olarak duruyor. Ama bir modülün içindeki
kaynağın **biçimi** ve uyduğu **kalıp** için aynı sorunun yanıtı farklı bir yerde: onları
uygulayan bir kişi ya da bir kural kitabı değil, Go'nun kendi komut ailesi. Sıradaki ders
bu komutları program içinden çağırıp, yazılmayan bir biçim kuralının kaynağı nasıl
değiştirdiğini ve derleyicinin reddetmediği bir kalıbı hangi aracın yakaladığını ölçüyor.
