---
title: 'Sürekli Tümleştirme'
source: 'https://academia.sh/tr/kurslar/ci-cd/surekli-tumlestirme'
course: 'Sürekli Tümleştirme ve Teslimat'
language: tr
updated: '2026-08-17T18:09:03+00:00'
license: 'CC BY-SA 4.0'
---

# Sürekli Tümleştirme

Birleştirme aralığının taranması: aralık büyüdükçe çatışan dosya, çözüm turu ve çözümde doğan hata nasıl büyüyor, sıklığın kendi bedeli kaç koşum ve kaç çalıştırıcı dakikası tutuyor, birleştirme kapısı neyi yalnız kendisi yakalıyor ve kaç temiz değişikliği boşuna durduruyor.

Önceki kurs bir küme bıraktı: istenen durum bir yerde yazılı, uzlaştırıcı onu sürekli
uyguluyor, gerçek durum sapınca geri çekiliyor. Kapanışta duran soru şuydu — o istenen
durumu **kim, neye bakarak ve hangi denetimden geçirerek** değiştiriyor? Bu kurs o yolu
kurar. Değişikliğin geliştiricinin makinesinden üretime giden hattı üzerinde duran, geçmesine
izin veren ya da vermeyen her adım bir **kapı**dır ve bu kursun ölçü birimi kapıdır.

Yoldaki ilk kapı en eskisidir: bir değişikliğin ana dala girmesi. **Sürekli tümleştirme**
(continuous integration), her geliştiricinin işini ana dala sık aralıklarla döndürmesi ve her
dönüşte otomatik bir denetimden geçirmesidir. "Sık" bir sayı değil, bir eksendir; bu ders o
ekseni tarar. Taranan büyüklük **birleştirme aralığı**dır: bir dalın ana daldan ayrı yaşadığı
süre. Aralık büyüdükçe iki şey birlikte büyür — çatışan dosya sayısı ve çatışmayı çözerken
doğan hata. Aralık küçüldükçe başka bir şey büyür — koşum sayısı ve çalıştırıcı zamanı.
Sıklığın bedava olmadığı yer burasıdır. Süreç Modelleri konusunda ölçülen **hata bulma
gecikmesi** burada yeniden ölçülmez; bu dersin ölçtüğü şey çatışmanın kendisidir.

## Depo Modeli

Ölçüm, bölgesel ölçüm ağının deposu üzerinde yapılır: su sayacı okumalarını toplayan servis,
okumaları doğrulayan servis, faturalama ve iş emri servisleri. **Bu ağ kurgudur; aşağıdaki
depo da bir modeldir, gerçek bir depo taranmamıştır.** Süreler model dakikasıdır ve
ölçümün koştuğu makineden bağımsızdır.

**HT1 — depo dört servise bölünmüş 120 dosyadır; sekiz geliştirici yirmi iş günü çalışır,
gün 480 model dakikasıdır.** Her servisin ilk üç dosyası **arayüz dosyası**dır: başka
servisin beklentisini taşır.

**HT2 — bir geliştirici saatte 0,5 dosyaya dokunur.** Toplam iş aralıktan bağımsızdır:
her tarama satırında 640 dosya dokunuşu vardır, değişen yalnızca bu işin kaç birleştirmeye
bölündüğüdür.

**HT3 — dosyaların beşte biri sıcaktır ve sekiz kat sık dokunulur; geliştirici kendi
servisini yüzde 75 tercih eder.** Üreteç kendi yazılmıştır, tohum görünürdür.

**HT4 — çatışan dosya başına çözüm 20 model dakikasıdır.** Çözüm sürerken ana dal ilerler;
o sırada aynı dosyalara dokunan yeni bir birleşme olursa yeni bir tur açılır, en çok altı tur.

**HT5 — çatışan dosya başına çözümde hata doğma olasılığı 0,09'dur.** Çözümde doğan bir hata
ile kapının kaçırdığı bir arayüz uyuşmazlığının sonradan bulunup düzeltilmesi 180 model
dakikasıdır.

**HT6 — bir hat koşumu 14 model dakikası çalıştırıcı zamanı harcar; koşumların yüzde ikisi
kararsız kırmızı verir** ve temiz bir değişikliği boşuna durdurup bir koşum daha ister.

```js
// depo.mjs — kurgu bolgesel olcum agi deposunun modeli: servis, dosya, gelistirici
// Tohumlu ureteg: 32 bitlik dogrusal esleme; disaridan kutuphane kullanilmaz.
export const uretec = (tohum) => () => {
  tohum = (tohum * 1664525 + 1013904223) >>> 0;
  return tohum / 4294967296;
};

// HT1: dort servis, servis basina dosya sayisi; ilk uc dosya arayuz dosyasidir.
export const servisler = [
  { ad: 'toplayici', dosya: 38 },
  { ad: 'dogrulayici', dosya: 26 },
  { ad: 'faturalama', dosya: 31 },
  { ad: 'is-emri', dosya: 25 },
];

export const dosyalar = servisler.flatMap((s, si) =>
  Array.from({ length: s.dosya }, (_, i) => ({
    ad: `${s.ad}/${i}`, servis: si, arayuz: i < 3,
    // HT3: dosyalarin bese biri sicaktir ve sekiz kat sik dokunulur.
    agirlik: i % 5 === 0 ? 8 : 1,
  })));

// Agirliga gore dosya secimi: gelistirici kendi servisini yuzde 75 tercih eder.
export const dosyaSec = (r, gelistirici) => {
  const kendi = r() < 0.75;
  const havuz = kendi ? dosyalar.filter((d) => d.servis === gelistirici % 4) : dosyalar;
  const top = havuz.reduce((a, d) => a + d.agirlik, 0);
  let x = r() * top;
  for (const d of havuz) { x -= d.agirlik; if (x <= 0) return d; }
  return havuz[havuz.length - 1];
};

export const GELISTIRICI = 8;
export const GUN = 20, GUN_DK = 480;           // HT1: yirmi is gunu, gunde 480 model dakikasi
export const HIZ = 0.5;                        // HT2: saatte 0,5 dosya dokunusu
export const COZUM_DK = 20;                    // HT4: catisan dosya basina cozum suresi
export const HATA_OLASILIK = 0.09;             // HT5: cozumde hata dogma olasiligi
export const SONRAKI_DK = 180;                 // HT5: gec bulunan bir hatanin bedeli
export const KARARSIZ = 0.02;                  // HT6: kosumlarin yuzde ikisi yanlis tutar
export const KOSUM_DK = 14;                    // HT6: bir hat kosumunun calistirici zamani
```

## Aralık Taraması

Tarama altı aralık için aynı işi koşturur: iki saat, dört saat, bir gün, iki gün, dört gün ve
on iş günü. Her satır aynı tohumla başlar, yani karşılaştırılan şey aralıktır.

```js
// aralik.mjs — birlestirme araligi tarandiginda catisma ve kapinin bedeli
import * as D from './depo.mjs';

const kos = (saat, tohum = 7391) => {
  const r = D.uretec(tohum);                       // her satir ayni tohumla baslar
  const T = saat * 60, ufuk = D.GUN * D.GUN_DK, n = D.HIZ * saat;
  const govde = [], olay = [];                     // govde: birlesmis dosyalar {t, dosya, ...}
  for (let dev = 0; dev < D.GELISTIRICI; dev++) {
    for (let k = 0; k < ufuk / T; k++) olay.push({ dev, bas: k * T + dev * (T / D.GELISTIRICI) });
  }
  olay.sort((a, b) => a.bas - b.bas);
  let birlestirme = 0, catisan = 0, tur = 0, hata = 0, anlamsal = 0, yanlis = 0, kosum = 0, cozumDk = 0;

  for (const o of olay) {
    const kume = new Set(), arayuz = new Set();
    while (kume.size < n) {                        // dal ayri n dosyaya dokunur
      const d = D.dosyaSec(r, o.dev);
      if (kume.has(d.ad)) continue;
      kume.add(d.ad);
      if (d.arayuz) arayuz.add(d.servis);
    }
    birlestirme++;
    let t = o.bas + T, pencere = o.bas, adim = 0;
    while (true) {
      const rakip = govde.filter((g) => g.t > pencere && g.t <= t && g.dev !== o.dev);
      const c = new Set(rakip.filter((g) => kume.has(g.dosya)).map((g) => g.dosya)).size;
      kosum++;
      if (r() < D.KARARSIZ) { yanlis++; kosum++; }  // kararsiz kirmizi: temiz degisiklik durur
      if (adim === 0) {                             // metinsel catismasi olmayan arayuz uyusmazligi
        anlamsal += new Set(rakip.filter((g) => g.arayuz && arayuz.has(g.servis) && !kume.has(g.dosya))
          .map((g) => g.dosya)).size;
      }
      if (c === 0 || adim >= 5) break;
      catisan += c; tur++;
      for (let i = 0; i < c; i++) if (r() < D.HATA_OLASILIK) hata++;
      const sure = c * D.COZUM_DK;
      cozumDk += sure; pencere = t; t += sure; adim++;
    }
    for (const ad of kume) {
      const d = D.dosyalar.find((x) => x.ad === ad);
      govde.push({ t, dosya: ad, dev: o.dev, servis: d.servis, arayuz: d.arayuz });
    }
  }
  const sonrakiBedel = (hata + anlamsal) * D.SONRAKI_DK;
  return { saat, n, birlestirme, sonrakiBedel, catisan, tur, hata, anlamsal, yanlis, kosum,
    cozumDk, kosumDk: kosum * D.KOSUM_DK, dokunus: govde.length };
};

const s = (x, w) => String(x).padStart(w);
const satir = [2, 4, 8, 16, 32, 80].map((h) => kos(h));

const f = (x, w) => String(x.toFixed(2)).padStart(w);
console.log(`${'aralik'.padEnd(9)}${s('dal dosya', 10)}${s('birlestirme', 12)}${s('catisan', 9)}${s('catisan/blt', 12)}${s('tur/blt', 9)}${s('cozum hatasi', 14)}${s('kacan anlamsal', 16)}`);
for (const x of satir) {
  console.log(`${(x.saat + ' sa').padEnd(9)}${s(x.n, 10)}${s(x.birlestirme, 12)}${s(x.catisan, 9)}`
    + `${f(x.catisan / x.birlestirme, 12)}${f(x.tur / x.birlestirme, 9)}${s(x.hata, 14)}${s(x.anlamsal, 16)}`);
}
console.log(`\n${'aralik'.padEnd(9)}${s('kosum', 7)}${s('calistirici dk', 15)}${s('cozum dk', 10)}${s('gec bulma dk', 14)}${s('toplam dk', 11)}${s('yanlis tutma', 14)}`);
for (const x of satir) {
  console.log(`${(x.saat + ' sa').padEnd(9)}${s(x.kosum, 7)}${s(x.kosumDk, 15)}${s(x.cozumDk, 10)}${s(x.sonrakiBedel, 14)}`
    + `${s(x.kosumDk + x.cozumDk + x.sonrakiBedel, 11)}${s(x.yanlis, 14)}`);
}
const enIyi = satir.reduce((a, x) =>
  (x.kosumDk + x.cozumDk + x.sonrakiBedel < a.kosumDk + a.cozumDk + a.sonrakiBedel ? x : a));
console.log(`\ndokunulan dosya her satirda ${satir.map((x) => x.dokunus).join('/')} (is sabit); `
  + `tohum 7391 icin en dusuk toplam ${enIyi.saat} saatlik aralikta`);
const en = (tohum) => [2, 4, 8, 16, 32, 80].map((h) => kos(h, tohum))
  .reduce((a, x) => (x.kosumDk + x.cozumDk + x.sonrakiBedel < a.kosumDk + a.cozumDk + a.sonrakiBedel ? x : a));
console.log('baska tohumlar: ' + [20260801, 4517, 66049, 131].map((s) => `${s} -> ${en(s).saat} sa`).join(', '));
```

```
aralik    dal dosya birlestirme  catisan catisan/blt  tur/blt  cozum hatasi  kacan anlamsal
2 sa              1         640       74        0.12     0.12            12               3
4 sa              2         320       90        0.28     0.27             4              10
8 sa              4         160      205        1.28     0.81            22              17
16 sa             8          80      320        4.00     0.97            31              26
32 sa            16          40      484       12.10     1.52            41              32
80 sa            40          16      654       40.88     1.63            55              43

aralik     kosum calistirici dk  cozum dk  gec bulma dk  toplam dk  yanlis tutma
2 sa         731          10234      1480          2700      14414            17
4 sa         407           5698      1800          2520      10018             2
8 sa         297           4158      4100          7020      15278             8
16 sa        163           2282      6400         10260      18942             5
32 sa        103           1442      9680         13140      24262             2
80 sa         43            602     13080         17640      31322             1

dokunulan dosya her satirda 640/640/640/640/640/640 (is sabit); tohum 7391 icin en dusuk toplam 4 saatlik aralikta
baska tohumlar: 20260801 -> 4 sa, 4517 -> 8 sa, 66049 -> 4 sa, 131 -> 4 sa
```

## Çatışma Doğrusal Büyümüyor

İlk tablonun asıl satırı `catisan/blt` sütunudur. Dal ömrü iki saatten kırk kat büyüdüğünde
birleştirme başına çatışan dosya 0,12'den 40,88'e çıkar — kat sayısı 340'tır. Büyüme dal
başına dosya sayısıyla orantılı değildir, çünkü çatışma iki kümenin **kesişimi**dir: hem
kendi dalı, hem de o sırada ana dala giren toplam iş büyür. İki uzun dal aynı sıcak dosyalara
dokunma olasılığını ikisi birden artırır.

`tur/blt` sütunu ilk bakışta bu büyümeye eşlik etmez: 0,12'den 1,63'e çıkar ve orada
doyar. Sebebi ölçümün içindedir — bir çözüm turu, çözüm sürerken ana dalın kımıldamasıyla
açılır. Dallar uzun olduğunda birleşmeler seyrektir, yani çözüm sürerken ana dal genellikle
kımıldamaz ve tek tur yeter. Uzun dalın bedeli tur sayısında değil, **turun uzunluğunda**
görünür: birleştirme başına çözüm süresi iki saatlik dalda 2,3 model dakikası, on iş günlük
dalda 818 model dakikasıdır.

Çözümde doğan hata da aralıkla büyür: 12'den 55'e. Bu hatalar birleştirmenin **kendi
ürettiği** kusurlardır; kimse yanlış kod yazmamıştır, iki doğru değişikliğin elle
uzlaştırılması sırasında doğmuşlardır. Dört saatlik satırdaki 4 değeri komşularının altında
kalır; bu sayılar küçük olduğu için tohumun gürültüsüdür, eğilim değildir.

## Sıklığın Kendi Bedeli

İkinci tablo ters yönü verir. İki saatlik aralıkta hat 731 kez koşar ve 10 234 model dakikası
çalıştırıcı zamanı harcar; on iş günlük aralıkta 43 koşum ve 602 dakika. Sık birleştirme
makineye on yedi kat daha pahalıdır. Aynı sütunla birlikte okunması gereken sayı `yanlis
tutma`dır: kararsız kırmızı koşum başına sabit olasılıktadır, dolayısıyla koşum arttıkça
yanlış durdurma da artar — 17'ye karşı 1. Sık birleştiren bir ekipte kapının haksız
yere durdurduğu değişiklik sayısı, seyrek birleştiren bir ekiptekinden on yedi kat fazladır
ve kapıyı atlama isteği tam buradan doğar.

Toplam sütunu üç kalemi birleştirir: çalıştırıcı zamanı, çözüm süresi ve geç bulunan
hatanın bedeli. Eğri iki uçtan da yükselir ve en düşük değer dört saatte, 10 018 dakikadadır.
Beş tohumun dördü aynı aralığı, biri sekiz saati seçer. **Sonuç "ne kadar sık, o kadar iyi"
değildir**: iki saatlik aralıkta çalıştırıcı zamanı ve yanlış tutma toplamı 4 000 dakikadan
fazla şişirir, on iş günlük aralıkta çözüm ve geç bulma 30 000 dakikaya çıkar. Aralığın
optimumu, koşumun bedeli ile çatışmanın bedelinin kesiştiği yerdedir; koşum ucuzladıkça
optimum sola kayar, bu da sonraki derslerin hat süresini kısaltma çabasının asıl gerekçesidir.

## Birleştirme Kapısının Üç Sayısı

Bu kapı ana dala giriş kapısıdır ve üç sayıyla yazılır. **Tuttuğu:** dört saatlik aralıkta
90 çatışan dosya birleştirme anında görünür hâle gelir. Bunun **yalnız bu kapıya ait**
olan kısmı tamamıdır — metinsel çatışmayı hattaki hiçbir sonraki kapı göremez. Derleme kapısı
kötü çözülmüş bir birleşmeyi ancak "bozuk" diye bildirir, hangi iki değişikliğin çakıştığını
söyleyemez; çatışma bilgisi birleştirme anında vardır, sonra kaybolur.

**Kaçırdığı:** arayüz uyuşmazlıkları. Bunlar iki geliştiricinin **farklı** dosyalara
dokunduğu, yani metinsel çatışması olmayan, ama birinin beklentisiyle ötekinin verdiği
biçimin ayrıştığı durumlardır. Dört saatlik aralıkta 10, on iş günlük aralıkta 43 tanesi
kapıdan geçer. Çözümde doğan hatalar da kapıdan geçer, çünkü kapı çözümün kendisini denetlemez.
**Yanlış tuttuğu:** dört saatlik aralıkta 2, iki saatlik aralıkta 17 temiz değişiklik.
**Eklediği süre:** koşum başına 14 dakika çalıştırıcı zamanı ve çatışan dosya başına 20 dakika
insan zamanı; dört saatlik aralıkta değişiklik başına 23 dakika.

Bu dört sayı birlikte okunmadığında kapı savunulamaz. Yalnız çalıştırıcı zamanına bakan bir
değerlendirme sık birleştirmeyi pahalı bulur ve aralığı büyütür; yalnız çatışma sayısına
bakan bir değerlendirme aralığı sıfıra indirmek ister. İkisi de yanlıştır, çünkü kapının
değeri tuttuğu ile kaçırdığının farkındadır ve bu fark aralığa bağlıdır. Aynı kapı iki
saatlik aralıkta 74 çatışma tutup 3 uyuşmazlık kaçırır, on iş günlük aralıkta 654 tutup 43
kaçırır: daha çok tutmak, daha iyi tuttuğu anlamına gelmez — tuttuğu şeyin çoğunu aralığın
kendisi üretmiştir.

Kaçırdığı iki kalem, bu kapının tek başına yetmediğini söyler ve hattın geri kalanının
gerekçesidir: arayüz uyuşmazlığını yakalayacak olan sözleşme sınaması, çözümde doğan hatayı
yakalayacak olan test kapısıdır. Kapıların sırası ve neyi yakaladığı, kursun ilerleyen
derslerinin konusudur.

## Özet

- Birleştirme aralığı iki saatten on iş gününe çıktığında birleştirme başına çatışan dosya
  0,12'den 40,88'e, çözümde doğan hata 12'den 55'e, kapının kaçırdığı arayüz uyuşmazlığı
  3'ten 43'e çıktı.
- Çözüm turu sayısı 1,63'te doydu; uzun dalın bedeli tur sayısında değil tur uzunluğundadır
  — birleştirme başına çözüm süresi 2,3 dakikadan 818 model dakikasına çıktı.
- Sıklığın kendi bedeli sayıldı: iki saatlik aralık 731 koşum ve 10 234 çalıştırıcı dakikası,
  on iş günlük aralık 43 koşum ve 602 dakika ister.
- Yanlış tutma koşum sayısıyla orantılıdır: 17'ye karşı 1; kapıyı atlama isteği bu sayıdan
  doğar.
- Üç kalem toplandığında en düşük değer dört saatlik aralıktadır (10 018 dakika) ve beş
  tohumun dördü aynı yeri gösterir; koşum ucuzladıkça optimum daha sık birleştirmeye kayar.

## Sonraki Adım

Bu dersin kapısı ana dala giriştedir ve kararı tek bir sorunun cevabıdır: bu değişiklik
girsin mi? Ana dal yeşile döndükten sonra ne olacağı sorusuna hiç dokunulmadı. Yeşil bir ana
dal, üretime konabilecek bir şey midir, yoksa yalnızca üretime konmaya **aday** bir şey mi?
İki cevap iki ayrı düzen kurar ve aralarındaki fark tek bir kapıda toplanır: yeşil koşumla
üretim arasında duran insan onayı. Sonraki ders aynı hattı iki kipte koşturur ve o onayın
yakaladığı gerçek kusuru, beklettiği süreyle birlikte sayar.
