---
title: 'Sürümleme ve Yayın'
source: 'https://academia.sh/tr/kurslar/tasarim-sistemleri/surumleme-ve-yayin'
course: 'Tasarım Sistemleri'
language: tr
updated: '2026-08-17T18:09:34+00:00'
license: 'CC BY-SA 4.0'
---

# Sürümleme ve Yayın

Bileşen arayüzünde neyin kırıcı sayıldığı, iki anlık görüntünün karşılaştırılmasından sürüm numarasının türetilmesi, göç kılavuzunun üretilmesi ve göç maliyetinin çağrı yeriyle ölçülmesi.

Belgedeki özellik tablosunun arayüzle karşılaştırılması bir soruyu açıkta bırakmıştı:
`dugme` bileşenine `genislik` özelliğinin eklenmesi ile `rozet` bileşeninden `ikonlu`
özelliğinin kaldırılması aynı ağırlıkta değildir. Birincisi hiçbir kullanımı bozmaz,
ikincisi o özelliği veren her yeri bozar.

Sürümleme bu ağırlık farkını bir sayıya çevirir. Modüller, Araçlar ve Ekosistem kursunda
tanıtılan anlamlı sürümleme, bir paketin sürüm numarasını değişikliğin sınıfına bağlar.
Bileşen kataloğunda uygulanabilmesi için tek bir şeye ihtiyaç vardır: bileşen arayüzünde
neyin kırıcı sayıldığının kesin listesi.

## Bileşen Arayüzünde Kırıcı Olan

Bir bileşenin sözleşmesi girdi özelliklerinden ibaret değildir, ama ölçülebilir bölümü
odur. Aşağıdaki değişiklikler kırıcıdır:

- **Bileşenin kaldırılması.** En görünür olanı; içe aktarım hemen kırılır.
- **Özelliğin kaldırılması.** O özelliği veren çağrılar sessizce etkisiz kalır.
- **Özelliğin adının değişmesi.** Kaldırma ile eklemenin birleşimi gibi görünür ama göç
  adımı farklıdır.
- **İsteğe bağlı özelliğin zorunlu hâle gelmesi.** Özelliği vermeyen her çağrı bozulur.
- **Varsayılan değerin değişmesi.** Kaynak metnini hiç değiştirmemiş her çağrı farklı
  davranış görür.
- **Değer kümesinin daralması.** Kalkan değeri veren çağrılar tanımsız duruma düşer.

Bunların dışında kalan iki sınıf uyumludur. **Ekleme**: yeni bileşen, isteğe bağlı yeni
özellik, değer kümesinin genişlemesi. **Yama**: arayüzü değiştirmeyen davranış düzeltmesi.

Listede olmayan ama önemli bir kalem var: **belge ağacının yapısı ve sınıf adları**. Bir
bileşenin ürettiği düğüm ağacına dışarıdan CSS yazılabiliyorsa, o ağaç da sözleşmenin
parçasıdır ve değişmesi kırıcıdır. Katalog bu yüzden ya ağacı sözleşmeye dahil eder ya da
dahil olmadığını açıkça yazar; ikisinden birini yapmayan sistem, her yayında adı konmamış
kırıcı değişiklikler üretir.

## Farkın Hesaplanması

Aşağıdaki program kataloğun iki anlık görüntüsünü alır, yukarıdaki kuralları uygular,
değişiklikleri sınıflandırır, sürüm numarasını türetir ve göç kılavuzunu üretir. Göç
maliyeti, her değişikliğin etkilediği çağrı yeri sayısıyla ölçülür.

```js
// surum.mjs — iki katalog anlik goruntusunu karsilastirip surum numarasi turetme

// Bir anlik goruntu: bilesen -> ozellik adi -> { deger kumesi, varsayilan, zorunlu }
const ESKI = {
  surum: "3.4.2",
  bilesen: {
    dugme:        { tur: { deger: ["birincil", "ikincil", "sessiz"], varsayilan: "ikincil", zorunlu: false },
                    boyut: { deger: ["kucuk", "orta"], varsayilan: "orta", zorunlu: false },
                    simge: { deger: null, varsayilan: null, zorunlu: false } },
    rozet:        { ton: { deger: ["notr", "olumlu", "uyari", "olumsuz"], varsayilan: "notr", zorunlu: false },
                    boyut: { deger: ["kucuk", "orta"], varsayilan: "orta", zorunlu: false },
                    ikonlu: { deger: null, varsayilan: false, zorunlu: false } },
    "acilir-liste": { boyut: { deger: ["kucuk", "orta"], varsayilan: "orta", zorunlu: false },
                    durum: { deger: null, varsayilan: null, zorunlu: false } },
    "kip-pencere": { boyut: { deger: ["orta", "genis"], varsayilan: "orta", zorunlu: false },
                    baslik: { deger: null, varsayilan: null, zorunlu: false } },
    "bildirim-seridi": { ton: { deger: ["bilgi", "olumlu", "uyari", "olumsuz"], varsayilan: "bilgi", zorunlu: false },
                    kapatilabilir: { deger: null, varsayilan: true, zorunlu: false } },
    "yukleme-gostergesi": { boyut: { deger: ["kucuk", "orta"], varsayilan: "orta", zorunlu: false } },
    iskelet:      { satir: { deger: null, varsayilan: 3, zorunlu: false } },
  },
};

const YENI = {
  bilesen: {
    dugme:        { tur: { deger: ["birincil", "ikincil", "sessiz", "tehlikeli"], varsayilan: "ikincil", zorunlu: false },
                    boyut: { deger: ["kucuk", "orta"], varsayilan: "orta", zorunlu: false },
                    "simge-adi": { deger: null, varsayilan: null, zorunlu: false },
                    genislik: { deger: ["icerik", "tam"], varsayilan: "icerik", zorunlu: false } },
    rozet:        { ton: { deger: ["notr", "olumlu", "uyari", "olumsuz"], varsayilan: "notr", zorunlu: false },
                    boyut: { deger: ["kucuk", "orta"], varsayilan: "kucuk", zorunlu: false } },
    "acilir-liste": { boyut: { deger: ["kucuk", "orta"], varsayilan: "orta", zorunlu: false },
                    durum: { deger: null, varsayilan: null, zorunlu: false },
                    aramali: { deger: null, varsayilan: false, zorunlu: false },
                    coklu: { deger: null, varsayilan: false, zorunlu: false } },
    "kip-pencere": { boyut: { deger: ["orta", "genis"], varsayilan: "orta", zorunlu: false },
                    baslik: { deger: null, varsayilan: null, zorunlu: true },
                    kapatilabilir: { deger: null, varsayilan: true, zorunlu: false } },
    "bildirim-seridi": { ton: { deger: ["olumlu", "uyari", "olumsuz"], varsayilan: "uyari", zorunlu: false },
                    kapatilabilir: { deger: null, varsayilan: true, zorunlu: false } },
    iskelet:      { satir: { deger: null, varsayilan: 3, zorunlu: false } },
    "sayi-alani": { boyut: { deger: ["kucuk", "orta"], varsayilan: "orta", zorunlu: false } },
  },
};

// Ad degisikligi elle bildirilir; yoksa kaldirma + ekleme olarak gorunur.
const YENIDEN_ADLANDIRMA = [{ bilesen: "dugme", eski: "simge", yeni: "simge-adi" }];

// Cagri yeri sayilari: goc maliyetini bu sayilar belirler.
const KULLANIM = { dugme: 412, rozet: 188, "acilir-liste": 57, "kip-pencere": 45, "bildirim-seridi": 63, "yukleme-gostergesi": 31, iskelet: 38 };
// Ozellik basina cagri yeri sayisi (o ozelligi acikca veren yerler).
const OZELLIK_KULLANIMI = { "dugme.simge": 96, "rozet.ikonlu": 24, "rozet.boyut": 61, "bildirim-seridi.ton": 63, "kip-pencere.baslik": 40 };

const esitKume = (a, b) => JSON.stringify(a) === JSON.stringify(b);
const adlandirmaBul = (bilesen, ad, yon) =>
  YENIDEN_ADLANDIRMA.find((r) => r.bilesen === bilesen && r[yon] === ad);

const degisiklikler = [];
const ekle = (sinif, bilesen, aciklama, gocAdimi, etki) =>
  degisiklikler.push({ sinif, bilesen, aciklama, gocAdimi, etki });

for (const [ad, eskiOzellikler] of Object.entries(ESKI.bilesen)) {
  const yeniOzellikler = YENI.bilesen[ad];
  if (!yeniOzellikler) {
    ekle("kirici", ad, "bileşen kaldırıldı", "yerine iskelet bileşeni kullanılır", KULLANIM[ad] ?? 0);
    continue;
  }
  for (const [ozAd, eskiOz] of Object.entries(eskiOzellikler)) {
    const adDegisimi = adlandirmaBul(ad, ozAd, "eski");
    if (adDegisimi) {
      ekle("kirici", ad, `özellik adı değişti: ${ozAd} → ${adDegisimi.yeni}`,
        `${ozAd} yazan her çağrı ${adDegisimi.yeni} olur`, OZELLIK_KULLANIMI[`${ad}.${ozAd}`] ?? 0);
      continue;
    }
    const yeniOz = yeniOzellikler[ozAd];
    if (!yeniOz) {
      ekle("kirici", ad, `özellik kaldırıldı: ${ozAd}`, `${ozAd} veren çağrılardan kaldırılır`,
        OZELLIK_KULLANIMI[`${ad}.${ozAd}`] ?? 0);
      continue;
    }
    if (!eskiOz.zorunlu && yeniOz.zorunlu) {
      ekle("kirici", ad, `özellik zorunlu hâle geldi: ${ozAd}`, `${ozAd} vermeyen çağrılara değer eklenir`,
        (KULLANIM[ad] ?? 0) - (OZELLIK_KULLANIMI[`${ad}.${ozAd}`] ?? 0));
    }
    if (eskiOz.varsayilan !== yeniOz.varsayilan) {
      ekle("kirici", ad, `varsayılan değişti: ${ozAd} ${JSON.stringify(eskiOz.varsayilan)} → ${JSON.stringify(yeniOz.varsayilan)}`,
        `${ozAd} vermeyen çağrılara eski değer açıkça yazılır`,
        (KULLANIM[ad] ?? 0) - (OZELLIK_KULLANIMI[`${ad}.${ozAd}`] ?? 0));
    }
    if (eskiOz.deger && yeniOz.deger && !esitKume(eskiOz.deger, yeniOz.deger)) {
      const kalkan = eskiOz.deger.filter((d) => !yeniOz.deger.includes(d));
      const gelen = yeniOz.deger.filter((d) => !eskiOz.deger.includes(d));
      if (kalkan.length) {
        ekle("kirici", ad, `değer kümesi daraldı: ${ozAd} ${JSON.stringify(kalkan)} kalktı`,
          `${JSON.stringify(kalkan)} veren çağrılar başka değere taşınır`, OZELLIK_KULLANIMI[`${ad}.${ozAd}`] ?? 0);
      }
      if (gelen.length) ekle("ekleme", ad, `değer kümesi genişledi: ${ozAd} ${JSON.stringify(gelen)} eklendi`, "-", 0);
    }
  }
  for (const ozAd of Object.keys(yeniOzellikler)) {
    if (eskiOzellikler[ozAd]) continue;
    if (adlandirmaBul(ad, ozAd, "yeni")) continue;
    const sinif = yeniOzellikler[ozAd].zorunlu ? "kirici" : "ekleme";
    ekle(sinif, ad, `${sinif === "kirici" ? "zorunlu " : ""}özellik eklendi: ${ozAd}`,
      sinif === "kirici" ? `${ozAd} her çağrıya eklenir` : "-", sinif === "kirici" ? (KULLANIM[ad] ?? 0) : 0);
  }
}
for (const ad of Object.keys(YENI.bilesen)) {
  if (!ESKI.bilesen[ad]) ekle("ekleme", ad, "bileşen eklendi", "-", 0);
}

const SIRA = { kirici: 0, ekleme: 1, yama: 2 };
degisiklikler.sort((a, b) => SIRA[a.sinif] - SIRA[b.sinif] || b.etki - a.etki);

console.log("sınıf     bileşen             değişiklik");
for (const d of degisiklikler) console.log(`${d.sinif.padEnd(9)} ${d.bilesen.padEnd(19)} ${d.aciklama}`);

const sayim = { kirici: 0, ekleme: 0, yama: 0 };
for (const d of degisiklikler) sayim[d.sinif]++;
console.log(`\nkırıcı: ${sayim.kirici}   ekleme: ${sayim.ekleme}   yama: ${sayim.yama}`);

// Surum turetimi: en agir sinif hangi bilesenin artacagini belirler.
function sonrakiSurum(mevcut, sayim) {
  const [ana, ikincil, yama] = mevcut.split(".").map(Number);
  if (sayim.kirici > 0) return `${ana + 1}.0.0`;
  if (sayim.ekleme > 0) return `${ana}.${ikincil + 1}.0`;
  return `${ana}.${ikincil}.${yama + 1}`;
}
console.log(`mevcut sürüm: ${ESKI.surum}   türetilen sürüm: ${sonrakiSurum(ESKI.surum, sayim)}`);
console.log(`kırıcılar ayıklansaydı: ${sonrakiSurum(ESKI.surum, { kirici: 0, ekleme: sayim.ekleme, yama: sayim.yama })}`);

// Goc kilavuzu: yalniz kirici degisiklikler, etkiledigi cagri yeri sayisiyla.
console.log("\n— GÖÇ KILAVUZU —");
let toplamEtki = 0;
for (const d of degisiklikler.filter((x) => x.sinif === "kirici")) {
  toplamEtki += d.etki;
  console.log(`* ${d.bilesen}: ${d.aciklama}\n    yapılacak: ${d.gocAdimi}\n    etkilenen çağrı yeri: ${d.etki}`);
}
console.log(`\ntoplam göç maliyeti: ${toplamEtki} çağrı yeri`);
console.log(`kırıcı değişiklik başına ortalama: ${(toplamEtki / sayim.kirici).toFixed(1)} çağrı yeri`);
```

```
sınıf     bileşen             değişiklik
kirici    rozet               varsayılan değişti: boyut "orta" → "kucuk"
kirici    dugme               özellik adı değişti: simge → simge-adi
kirici    bildirim-seridi     değer kümesi daraldı: ton ["bilgi"] kalktı
kirici    yukleme-gostergesi  bileşen kaldırıldı
kirici    rozet               özellik kaldırıldı: ikonlu
kirici    kip-pencere         özellik zorunlu hâle geldi: baslik
kirici    bildirim-seridi     varsayılan değişti: ton "bilgi" → "uyari"
ekleme    dugme               değer kümesi genişledi: tur ["tehlikeli"] eklendi
ekleme    dugme               özellik eklendi: genislik
ekleme    acilir-liste        özellik eklendi: aramali
ekleme    acilir-liste        özellik eklendi: coklu
ekleme    kip-pencere         özellik eklendi: kapatilabilir
ekleme    sayi-alani          bileşen eklendi

kırıcı: 7   ekleme: 6   yama: 0
mevcut sürüm: 3.4.2   türetilen sürüm: 4.0.0
kırıcılar ayıklansaydı: 3.5.0

— GÖÇ KILAVUZU —
* rozet: varsayılan değişti: boyut "orta" → "kucuk"
    yapılacak: boyut vermeyen çağrılara eski değer açıkça yazılır
    etkilenen çağrı yeri: 127
* dugme: özellik adı değişti: simge → simge-adi
    yapılacak: simge yazan her çağrı simge-adi olur
    etkilenen çağrı yeri: 96
* bildirim-seridi: değer kümesi daraldı: ton ["bilgi"] kalktı
    yapılacak: ["bilgi"] veren çağrılar başka değere taşınır
    etkilenen çağrı yeri: 63
* yukleme-gostergesi: bileşen kaldırıldı
    yapılacak: yerine iskelet bileşeni kullanılır
    etkilenen çağrı yeri: 31
* rozet: özellik kaldırıldı: ikonlu
    yapılacak: ikonlu veren çağrılardan kaldırılır
    etkilenen çağrı yeri: 24
* kip-pencere: özellik zorunlu hâle geldi: baslik
    yapılacak: baslik vermeyen çağrılara değer eklenir
    etkilenen çağrı yeri: 5
* bildirim-seridi: varsayılan değişti: ton "bilgi" → "uyari"
    yapılacak: ton vermeyen çağrılara eski değer açıkça yazılır
    etkilenen çağrı yeri: 0

toplam göç maliyeti: 346 çağrı yeri
kırıcı değişiklik başına ortalama: 49.4 çağrı yeri
```

## Sürüm Numarasının Türetilmesi

Sürüm numarası bir karar değil, bir **sonuçtur**. Program yedi kırıcı değişiklik bulduğu
için 3.4.2 sürümü 4.0.0'a çıkıyor. Kırıcılar ayıklanıp yalnızca eklemeler yayımlansaydı
sürüm 3.5.0 olurdu; bu ikinci satır, sürüm numarasını tartışılabilir bir konu olmaktan
çıkarıp yayın kapsamının sonucu hâline getirir.

Bu ayrım pratik bir seçenek üretir. Yedi kırıcı değişiklikten hiçbiri yayına zorunlu
değilse, ekleme yayını hemen çıkabilir ve kırıcılar bir sonraki ana sürüme biriktirilebilir.
Kırıcı değişiklikleri **biriktirmek**, her birini ayrı ana sürümle yayımlamaktan ucuzdur:
kullanan ekipler bir kez göç eder, yedi kez değil.

Ad değişikliği satırı türetimin sınırını gösteriyor. `simge` özelliğinin `simge-adi`
olması, program tarafından ancak elle bildirildiği için ad değişikliği olarak
tanınabiliyor. Bildirilmeseydi iki ayrı satır üretilirdi: "özellik kaldırıldı: simge" ve
"özellik eklendi: simge-adi". Sürüm numarası yine 4.0.0 çıkardı, ama göç kılavuzu yanlış
olurdu; kullanan kişi kaldırma ile eklemenin aynı özelliğin iki hâli olduğunu bilemezdi.
Fark alma otomatik, **niyet bildirimi elle** yapılır.

## Göç Maliyetinin Ölçülmesi

Göç kılavuzu, kırıcı değişikliklerin çağrı yeri sayısına göre sıralanmış hâlidir ve bu
sıralama iki kararı besler.

Birincisi, hangi kırıcı değişikliğin gerçekten değdiğidir. `rozet` bileşeninin `boyut`
varsayılanının değişmesi 127 çağrı yerini etkiliyor; `ikonlu` özelliğinin kaldırılması
yalnızca 24. Aynı yayında duran iki değişikliğin maliyeti beş kattan fazla farklı.
Varsayılan değişikliğinin gerekçesi bu 127 sayısına değmiyorsa, değişiklik geri çekilir ve
kırıcı listesinden düşer.

İkincisi, göçün nasıl yapılacağıdır. Listedeki adımların bir bölümü **mekaniktir**: ad
değişikliği, bir dizgi değiştirmedir ve kod dönüştürücüyle otomatikleştirilebilir. Bir
bölümü ise **karar ister**: `bildirim-seridi` bileşeninin `bilgi` tonunu veren 63 çağrının
hangisinin `olumlu`, hangisinin `uyari` olacağına bakmadan karar verilemez. Kılavuzun bu
iki sınıfı ayırması, göç süresinin tahminini gerçekçi kılar.

Son satır ilginç bir durum gösteriyor: `bildirim-seridi` bileşeninin varsayılan
değişikliğinin etkisi sıfır. Nedeni, o bileşeni kullanan 63 çağrının hepsinin `ton`
değerini zaten açıkça vermesi. Değişiklik sözleşme gereği kırıcıdır — sürüm numarasını
yükseltir — ama göç maliyeti yoktur. Sözleşme ile maliyet ayrı şeylerdir; sürüm numarası
sözleşmeye, yayın planı maliyete bakar.

## Yayın Biçimi

Kataloğun tek paket olarak mı yoksa bileşen başına ayrı paketler olarak mı yayımlanacağı,
yukarıdaki sayıların dağılımını değiştirir.

**Tek paket** yayımlandığında bir bileşendeki kırıcı değişiklik tüm paketin ana sürümünü
yükseltir. Yukarıdaki örnekte `rozet` yüzünden `iskelet` bileşenini kullanan ekip de 4.0.0
sürümüne geçmek zorunda kalır; kendisi için hiçbir şey değişmediği hâlde bir göç kararı
vermesi gerekir. Buna karşılık bağımlılık yönetimi tektir, sürümler arası uyum sorunu
çıkmaz.

**Bileşen başına paket** yayımlandığında her bileşen kendi hızında ilerler ve göç kararları
ayrışır. Bedeli, bağımlılık ağacında aynı sistemin farklı sürümlerinin yan yana
bulunabilmesidir; iki bileşen aynı belirteç katmanının farklı sürümlerine bağlanırsa
tutarlılık, sistemin var oluş nedeni, kaybolur.

Seçim, iki sayının karşılaştırılmasıyla yapılır: gereksiz göçe zorlanan çağrı yeri sayısı
ile yan yana duran sürümlerden doğan tutarsızlık sayısı. Katalog küçük ve bileşenler
birbirine sıkı bağlıyken tek paket ucuzdur; katalog büyüdükçe ve bileşenler arası bağ
gevşedikçe ayrı paketler ucuzlar.

## Kullanımdan Kaldırma Penceresi

Kırıcı değişiklik doğrudan yapılmaz; önce kullanımdan kaldırma işareti konur, sonra
kaldırma yapılır. Aradaki süre bir **pencere**dir ve takvimle değil yayınla ölçülür:
"bir ana sürüm boyunca" ifadesi, "üç ay" ifadesinden daha kullanışlıdır, çünkü yayın
kadansı değişse bile anlamını korur.

Pencerenin işlevi, göçün yayın anına yığılmasını önlemektir. `simge` özelliği bir ana
sürüm önce kullanımdan kaldırılmış olsaydı, 96 çağrı yerinin bir bölümü zaten taşınmış
olurdu ve 4.0.0 yayınındaki göç maliyeti daha düşük çıkardı. Kullanımdan kaldırma işareti
konurken uyarı metninin **göç adımını** içermesi de bu yüzden gereklidir; uyarı yalnızca
"bu özellik kaldırılacak" diyorsa hiçbir göçü hızlandırmaz.

## Özet

- Bileşen arayüzünde kırıcı olanlar sayılabilir bir listedir: bileşenin kaldırılması,
  özelliğin kaldırılması veya adının değişmesi, zorunlu hâle gelme, varsayılanın değişmesi
  ve değer kümesinin daralması.
- Sürüm numarası bir karar değil, yayına giren en ağır değişiklik sınıfının sonucudur;
  kırıcıları biriktirip tek ana sürümde yayımlamak, her birini ayrı yayımlamaktan ucuzdur.
- Fark alma otomatikleştirilebilir ama ad değişikliği gibi niyet bildirimleri elle
  yapılmalıdır; bildirilmezse kılavuz yanlış olur.
- Göç maliyeti etkilenen çağrı yeri sayısıyla ölçülür ve mekanik adımlarla karar
  gerektiren adımların ayrılması gerekir.
- Sözleşme ile maliyet ayrı şeylerdir: göç maliyeti sıfır olan bir değişiklik de sürüm
  numarasını yükseltir.
- Tek paket ile bileşen başına paket arasındaki seçim, gereksiz göçe zorlanan çağrı yeri
  sayısı ile yan yana duran sürümlerden doğan tutarsızlık sayısı karşılaştırılarak yapılır.

## Sonraki Adım

Bu dersin ürettiği sürüm ve göç kılavuzu, değişikliklerin nereden geldiğini sormadı;
arayüz tanımı iki kez okundu ve arada bir şey değişmişti. Değişikliği kimin önerdiği,
kimin incelediği ve hangi ölçütle kabul edildiği açık kaldı. `dugme` bileşenine `genislik`
özelliğinin eklenmesi kimin kararıydı ve bu karar kaç günde verildi? Sonraki ders katkı
sürecini bir durum makinesi olarak kurar, geçersiz geçişleri saptar ve öneriden yayına
kadar geçen süreyi aşamalara ayırarak ölçer.
