---
title: 'Aşamalı Yayın'
source: 'https://academia.sh/tr/kurslar/android-uretim/asamali-yayin'
course: 'Test, Başarım ve Dağıtım'
language: tr
updated: '2026-10-21T05:02:42+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Aşamalı yayının, geri alma ve durdurma kararlarının görevini ayırt eder'
    - 'Her aşamadaki çökme sayısını aynı büyüklükteki kullanıcı kümesi üzerinden ölçer'
    - 'Hata eşiğini ekibin kararı olarak koyar ve aşamaların aşımını sınıflar'
    - 'Durdurmanın bir kategori değil, aşımın sonucunda verilen bir karar olduğunu açıklar'
---

# Aşamalı Yayın

Yayını kullanıcı kümesine aşama aşama genişletir ve her aşamada çökme sayısını hata eşiğiyle karşılaştırır. Aşım görülen aşamada durdurmanın sonraki aşamaları örnek toplamayı bıraktığını ve örnek sayısının neden en az dört olması gerektiğini gösterir.

Önceki ders bir derlemenin kanallardan nasıl geçtiğini ölçtü. Üretim kanalına çıkan derleme kullanıcıların kendi aygıtlarına iner; ne kadar iyi sınanmış olursa olsun bir kısım hata ancak gerçek kullanımda görünür. Aşamalı yayın (staged rollout) bu hatanın bedelini sınırlar: derleme önce küçük bir kullanıcı kümesine açılır, sorun görülmezse sonraki aşamada daha geniş bir kümeye genişler.

Her aşamada ekip yayını izler. İzleme penceresi (monitoring window) bir aşamanın açık kaldığı ve çökmelerin sayıldığı dönemdir. Çökme sayısı hata eşiğini (error threshold) aşarsa ekibin elinde iki karar vardır: yayını durdurmak (halt), yani sonraki aşamaya geçmemek ve sürümü yeni kullanıcılara sunmayı kesmek, ama sürümü almış aygıtlar eski sürüme dönmez; ya da geri almak (rollback), yani önceki sürümü daha yüksek bir sürüm koduyla yeniden yayımlamak. Aşamaların kaçıncı olduğu bu derste sırayla anılır; her ekibin kendi aşama düzeni vardır.

Kursun sorusu burada da aynıdır: **bu ölçüm bütçesini kaç örnekte aşıyor?** Ölçüm bir aşamada, aynı büyüklükteki bir kullanıcı kümesinden gelen çökme sayısıdır (`crashCount`), bütçe hata eşiğidir, her aşama bir örnektir.

```kotlin
// Overrun.kt — bir olcumun butcesini kac ornekte astigini siniflandirir
class Budget(val name: String, val limit: Int)

class Overrun(val label: String, val budget: Budget, samples: List<Int>) {
    val total: Int = samples.size
    val over: Int = samples.count { it > budget.limit }
    val verdict: String = when {
        over == 0 -> "icinde"
        over == total -> "hepsinde"
        over * 2 < total -> "azinda"
        else -> "cogunda"
    }

    fun line() {
        println("$label: butce=${budget.name}<=${budget.limit} | asan=$over/$total -> $verdict")
    }
}

fun countOverruns(overruns: List<Overrun>) {
    for (name in listOf("icinde", "azinda", "cogunda", "hepsinde")) {
        println("$name: ${overruns.count { it.verdict == name }}")
    }
}
```

`Overrun` sınırı kesin aşan örnekleri sayar ve yaygınlığı dört adla verir: içinde, azında, çoğunda, hepsinde. Kural kaç aşamanın aştığını görür; bir aşamanın bir mi yoksa on çökme fazla mı taşıdığı aşan her örnek için aynıdır.

## Çökmeyi Toplayan Kod

Uygulama bir çökme olduğunda sistem varsayılan bir işleyiciye gelir; ekipler çökmeyi kaydetmek için bu işleyiciyi kendi koduna yönlendirir.

```kotlin
// Platform kodu: CrashRecorder
Thread.setDefaultUncaughtExceptionHandler { thread, error ->
    recordCrash(thread.name, error)
}
```

Bu parça bir platform çağrısıdır; çıktı iddia etmiyoruz. Model, bu ölçümün kuralını kurar; model aracın kendisi değildir. Modelde çökme, bir oturumun sonucudur: oturum ya sağlam biter ya da çöker.

İşleyici sistemin çökme yolunu değiştirmez; yalnız çökmenin kaydedilmesini ekibin koduna bağlar. İşleyici kayıt aldıktan sonra uygulama yine sonlanır. Bu ayrım ölçüm için önemlidir: çökme sayısı, kayıt edilebilen çökmelerin sayısıdır. Kaydı hiç alınamayan çökme ya da hiç çökmeyen ama bozuk çalışan oturum sayıya girmez. Modelde oturum ya `cokme` ya `saglam` olduğundan bu iki eksik de modelin dışında kalır.

## Aşamaları Modellemek

Yardımcı dosya her aşamayı sabit sayıda oturumdan oluşan bir küme olarak kurar. Aşamalar aynı büyüklükte olduğundan çökme sayıları doğrudan karşılaştırılabilir; aşamaların büyüklüğü farklı olsaydı sayı yanıltırdı.

```kotlin
// RolloutModel.kt — asamali yayini ve asama basina cokme sayisini modeller
class Stage(val label: String, val outcomes: List<String>)

fun crashCount(stage: Stage): Int = stage.outcomes.count { it == "cokme" }

fun stageNames(): List<String> = listOf(
    "ilk-asama", "ikinci-asama", "ucuncu-asama",
    "dorduncu-asama", "besinci-asama", "son-asama",
)

fun rolloutStages(crashes: List<Int>): List<Stage> =
    stageNames().zip(crashes).map { (name, crashed) ->
        Stage(name, List(10) { if (it < crashed) "cokme" else "saglam" })
    }

fun releaseA(): List<Int> = listOf(1, 1, 2, 5, 3, 4)

fun decide(count: Int, limit: Int): String = if (count > limit) "durdur" else "devam"

fun firstOverIndex(counts: List<Int>, limit: Int): Int {
    val index = counts.indexOfFirst { it > limit }
    return if (index == -1) counts.lastIndex else index
}
```

Her aşama on oturumdan kurulur; `releaseA` aşama başına çökme sayısını verir. Altı aşama, altı örnek demektir.

## Aşamaların Ölçümü

Hata eşiğini üç çökme olarak koyalım: ekip bir aşamada üçten fazla çökmeyi kabul etmiyor.

```kotlin
// Main.kt — her asamadaki cokme sayisi olculur, hata esigi uc cokme
fun main() {
    val stages = rolloutStages(releaseA())
    val samples = stages.map { crashCount(it) }
    for (stage in stages) {
        println("${stage.label}: cokme=${crashCount(stage)}/${stage.outcomes.size}")
    }
    println("cokme: $samples")
    Overrun("asama-cokmesi", Budget("cokme", 3), samples).line()
}
```

```text
ilk-asama: cokme=1/10
ikinci-asama: cokme=1/10
ucuncu-asama: cokme=2/10
dorduncu-asama: cokme=5/10
besinci-asama: cokme=3/10
son-asama: cokme=4/10
cokme: [1, 1, 2, 5, 3, 4]
asama-cokmesi: butce=cokme<=3 | asan=2/6 -> azinda
```

İki aşama üç çökmeyi aştı: dördüncü aşama beş, son aşama dört çökme taşıdı. Beşinci aşama üçte durdu ve sınıra eşit olduğu için aşım sayılmadı. Altı aşamanın üçten azı aştığı için yanıt **azında**. Bu, dersin aşımıdır.

**DA3.** Model her aşamayı aynı büyüklükte on oturumdan kurar ve çökmeyi oturum başına bir sonuç sayar. Gerçekte aşamalar farklı büyüklüktedir ve bir kullanıcının çok oturumu bir çökmeyi tekrarlayabilir; model ikisini de görmez. Çökme sayıları elle yazılmış girdilerdir, bir ölçüm değildir.

## Eşik Bir Karardır

Aynı altı aşamayı dört ayrı eşikle ölçelim. Eşik değişince aşım değişir; bu çelişki değil, ölçünün kendisidir.

```kotlin
// Main.kt — ayni alti asama dort ayri esikle dort ayri asim verir
fun main() {
    val samples = rolloutStages(releaseA()).map { crashCount(it) }
    val loose = Overrun("bes-cokme", Budget("cokme", 5), samples)
    val normal = Overrun("uc-cokme", Budget("cokme", 3), samples)
    val strict = Overrun("bir-cokme", Budget("cokme", 1), samples)
    val zero = Overrun("sifir-cokme", Budget("cokme", 0), samples)
    listOf(loose, normal, strict, zero).forEach { it.line() }
    countOverruns(listOf(loose, normal, strict, zero))
}
```

```text
bes-cokme: butce=cokme<=5 | asan=0/6 -> icinde
uc-cokme: butce=cokme<=3 | asan=2/6 -> azinda
bir-cokme: butce=cokme<=1 | asan=4/6 -> cogunda
sifir-cokme: butce=cokme<=0 | asan=6/6 -> hepsinde
icinde: 1
azinda: 1
cogunda: 1
hepsinde: 1
```

Dört eşik dört kategori verdi. Beş çökmeye izin veren eşik hiçbir aşamayı aşım saymadı. Sıfır çökmeye izin veren eşik ise altı aşamanın altısını aştı, çünkü her aşamada en az bir çökme vardı. Eşiğin hangi değerde olması gerektiği bir ürün kararıdır; model karar vermez, kararın aşımı nasıl değiştirdiğini gösterir.

## Aşım Bir Karar Değildir

Burada bir ayrım yapmak gerekir. Durdurma, aşımın dört kategorisinden biri değildir; aşımın sonucunda verilen bir karardır. Kural yalnız sayıyı görür ve içinde, azında, çoğunda ya da hepsinde der. Yayını durdurmak ise ekibin, aşımı gördükten sonra aldığı karardır: bir aşamada eşik aşıldığında sonraki aşamaya geçilmez.

Bu yüzden karar ayrı bir işlevdedir. `Overrun` hiçbir noktada "durdur" demez; `decide` her aşama için ayrıca karar verir.

```kotlin
// Main.kt — kategori ile karar ayri islevlerdir
fun main() {
    val limit = 3
    val stages = rolloutStages(releaseA())
    val samples = stages.map { crashCount(it) }
    val overrun = Overrun("asama-cokmesi", Budget("cokme", limit), samples)
    overrun.line()
    for (stage in stages) {
        println("${stage.label}: karar=${decide(crashCount(stage), limit)}")
    }
    println("kategori: ${overrun.verdict}")
}
```

```text
asama-cokmesi: butce=cokme<=3 | asan=2/6 -> azinda
ilk-asama: karar=devam
ikinci-asama: karar=devam
ucuncu-asama: karar=devam
dorduncu-asama: karar=durdur
besinci-asama: karar=devam
son-asama: karar=durdur
kategori: azinda
```

Kategori **azında**; karar ise dördüncü aşamada durdurmaktır. İkisi farklı sorulara yanıt verir: kategori "aşım ne kadar yaygın?", karar "ne yapacağız?". İlk aşım görüldüğü anda yayın zaten durdurulacaksa, beşinci ve son aşamalar hiç açılmaz ve onların sayıları ölçülmez.

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

Bu bölüm bir kategorinin ne zaman okunabilir olmadığını sorar. Durdurma, sonraki aşamaların örneklenmesini keser. Örnek sayısı azalır ve kategori değişebilir. Üç ayrı yayın ele alalım: ilkinde aşım dördüncü aşamada, ikincisinde ikinci aşamada, üçüncüsünde ilk aşamada görünür. Her biri tam altı aşama ile ve ilk aşımda durdurulmuş hâliyle ölçülür.

```kotlin
// Main.kt — ilk asimda durdurmak ornek sayisini azaltir, kategori degisebilir
fun main() {
    val limit = 3
    val releases = listOf(
        "yayin-a" to releaseA(),
        "yayin-b" to listOf(1, 4, 1, 1, 2, 1),
        "yayin-c" to listOf(4, 1, 1, 1, 2, 1),
    )
    for ((name, crashes) in releases) {
        val full = rolloutStages(crashes).map { crashCount(it) }
        val halted = full.take(firstOverIndex(full, limit) + 1)
        Overrun("$name-tam", Budget("cokme", limit), full).line()
        Overrun("$name-durdurulmus", Budget("cokme", limit), halted).line()
    }
}
```

```text
yayin-a-tam: butce=cokme<=3 | asan=2/6 -> azinda
yayin-a-durdurulmus: butce=cokme<=3 | asan=1/4 -> azinda
yayin-b-tam: butce=cokme<=3 | asan=1/6 -> azinda
yayin-b-durdurulmus: butce=cokme<=3 | asan=1/2 -> cogunda
yayin-c-tam: butce=cokme<=3 | asan=1/6 -> azinda
yayin-c-durdurulmus: butce=cokme<=3 | asan=1/1 -> hepsinde
```

İlk yayında dört aşama örneklendi ve kategori **azında** kaldı. İkinci yayında yalnız iki örnek kaldı ve aynı yayın tam ölçümde azında iken durdurulmuş ölçümde **çoğunda** göründü. Üçüncü yayında tek örnek kaldı ve yanıt **hepsinde** oldu. Üç yayının da tam ölçümü azında; değişen yayının davranışı değil, ölçülen örnek sayısıdır.

Buradan örnek sayısı kuralı çıkar: bir ölçümden kategori çıkarmak için en az dört örnek gerekir. Dört örneğin altında kategori yalnız örnek toplamanın durdurulduğunu anlatır; yayının kendisini değil. İki örnekte bir aşım, örneklerin yarısıdır. Bu yüzden durdurulmuş bir yayının kategorisi okunmaz; kararın kendisi okunur.

**DA4.** Model durdurmanın hemen ilk aşımda yapıldığını varsayar. Gerçekte ekipler birkaç aşım bekleyebilir ya da aşımı bir insan değerlendirir; model bu gecikmeyi kurmaz. Model ayrıca durdurulan yayında sonraki aşamaların ne olacağını bilmez: durdurulan aşamalar sonradan açılırsa ölçüm yeniden başlar.

## Aşama Büyüklüğü

Üçüncü varsayımda aşamaların aynı büyüklükte olduğunu söyledik. Gerçek bir yayın böyle ilerlemez: ilk aşama küçüktür, son aşama kullanıcıların çoğunu kapsar. Büyüklük değişince ham çökme sayısı yanıltıcı olur, çünkü büyük bir aşamada aynı çökme oranı daha çok çökme demektir. Aşağıdaki ölçüm altı aşamayı büyüyen boyutlarla kurar ve iki ölçümü yan yana koyar: ham çökme sayısı ve bin oturumdaki çökme sayısı. İkincisi tamsayı bölmesiyle hesaplanır, yani yuvarlama aşağıya doğrudur. İki ölçümün bütçesi farklı birimdedir ve birbirinden türetilmemiştir; ham sayı için üç çökme, oran için bin oturumda elli çökme koyduk.

```kotlin
// Main.kt — buyuyen asamalarda ham cokme sayisi ile bin oturumdaki oran baska yanit verir
fun main() {
    val sizes = listOf(10, 20, 50, 100, 200, 500)
    val crashes = listOf(1, 1, 2, 5, 6, 40)
    val stages = stageNames().indices.map { index ->
        Stage(stageNames()[index], List(sizes[index]) { if (it < crashes[index]) "cokme" else "saglam" })
    }
    val counts = stages.map { crashCount(it) }
    val rates = stages.map { crashCount(it) * 1000 / it.outcomes.size }
    println("cokme: $counts")
    println("bin oturumda cokme: $rates")
    Overrun("ham-sayi", Budget("cokme", 3), counts).line()
    Overrun("oran", Budget("cokme-bin", 50), rates).line()
}
```

```text
cokme: [1, 1, 2, 5, 6, 40]
bin oturumda cokme: [100, 50, 40, 50, 30, 80]
ham-sayi: butce=cokme<=3 | asan=3/6 -> cogunda
oran: butce=cokme-bin<=50 | asan=2/6 -> azinda
```

Ham sayıda üç aşama bütçeyi aştı ve yanıt **çoğunda** oldu; oranda yalnız iki aşama aştı ve yanıt **azında** kaldı. Fark ilk ve son aşamada görünür. İlk aşamada tek çökme bin oturumda yüz çökme demektir ve ham sayıda hiç görünmez. Son aşamada kırk çökme ham sayıda çok görünür ama beş yüz oturuma bölününce bin oturumda seksen çökmedir. Dördüncü aşamanın beş çökmesi bin oturumda elli çökmeye denk gelir ve her iki ölçümde de bütçenin üstünde değildir.

Sonuç ölçüm seçiminin kategoriyi oynattığıdır. Hangi ölçümün doğru olduğuna kural karar vermez; ekip bütçeyi bir birimle koyar ve o birimde sayar.

**DA5.** Model bir oturumu bir çökme sonucu sayar ve oranı tamsayı bölmesiyle hesaplar. Gerçekte oranlar kesirlidir ve küçük aşamalarda tek bir çökmenin oranı çok oynak kılması istatistiksel bir güvensizlik taşır; model bu güvensizliği ölçmez. Boyutlar ve çökmeler girdidir.

## Yaygın Yanılgılar

**"Aşamalı yayın hatayı önler."** Önlemez; hatanın kaç kullanıcıyı etkilediğini sınırlar.

**"Çökme sayısı az ise yayın sağlıklıdır."** Sayı yalnız çöken oturumları sayar. Çökmeyen ama bozuk çalışan bir sürüm sayıda görünmez.

**"Küçük bir aşamadaki tek çökme önemsizdir."** Aşama küçükse tek çökme yüksek bir orandır; ham sayı bunu saklar. Yukarıdaki ilk aşama bin oturumda yüz çökmeye denk geldi.

**"Büyük aşamada çok çökme olması kötü bir işarettir."** Çok olması, büyük olmasının sonucu olabilir. Son aşamada kırk çökme bin oturumda seksene denk geldi ve bütçeyi aşan iki orandan yalnız biriydi.

**"Durdurma bir sonuçtur."** Durdurma kategorilerden biri değil, bir karardır. Aynı kategori iki ekipte iki ayrı kararla sonuçlanabilir.
**"Aşama sayısı artarsa yayın daha güvenli olur."** Her ek aşama çökmenin görüneceği bir pencere ekler, ama yayının tamamlanmasını da geciktirir. Aşama sayısı bir denge kararıdır; bu ders yalnız verilen aşamaların ölçümünü yaptı ve ideal sayıyı önermedi.

**"Oran ham sayıdan her zaman doğrudur."** Oran büyüklüğü hesaba katar ama küçük bir aşamada tek bir çökme bile oranı sıçratır. Yukarıdaki ilk aşamada bu görüldü. Oranın güvenilir olması için yeterli oturum gerekir; kaç oturumun yeterli olduğu bu derste verilmedi, çünkü ölçülmedi.

**"Çökme izlemesi yayın bitince biter."** Yayın bütün kullanıcılara ulaştıktan sonra da çökmeler gelir. Aşamalar yalnız hatanın erken yakalanma şansını artırır; izleme pencerelerinin kapanması, sürümün sorunsuz olduğunu söylemez.

**"Eşiği bir kez koymak yeter."** Eşik yayının büyüklüğüne ve kullanıcı sayısına göre yeniden düşünülür. Aynı sayıyı her aşamaya koymak, aşamalar büyüdükçe farklı bir anlam taşır; ham sayı eşiği büyük aşamada daha kolay aşar, oran eşiği ise büyüklükten bağımsızdır. Bu yüzden eşiğin hangi ölçüye bağlandığı, eşiğin kendisi kadar önemlidir. Eşik değiştiğinde önceki aşamaların sonuçları da yeniden yorumlanır.


## Özet

- Bu dersin aşımı: azında. Altı aşamadan ikisi üç çökmelik eşiği aştı; yarısından azı aştığı için yanıt azında.
- Aşamalı yayın hatanın bedelini sınırlar; aşım görüldüğünde ekip yayını durdurur ya da geri alır.
- Aynı altı aşama beş, üç, bir ve sıfır çökmelik eşiklerde içinde, azında, çoğunda ve hepsinde çıktı.
- Durdurma kategori değil, aşımın sonucunda verilen karardır; kategori azında, karar dördüncü aşamada durdurmaktır.
- Aşama büyüklükleri büyüyünce ham çökme sayısı çoğunda, bin oturumdaki oran azında çıktı; ölçüm seçimi kategoriyi oynatır.
- Durdurma sonrası kalan örnek dörtten az olduğunda kategori yayını değil örnek toplamanın durduğunu anlatır: iki örnekte çoğunda, tek örnekte hepsinde çıktı.

## Sonraki Adım

Yayın aşama aşama genişledi ve her aşama bir eşikle karşılaştırıldı. Ama yayına çıkmadan önce bir engel daha vardır: uygulama mağazasının inceleme kuralları. Sıradaki ders, `Mağaza İnceleme ve Politikalar`, uygulamanın beyan ettiği ama kullanmadığı izinleri sayar.
