---
title: 'Kod Küçültme ve Karartma'
source: 'https://academia.sh/tr/kurslar/android-uretim/kod-kucultme-ve-karartma'
course: 'Test, Başarım ve Dağıtım'
language: tr
updated: '2026-10-21T05:02:43+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Küçültme, karartma ve koruma kuralını ve eşleme dosyasının görevini tanımlar'
    - 'Erişilebilirlik çözümlemesinin kökten ulaşılabilen sınıfları koruyup geri kalanını attığını modelden ölçer'
    - 'Karartmanın sınıf adlarını değiştirdiğini, kalan sınıf sayısını değiştirmediğini gösterir'
    - 'Yansımayla erişilen paket için yazılan geniş bir koruma kuralının küçültmeyi nasıl boşa çıkardığını ayırt eder'
---

# Kod Küçültme ve Karartma

Yayımlanan uygulama paketinden kullanılmayan sınıflar ayıklanır ve kalan sınıfların adları kısaltılır. Ders küçültmeden sonra kalan sınıf sayısının bütçeyi kaç modülde aştığını, karartmanın bu sayıyı değiştirmediğini ve geniş bir koruma kuralının küçültmeyi boşa çıkardığını ölçer.

Önceki derste bir değişikliğin işi yavaşlatıp yavaşlatmadığını ölçmenin yolunu kurduk. Bu ders, ölçülen şeyin kullanıcıya ulaşan pakete nasıl girdiğine bakıyor. Yayımlanan uygulama, geliştirme sırasında yazılan her sınıfı taşımak zorunda değildir. Bir kitaplığın yüzlerce sınıfından uygulama yalnız birkaçını çağırıyorsa, çağrılmayan sınıflar pakete girdiğinde yalnız yer kaplar; indirmeyi büyütür ve yükleme işini artırır.

Pakete girecek kodu azaltmak iki ayrı işlemdir. **Küçültme** (shrinking), uygulamanın giriş noktalarından ulaşılamayan sınıfları ve üyeleri pakete koymaz. **Karartma** (obfuscation), kalan sınıfların ve üyelerin adlarını kısa, anlamsız adlarla değiştirir. İkisi de derleme sırasında, yayın paketini üreten adımda çalışır; geliştirme sırasında yazılan kaynak koda dokunmaz. Küçültücü bir kodu kaldırmadan önce o koda erişilebildiğini ya da erişilemediğini çözümler ve çözümleme yansımayla erişilen kodu göremez. Bu yüzden geliştirici, atılmaması gereken sınıfları bir **koruma kuralıyla** (keep rule) bildirir.

Ölçü yine kursun ölçüsüdür: küçültmeden sonra bir modülde kalan sınıf sayısı, bütçeyi kaç modülde aşıyor.

```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 }}")
    }
}
```

`Budget` ölçümün adını ve sınırını taşır, `Overrun` aşan örnekleri sayıp dört aşımdan birini verir. Bu dosya kursun her dersinde aynıdır.

## Erişilebilirlik Çözümlemesi Modeli

Modelde bir modül, sınıf numaralarından ve sınıflar arasındaki çağrı kenarlarından oluşur. Uygulamanın giriş noktası sıfır numaralı sınıftır; küçültücü o noktadan başlayarak çağrı kenarlarını izler ve ulaştığı sınıfları tutar. Ulaşamadığı sınıfları atar. Yansımayla erişilen bir sınıf çağrı kenarı taşımaz; küçültücü onu görmez ve yalnız koruma kuralı sayesinde kalır.

```kotlin
// Shrink.kt — kokten ulasilabilen siniflar kalir, digerleri atilir
class Module(val name: String, val edges: Map<Int, List<Int>>, val total: Int)

fun buildModule(name: String, total: Int, used: Int): Module =
    Module(name, (0 until used - 1).associateWith { listOf(it + 1) }, total)

fun sampleModules(): List<Module> {
    val sizes = listOf(12 to 4, 20 to 6, 14 to 3, 30 to 9, 16 to 5, 11 to 4, 24 to 7, 14 to 5)
    return sizes.mapIndexed { index, (total, used) -> buildModule("modul-${index + 1}", total, used) }
}

fun reachableFrom(module: Module, root: Int): Set<Int> {
    val seen = mutableSetOf(root)
    val queue = ArrayDeque(listOf(root))
    while (queue.isNotEmpty()) {
        val current = queue.removeFirst()
        for (next in module.edges[current].orEmpty()) {
            if (seen.add(next)) queue.addLast(next)
        }
    }
    return seen
}

fun keptClasses(module: Module, rule: String): Int = when (rule) {
    "kucult", "karart" -> reachableFrom(module, 0).size
    "dar" -> (reachableFrom(module, 0) + (module.total - 1)).size
    else -> module.total
}

fun mappingLines(module: Module): List<String> =
    reachableFrom(module, 0).sorted().map { "C$it -> ${'a' + it}" }
```

`sampleModules` sekiz modül kurar; her modül `total` sınıf taşır ve bunların `used` kadarı giriş noktasından ulaşılabilir. `reachableFrom` ulaşılabilen sınıfları bulur. `keptClasses` bir modülde kalan sınıf sayısını kurala göre verir: `kucult` yalnız ulaşılabilenleri tutar, `karart` aynı sayıyı verir, `dar` ulaşılabilenlere yansımayla erişilen tek bir sınıfı ekler, öteki kurallar modülün tüm sınıflarını tutar. `mappingLines` karartmanın yazdığı adların karşılıklarını döndürür. Önce küçültmeyi açık bırakıp, kalan sınıf sayısını on sınıflık bütçeyle ölçelim.

```kotlin
// Main.kt — kucultmeden sonra kalan sinif sayisi modul basina olculur
fun main() {
    val modules = sampleModules()
    val samples = modules.map { keptClasses(it, "kucult") }
    println("kalan: $samples")

    val overrun = Overrun("kucult", Budget("sinif", 10), samples)
    overrun.line()
    countOverruns(listOf(overrun))
}
```

```text
kalan: [4, 6, 3, 9, 5, 4, 7, 5]
kucult: butce=sinif<=10 | asan=0/8 -> icinde
icinde: 1
azinda: 0
cogunda: 0
hepsinde: 0
```

Hiçbir modülde kalan sınıf sayısı ona çıkmadı: **içinde**. Kural, bu kodun üretime çıkıp çıkmadığını değil, küçültmeden sonra kalan işin bütçeyi kaç örnekte aştığını sayar.

**BA1.** Modelde sınıf sayısı bir paket boyutunun yerine kullanılır ve her sınıf aynı ağırlıktadır. Gerçek bir sınıfın boyutu birkaç satırdan binlerce satıra kadar değişir; model sınıfları sayar; her birinin ne kadar yer tuttuğunu kurmaz ve paketin ne kadar küçüldüğünü bir oran olarak söylemez. Küçültmeden sonra kalan sayının bütçeye göre doğru olup olmadığını model değil, ürün kararı belirler.

## Küçültme Kapalıyken

Küçültmenin kazancını görmek için aynı sekiz modülü küçültme olmadan ölçelim: bu durumda her modül tüm sınıflarını taşır.

```kotlin
// Main.kt — kucultme kapaliyken ve acikken ayni butce
fun main() {
    val modules = sampleModules()
    val closed = modules.map { keptClasses(it, "kapali") }
    val shrunk = modules.map { keptClasses(it, "kucult") }
    println("kapali: $closed")
    println("kucult: $shrunk")

    val overruns = listOf(
        Overrun("kapali", Budget("sinif", 10), closed),
        Overrun("kucult", Budget("sinif", 10), shrunk),
    )
    overruns.forEach { it.line() }
    countOverruns(overruns)
}
```

```text
kapali: [12, 20, 14, 30, 16, 11, 24, 14]
kucult: [4, 6, 3, 9, 5, 4, 7, 5]
kapali: butce=sinif<=10 | asan=8/8 -> hepsinde
kucult: butce=sinif<=10 | asan=0/8 -> icinde
icinde: 1
azinda: 0
cogunda: 0
hepsinde: 1
```

Küçültme kapalıyken her modül ona yakın ya da onu aşan sayıda sınıf taşıdı: **hepsinde**. Küçültme açıkken sekizi de bütçenin içinde kaldı. Aradaki fark, kodun kendisinin değişmesi değil, pakete girecek kodun azalmasıdır.

Platformda bu karar derleme betiğinde verilir. Aşağıdaki parça yayın derlemesi için küçültmeyi açar ve kural dosyasını bağlar.

```kotlin
// Platform kodu: yayin derlemesinde kucultmeyi acma
android {
    buildTypes {
        release {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro",
            )
        }
    }
}
```

Bu parça platform kodudur ve burada çıktı, boyut ya da süre iddia etmiyoruz. Model, bu ölçümün kuralını kurar; model aracın kendisi değildir. Küçültücünün gerçek bir uygulamada hangi sınıfları ulaşılabilir saydığı, modelin göstermediği bilgidir ve derleme sonunda üretilen raporlardan okunur.

## Karartma Sayıyı Değiştirmez

Karartma sınıfların adlarını kısaltır. Kalan sınıf sayısını değiştirmez, çünkü hiçbir sınıfı atmaz; yalnız adını değiştirir. Ayrıca özgün adlar ile kısa adlar arasındaki karşılıklar bir **eşleme dosyasına** (mapping file) yazılır. Bu dosya olmadan çökme izlerindeki kısa adlar özgün adlara çevrilemez.

```kotlin
// Main.kt — karartma adlari degistirir, sinif sayisini degistirmez
fun main() {
    val modules = sampleModules()
    println("eslesme:")
    mappingLines(modules[0]).forEach { println("  $it") }

    val overruns = listOf("kucult", "karart").map { rule ->
        Overrun(rule, Budget("sinif", 10), modules.map { keptClasses(it, rule) })
    }
    overruns.forEach { it.line() }
    countOverruns(overruns)
}
```

```text
eslesme:
  C0 -> a
  C1 -> b
  C2 -> c
  C3 -> d
kucult: butce=sinif<=10 | asan=0/8 -> icinde
karart: butce=sinif<=10 | asan=0/8 -> icinde
icinde: 2
azinda: 0
cogunda: 0
hepsinde: 0
```

İlk modülün dört sınıfı `a`'dan `d`'ye kısaltıldı ve iki satır aynı aşımı verdi. Karartma tek başına bütçeyi etkilemez; ayrı bir amaca hizmet eder, yani adları okunmaz kılar. Eşleme dosyasının kaybolması ise kısa adların çözülememesine yol açar. Bu dersin ölçümü yalnız sınıf sayısına baktığı için eşleme dosyasının varlığını ya da yokluğunu görmez.

**BA2.** Modelde karartma adı tek harfle değiştirir ve çakışma olmaz. Gerçek karartıcı çok sayıda sınıfı iç içe ad uzayında kısaltır, bazı adları (örneğin yansımayla aranan adları) korur ve eşleme dosyasını her derlemede yeniden üretir. Önceki bir derlemenin eşleme dosyası sonraki derlemenin adlarını çözmez.

## Küçültmenin Neyi Çözdüğü

Küçültmenin amacı, pakete girmeyen kodun maliyetini sıfırlamaktır. Kod pakete girmezse indirilmez, yüklenmez ve yüklenme sırasında taranmaz. Ancak bu kazanç yalnız ulaşılamayan kodda geçerlidir. Giriş noktasından ulaşılabilen bir sınıf, kullanıcı hiç çağırmasa bile pakette kalır; çünkü küçültücü, çalışma zamanındaki kullanımı değil, kodun yapısal olarak ulaşılabilirliğini çözümler.

Bu yüzden küçültme sonrası kalan sayı bir alt sınır değil, bir üst tahmindir. Modelde ulaşılabilirlik bir zincirdir: her sınıf bir sonrakini çağırır. Gerçek uygulamalarda çağrı grafiği çok daha dallıdır ve küçültücünün bırakacağı sınıf sayısı, grafiğin biçimine göre değişir.

Koruma kuralının bir maliyeti vardır ve bu maliyet yazan kişinin gözünden kaçar. Her kural, küçültücüye "bunu at" yerine "bunu tut" der. Bir kuralı genişletmek kolaydır, çünkü hata yalnız çalışma zamanında görünür: eksik bir kural uygulamayı çökertir, fazla bir kural yalnız paketi büyütür. Bu asimetri yüzünden kurallar zamanla genişler ve küçültmenin kazancı sessizce erir. Bu dersin sayısal ölçümü tam bu erimeyi bir bütçeyle görünür kılar.

Dar bir kural yazmak, hangi sınıfa neden yansımayla erişildiğini bilmeyi gerektirir. Bu bilgi çoğu zaman bir kitaplığın belgesinde ya da kendi kodunuzda bir ad dizisinin çalışma zamanında sınıfa çevrildiği satırdadır. Kuralın yanına gerekçesini yazmak, bir sonraki kişinin kuralı güvenle daralttığı ya da kaldırdığı bir iz bırakır.

Yayın derlemesini sınamak da ayrı bir iştir. Küçültme çoğunlukla yalnız yayın derlemesinde açıldığından, geliştirme sırasında çalışan bir uygulama küçültmeden sonra çökebilir. Bu yüzden küçültülmüş derleme, en az bir kez uçtan uca çalıştırılır ve yansımayla erişilen yollar özellikle gezilir. Çökmeyi önceden yakalamak, koruma kuralını gerekçesiyle birlikte yazmaktan daha ucuza gelir.

Küçültmenin sonucu uygulama güncellendikçe değişir. Yeni bir kitaplık eklemek ulaşılabilen sınıf sayısını artırır, kullanılmayan bir özelliği kaldırmak azaltır. Bu yüzden kalan sınıf sayısı bir kez ölçülüp unutulmaz; her yayın derlemesinde aynı bütçeyle yeniden sayılır ve bütçeyi aşan modül, hangi değişiklikle büyüdüğü sorulacak bir gerileme olarak ele alınır. Bu dersin sekiz modülü bu izlemenin tek bir anının görüntüsüdür.

Eşleme dosyasının saklanması bir süreç sorusudur. Dosya derlemeyle birlikte üretilir ve yayımlanan paketle eşleşmesi gerekir; yanlış derlemenin dosyası kısa adları yanlış özgün adlara çevirir ve çökme incelemesini yanıltır. Bu yüzden dosya, paket yayımlandığı anda sürüm numarasıyla birlikte arşivlenir.

## Yaygın Yanılgılar

**"Karartma paketi küçültür."** Karartma adları kısaltır; bu, adlar uzun olduğunda yer kazandırabilir ama sınıf sayısını azaltmaz. Dersin karartma ölçümünde iki satır aynı aşımı verdi.

**"Küçültme kodu güvenli kılar."** Karartma, kodu okumayı zorlaştırır; ama koruma sağlamaz. Kısa adlar kodu çözmeyi engellemez, yalnız yavaşlatır. Gizli tutulması gereken bir bilgi pakete hiç girmemelidir.

**"Koruma kuralı yazınca sorun çözülür."** Koruma kuralı yansımayla erişilen sınıfı kurtarır, ama kuralın genişliği maliyeti belirler. Geniş bir kural bütün paketi tutar ve küçültmenin kazancını siler. Dersin sınırlayıcı ölçümü bunu gösterdi.

**"Eşleme dosyası isteğe bağlıdır."** Eşleme dosyası olmadan çökme izlerindeki kısa adlar özgün adlara çevrilemez. Her yayın derlemesinin eşleme dosyası, o derlemeyle birlikte saklanır.

## Bütçe ve Kuralın Gördüğü

Sınıf sayısı bütçesi, bir modülün küçültmeden sonra taşıyabileceği en çok sınıfı söyler. Bütçeyi hesaplamanın tek bir doğru yolu yoktur: bazı projeler önceki sürümün ortalamasını, bazıları paketin toplam sınırından geriye doğru bir dağılımı kullanır. Kural bütçenin nereden geldiğini bilmez; yalnız onu örneklere uygular.

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

Küçültücü yansımayla erişilen kodu göremez. Yansımayla erişilen tek bir sınıf varsa o sınıfı korumak gerekir. Bunu iki biçimde yapabiliriz. Dar kural yalnız o sınıfı korur. Geniş kural ise o sınıfın paketindeki her şeyi korur. Platformda kural dosyasındaki karşılıkları şöyledir.

```proguard
# dar: yalniz yansimayla erisilen sinif korunur
-keep class com.example.plugin.PluginEntry { *; }

# genis: paketteki her sinif korunur
-keep class com.example.plugin.** { *; }
```

Bu kural dosyası çalıştırılmaz ve çıktısı yoktur; iki satır, aynı yansıma sorusuna iki farklı cevap verir. Aynı iki cevabı modelde ölçelim.

```kotlin
// Main.kt — dar ve genis koruma kurali ayni butceyle olculur
fun main() {
    val modules = sampleModules()
    val narrow = modules.map { keptClasses(it, "dar") }
    val broad = modules.map { keptClasses(it, "genis") }
    println("dar: $narrow")
    println("genis: $broad")

    val overruns = listOf(
        Overrun("dar", Budget("sinif", 10), narrow),
        Overrun("genis", Budget("sinif", 10), broad),
    )
    overruns.forEach { it.line() }
    countOverruns(overruns)
}
```

```text
dar: [5, 7, 4, 10, 6, 5, 8, 6]
genis: [12, 20, 14, 30, 16, 11, 24, 14]
dar: butce=sinif<=10 | asan=0/8 -> icinde
genis: butce=sinif<=10 | asan=8/8 -> hepsinde
icinde: 1
azinda: 0
cogunda: 0
hepsinde: 1
```

Dar kural ulaşılabilen sınıflara yalnız yansımayla erişilen sınıfı ekledi ve **içinde** kaldı; dördüncü modül tam ona, sınıra eşit olduğu için aşım sayılmadı. Geniş kural ise her modülde kullanılmayan sınıfları da tuttu ve küçültme kapalıyken ölçülen sayılarla aynı değerleri verdi: **hepsinde**. Geniş kuralın küçültmeyi nasıl boşa çıkardığı bu iki satırdan okunur: kuralın yazılması küçültücüyü kapatmadı, ama kapalı olduğu durumla aynı sayıyı doğurdu.

Kural, aşımın sayısını görür, nedenini görmez. Hepsinde çıkan satırdan kalan sınıfların neden kaldığı okunamaz; bunun için hangi koruma kuralının hangi sınıfı tuttuğu, küçültücünün derleme sonunda yazdığı raporlarda aranır. Karartma da burada hiçbir şeyi düzeltmez: adlar kısalsa da sayı aynı kalır.

**BA3.** Modelde geniş kural paketteki her sınıfı tutar ve paket, modülle aynıdır. Gerçek bir koruma kuralı yalnız belirli üyeleri ya da yalnız belirli ad kalıplarını koruyabilir; bu derste yazılan iki kural en dar ve en geniş uçları gösterir. Bunların arasında kalan kuralların sonucu, ölçüm yapılmadan modelden çıkarılamaz.

## Özet

- Bu dersin aşımı: içinde. Küçültme açıkken sekiz modülün hiçbirinde kalan sınıf sayısı on sınıflık bütçeyi aşmadı.
- Küçültme, giriş noktasından ulaşılamayan sınıfları atar. Karartma yalnız adları kısaltır; kalan sınıf sayısı değişmez ve eşleme dosyası kısa adları geri çevirmek için saklanır.
- Küçültme kapalıyken aynı sekiz modül bütçeyi hepsinde aştı. Fark kodun değişmesinden değil, pakete girecek kodun azalmasından geldi.
- Dar koruma kuralı içinde kaldı. Yansımayla erişilen paketi kapsayan geniş koruma kuralı, her modülde kullanılmayan sınıfları da tuttu ve sonuç hepsinde çıktı.
- Kural aşımın sayısını görür, nedenini görmez. Kalan sınıfların hangi kuralla tutulduğu küçültücünün raporlarından okunur.

## Sonraki Adım

Kod küçültülüp karartıldıktan sonra uygulama bir pakete dönüşür ve bu paketin birden çok biçimi vardır: hepsini tek dosyada taşıyan biçim ile aygıta özel parçalar üreten biçim. Sıradaki ders, `Uygulama Paketi Biçimleri`, bu biçimlerin neyi nasıl dağıttığını ve aygıt başına indirilen parça sayısının bütçeyi kaç aygıt türünde aştığını ölçer.
