İçeriğe geç
academia.sh

Ders 14 / 19

Sürüm Yönetimi

Sürüm kodu platformun karşılaştırdığı tamsayıdır, sürüm adı ise kullanıcıya gösterilen dizedir. Ders sekiz aygıtın kurulu sürüm kodundan son sürüme uzaklığını bütçeyle karşılaştırır ve sürüm adını değiştirmenin uzaklığı değiştirmediğini gösterir.

Yayın Güncelleme

Bu derste

  • Sürüm kodu ile sürüm adının görevlerini ve hangisinin karşılaştırma için kullanıldığını ayırır
  • Sürüm kodunun tekdüze artması gerektiğini ve düşürülemeyeceğini açıklar
  • Bir aygıtın kurulu sürüm kodundan son sürüme uzaklığını geride kalan sürüm sayısı olarak ölçer
  • Desteklenen en düşük sürümden bütçeyi türetir ve sekiz örneğin aşımını sınıflar
İçindekiler

Önceki ders derlemenin çıktısına baktı: bir yapılandırmaya kaç dosya iniyor. Bu ders çıktının kendisine bir kimlik vermeyi ele alıyor. Aygıtlar derlemelerin hepsini aynı anda almaz; bir kısmı son sürümde, bir kısmı eski bir sürümde kalır. Bu yüzden her derlemeye, hangisinin hangisinden yeni olduğunu söyleyen bir sayı gerekir. Soru yine kursun sorusudur: bu ölçüm bütçesini kaç örnekte aşıyor? Ölçüm bir aygıtın son sürümden kaç sürüm geride kaldığıdır, bütçe desteklenen geri sürüm sayısıdır, her aygıt bir örnektir.

// 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 olarak aşan örnekleri sayar ve aşım yaygınlığını dört adla verir: içinde, azında, çoğunda, hepsinde. Kural sayıyı görür; bir aygıtın bir sürüm mü yoksa on sürüm mü geride olduğu, aşan her örnek için aynı sayılır.

Sürüm Kodu ve Sürüm Adı

Android her uygulama sürümüne iki ayrı değer verir. Sürüm kodu (version code) bir tamsayıdır; platform ve uygulama mağazası iki sürümden hangisinin daha yeni olduğunu bu sayıya bakarak anlar. Sürüm adı (version name) ise kullanıcıya gösterilen dizedir ve platform onu karşılaştırmaz. 2.2.1 bir sürüm adıdır; platform için onun kendinden önceki sürümden yeni olup olmadığı hakkında hiçbir anlamı yoktur.

// Platform kodu: modül derleme betiği
android {
    defaultConfig {
        versionCode = 12
        versionName = "2.2.1"
    }
}

Bu parça platform kodudur; burada çıktı iddia etmiyoruz. Model, bu ölçümün kuralını kurar; model aracın kendisi değildir. Modelde bir yayın Release olarak iki alan taşır: kod ve ad. Karşılaştırmayı yalnız kod yapar.

Betikte iki değer yan yana durur ve ikisi de elle yazılmıştır, ama okurları farklıdır. versionCode makineye söyler: bu derleme öncekinden sonradır. versionName insana söyler: bu derleme hangi sürümdür. Bir ekip ikisini aynı anda artırabilir, yalnız birini artırabilir ya da kodu ad üzerinden hesaplayabilir; hangisinin yapıldığı, ders boyunca ölçeceğimiz uzaklığı doğrudan etkiler. Modelde kodu ve adı ayrı alanlara koymamızın nedeni budur: biri değişince öteki değişmek zorunda değildir.

İki kural sürüm kodunu yönetir. Birincisi tekdüze artıştır: her yeni yayının kodu bir öncekinden büyük olmalıdır. İkincisi kodun düşürülememesidir: platform, kurulu olandan düşük kodlu bir paketle güncellemeyi kabul etmez. Bu yüzden hatalı çıkan bir sürümü geri almak, eski kodu yeniden yayımlamak değildir; eski davranışı taşıyan ama daha yüksek kodlu yeni bir derleme yayımlamaktır.

Geride Kalan Sürüm Sayısı

Model yayın geçmişini, kurulu aygıtları ve bir uzaklık işlevini yardımcı dosyada toplar. isMonotonic kodların kesin biçimde arttığını denetler. versionsBehind bir aygıtın kurulu kodundan büyük kaç ayrı kod olduğunu sayar; aynı kodu taşıyan iki yayın tek sayılır, çünkü platform onları aynı sürüm sayar.

// VersionModel.kt — surum kodunu ve adini modeller, geride kalan surum sayisini hesaplar
class Release(val code: Int, val name: String)

class InstalledDevice(val label: String, val installedCode: Int)

fun isMonotonic(releases: List<Release>): Boolean =
    releases.zipWithNext().all { (previous, next) -> next.code > previous.code }

fun latestCode(releases: List<Release>): Int = releases.maxOf { it.code }

fun versionsBehind(releases: List<Release>, device: InstalledDevice): Int =
    releases.map { it.code }.distinct().count { it > device.installedCode }

fun releaseHistory(): List<Release> = listOf(
    Release(5, "1.8.0"),
    Release(6, "1.8.1"),
    Release(7, "1.9.0"),
    Release(8, "2.0.0"),
    Release(9, "2.0.1"),
    Release(10, "2.1.0"),
    Release(11, "2.2.0"),
    Release(12, "2.2.1"),
)

fun installedDevices(): List<InstalledDevice> = listOf(
    InstalledDevice("aygit-a", 12),
    InstalledDevice("aygit-b", 11),
    InstalledDevice("aygit-c", 10),
    InstalledDevice("aygit-d", 9),
    InstalledDevice("aygit-e", 8),
    InstalledDevice("aygit-f", 6),
    InstalledDevice("aygit-g", 5),
    InstalledDevice("aygit-h", 12),
)

Sekiz yayın sekiz farklı kod taşır ve sekiz aygıttan ikisi son sürümdedir. Bütçeyi bir kararın sonucu olarak türetelim: ekip, kurulu kodu 10’dan küçük olan aygıtları desteklemeyeceğine karar verdi. En düşük desteklenen sürüm kodu 10 ise ve son kod 12 ise desteklenen geri sürüm sayısı ikidir.

// Main.kt — en dusuk desteklenen surumden butce turetilir, sekiz aygit olculur
fun main() {
    val releases = releaseHistory()
    println("kodlar tekduze mu: ${isMonotonic(releases)}")
    val minSupported = 10
    val budget = Budget("surum", latestCode(releases) - minSupported)
    val samples = installedDevices().map { versionsBehind(releases, it) }
    println("geride kalan: $samples")
    Overrun("surum-uzakligi", budget, samples).line()
}
kodlar tekduze mu: true
geride kalan: [0, 1, 2, 3, 4, 6, 7, 0]
surum-uzakligi: butce=surum<=2 | asan=4/8 -> cogunda

Dört aygıt ikiden fazla sürüm geride: üç, dört, altı ve yedi. Sekizin yarısı sınırı aştığı için yanıt çoğunda; tam yarı çoğunda sayılır, azında değil. Bu, dersin aşımıdır. Kural aygıt-g’nin yedi, aygıt-d’nin üç sürüm geride olduğunu görmez; ikisi de aşan bir örnektir.

DA3. Model uzaklığı sürüm sayısıyla ölçer ve her sürümü eşit ağırlıkta görür. Gerçekte bir sürüm küçük bir düzeltme, bir başkası büyük bir değişiklik taşıyabilir; model bu farkı kurmaz. Ayrıca model aygıtların kurulu kodunu bir girdi olarak alır; gerçekte güncellemelerin ne zaman indiği kullanıcının ayarlarına ve ağa bağlıdır.

Bütçe Bir Karardır

En düşük desteklenen sürüm değişirse bütçe değişir, aynı sekiz aygıt başka bir aşım verir. Üç ayrı bütçe koyalım: ekip yedi sürüm geriyi destekliyor, dört sürüm geriyi destekliyor, iki sürüm geriyi destekliyor.

// Main.kt — ayni sekiz aygit uc ayri butceyle uc ayri asim verir
fun main() {
    val releases = releaseHistory()
    val samples = installedDevices().map { versionsBehind(releases, it) }
    val wide = Overrun("yedi-surum", Budget("surum", 7), samples)
    val middle = Overrun("dort-surum", Budget("surum", 4), samples)
    val narrow = Overrun("iki-surum", Budget("surum", 2), samples)
    listOf(wide, middle, narrow).forEach { it.line() }
    countOverruns(listOf(wide, middle, narrow))
}
yedi-surum: butce=surum<=7 | asan=0/8 -> icinde
dort-surum: butce=surum<=4 | asan=2/8 -> azinda
iki-surum: butce=surum<=2 | asan=4/8 -> cogunda
icinde: 1
azinda: 1
cogunda: 1
hepsinde: 0

Bütçe yediyken hiçbir aygıt sınırı kesin olarak aşmaz; yedi sürüm geride kalan aygıt sınıra eşittir, aşım değildir. Bütçe dörde inince yalnız altı ve yedi sürüm geride olanlar aşar. İkiye inince dört aygıt aşar. Dört yanıttan üçü aynı ölçümden çıktı; hepsinde hiç görünmedi, çünkü iki aygıt son sürümdedir ve sıfır, hiçbir geçerli bütçeyi aşmaz.

Bu üç sonuç bir çelişki değildir; ölçünün kendisidir. Desteklenen geri sürüm sayısı bir ürün kararıdır. Ekip eski sürümleri ne kadar süre desteklerse destek yükü o kadar artar; destekledikçe bütçe genişler ve aşım azalır. Ders bütçeyi dayatmaz, yalnız bütçenin aşımı nasıl belirlediğini gösterir.

Sürüm Adı Kuralı Etkilemez

Sürüm adı kullanıcıya aittir: bir sürüme 2.3.0 demek pazarlama ya da iletişim kararıdır. Platform ise yalnız kodu okur. Ekibin sürüm adını 2.3.0 yapıp kodu 12’de bıraktığını düşünelim; ikinci bir senaryoda aynı sürüm adı verilir ama kod 13’e artırılır.

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

Sınırlayıcı ölçüm, dersin sorusunu aşımın dört yanıtından birine bağlamadan önce hangi değişikliğin kategoriyi oynattığını sorar. Üç senaryoyu aynı sekiz aygıtla ve iki bütçesiyle ölçelim: yalnız ad değişti, hem ad hem kod değişti. İlk senaryoda yayın geçmişine kodu 12 olan ama adı 2.3.0 olan bir yayın ekleniyor.

// Main.kt — surum adi degisip kod artmazsa uzaklik ayni kalir
fun main() {
    val devices = installedDevices()
    val budget = Budget("surum", 2)

    val before = releaseHistory()
    val renamed = before + Release(12, "2.3.0")
    val bumped = before + Release(13, "2.3.0")

    val baseline = Overrun("ilk-hal", budget, devices.map { versionsBehind(before, it) })
    val nameOnly = Overrun("yalniz-ad", budget, devices.map { versionsBehind(renamed, it) })
    val nameAndCode = Overrun("ad-ve-kod", budget, devices.map { versionsBehind(bumped, it) })
    listOf(baseline, nameOnly, nameAndCode).forEach { it.line() }
    println("kodlar tekduze mu: ${isMonotonic(renamed)} ${isMonotonic(bumped)}")
}
ilk-hal: butce=surum<=2 | asan=4/8 -> cogunda
yalniz-ad: butce=surum<=2 | asan=4/8 -> cogunda
ad-ve-kod: butce=surum<=2 | asan=5/8 -> cogunda
kodlar tekduze mu: false true

Yalnız ad değişen senaryo ilk hâlle birebir aynı satırı verdi: aşan örnek sayısı dört, yanıt çoğunda. Kural sürüm adını görmez; versionsBehind yalnız kodları sayar ve kodlar değişmedi. Kod 13’e artınca aşan örnek sayısı beşe çıktı, yanıt yine çoğunda kaldı. Burada kuralın ikinci sınırı görünür: aşım sayısı arttı ama kategori sabit kaldı, çünkü kural sınırı aşan örnek sayısını dört yanıttan birine indirger.

Sonuç iki yönde okunur. Ad değişikliği kullanıcıya yeni bir sürüm olduğunu söyler ama bir aygıtı güncel yapmaz; güncelleme kararı kodla verilir. Kod artışı ise aynı sekiz aygıtı bir sürüm daha geride bırakır: son sürüm yeniden yayımlandığında herkes bir basamak geriye düşer. Her yayın bütçeyi bir yönde zorlar.

DA4. Model aynı kodlu ikinci yayını kabul eder ve onu mevcut koda sayar. Yayın konsolu gerçekte aynı sürüm koduyla ikinci bir yüklemeyi kabul etmez; model reddi kurmaz, çünkü ders kuralın sürüm adına bakmadığını göstermek istiyor. Bu senaryoda isMonotonic ilk durumda yanlış döner: bu dönüş reddin modeldeki karşılığıdır.

Kodu Adtan Türetmek

Birçok ekip kodu elle artırmaz; sürüm adından hesaplar. Yaygın bir biçim major, minor ve patch değerlerini ağırlıklarla toplamaktır. Bu kuralın sessiz bir tuzağı vardır: ağırlıklar dar seçilirse farklı adlar aynı koda iner. Tuzağı ölçelim. Girdi dokuz sürüm adıdır; bu kez listede 1.10.0 da var. Bütçe sıfırdır: aynı kodu paylaşan her yayın bir aşımdır. Her yayın için aynı kodu taşıyan öteki yayınların sayısı örnektir.

// Main.kt — addan kod uretiminde dar carpanlar farkli adlari ayni koda indirger
fun foldCode(name: String, minorWeight: Int, majorWeight: Int): Int {
    val (major, minor, patch) = name.split(".").map { it.toInt() }
    return major * majorWeight + minor * minorWeight + patch
}

fun collisions(names: List<String>, minorWeight: Int, majorWeight: Int): List<Int> {
    val codes = names.map { foldCode(it, minorWeight, majorWeight) }
    return codes.map { code -> codes.count { it == code } - 1 }
}

fun main() {
    val names = listOf("1.8.0", "1.8.1", "1.9.0", "1.10.0", "2.0.0", "2.0.1", "2.1.0", "2.2.0", "2.2.1")
    val budget = Budget("cakisma", 0)
    val narrow = collisions(names, 10, 100)
    val wide = collisions(names, 100, 10000)
    println("dar carpan: $narrow")
    println("genis carpan: $wide")
    Overrun("dar-carpan", budget, narrow).line()
    Overrun("genis-carpan", budget, wide).line()
}
dar carpan: [0, 0, 0, 1, 1, 0, 0, 0, 0]
genis carpan: [0, 0, 0, 0, 0, 0, 0, 0, 0]
dar-carpan: butce=cakisma<=0 | asan=2/9 -> azinda
genis-carpan: butce=cakisma<=0 | asan=0/9 -> icinde

Dar çarpanla 1.10.0 ve 2.0.0 ikisi de 200 üretir. Dokuz yayından ikisi çakıştığı için yanıt azında; geniş çarpanda çakışma yoktur ve yanıt içinde. Çakışma yalnız bir tablo kusuru değildir: iki yayın aynı kodu taşıyınca platform onları ayırt edemez, yani ikincisini güncelleme olarak göremez. Kural çakışmanın nedenini görmez, yalnız kaç yayının etkilendiğini sayar.

DA5. Model kodu üç alanlı bir ad ve iki ağırlıkla katlar. Gerçek ekipler derleme numarası ya da tarih gibi başka şemalar da kullanır; model bunları kurmaz ve hangi şemanın seçilmesi gerektiğini söylemez. Ölçülen şey yalnız çakışma sayısıdır.

Yaygın Yanılgılar

“Sürüm adı büyüdüyse sürüm yenidir.” Platform için yeni olan, kodu büyük olandır. Adı büyük kodu küçük bir paket, kurulu olandan eski sayılır.

“Sürüm kodu 2.2.1 gibi bir biçim taşır.” Sürüm kodu tamsayıdır. Noktalı bir gösterim sürüm adının işidir; kodu birden çok alana bölen ekipler bunu kendi kuralıyla, tek bir tamsayıya katlayarak yapar.

“Kodu addan türetmek her zaman güvenlidir.” Türetme tekdüzeliği korur gibi görünür ama ağırlıklar dar seçilirse çakışır. Yukarıdaki ölçümde 1.10.0 ve 2.0.0 aynı kodu verdi. Türetme kuralı değişmeden önce yayın geçmişindeki tüm adlarla denenmelidir.

“Yalnız sürüm adını değiştirmek bir güncelleme yayımlar.” Platform için güncelleme, kodu artan pakettir. Yalnız adı değişen paket aynı sürüm sayılır; bu derste aşan örnek sayısı dört kaldı.

“Geri alınan sürümün kodu geri alınır.” Kod düşürülemez. Geri alma, daha yüksek kodlu yeni bir derlemeyle yapılır; bu derse göre bütçe tek bir sürüm daha zorlanır. “Sürüm kodunu atlamak zararsızdır.” Kod yalnız artmalıdır; atlanan sayılar sıralamayı bozmaz ama boşa gider. Türetilmiş bir kodda atlama bir yan etki olarak da çıkar: ağırlıklar geniş seçilirse ardışık iki yayın arasındaki fark büyür. Ölçüm yalnız iki yayın arasındaki sıralamaya bakar; aradaki boşluğu görmez, bu yüzden boşluğun bir maliyeti olup olmadığına ekip kendi kuralıyla karar verir.

“Çakışma yalnız aynı kodu üretir.” Çakışmanın asıl zararı iki ayrı yayının platform için aynı yayın sayılmasıdır. İkinci paket birincinin üzerine kurulamaz, çünkü kodu daha büyük değildir. Bu yüzden çakışma bütçesi sıfırdır ve tek bir çakışma bile aşımdır.

Özet

  • Bu dersin aşımı: çoğunda. Sekiz aygıttan dördü, ikiden fazla sürüm geride kaldı: yarısı sınırı aştığı için yanıt azında değil, çoğunda.
  • Sürüm kodu platformun karşılaştırdığı tamsayıdır ve tekdüze artar; sürüm adı kullanıcıya gösterilen dizedir ve karşılaştırmaya girmez.
  • Bütçe en düşük desteklenen sürümden türedi: 10 ve 12 arasındaki fark ikiydi. Aynı sekiz aygıt yedi bütçesinde içinde, dört bütçesinde azında, iki bütçesinde çoğunda çıktı.
  • Yalnız sürüm adı değişince aşan örnek sayısı dört kaldı; kural sürüm adını görmez.
  • Kodu addan türeten bir kural dar ağırlıkla 1.10.0 ile 2.0.0 adlarını aynı koda indirdi: dokuz yayından iki aşım, yanıt azında; geniş ağırlıkta içinde.
  • Sürüm kodu 13’e artınca aşan örnek sayısı beşe çıktı ama yanıt çoğunda kaldı; kural sayıyı sayar, büyüklüğü görmez.

Sonraki Adım

Bu derste sürümün kimliği elle yazıldı: bir betik parçasında versionCode = 12. Gerçek bir ekipte bu sayıyı her değişiklikte birinin artırması ve her derlemeyi birinin üretmesi gerekir. Sıradaki ders, Sürekli Tümleştirme ve Teslimat, bir değişiklik geldiğinde kaç işin koştuğunu ölçer.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat