Kode ditulis sekali tetapi dibaca ratusan kali. Setiap developer yang membaca kode kita — termasuk diri kita sendiri tiga bulan ke depan — perlu memahami dengan cepat apa yang kode lakukan dan mengapa. Clean code bukan sekadar “kode rapi”; ia adalah kode yang dirancang untuk dibaca manusia terlebih dahulu, baru kemudian kompilator. Pada chapter ini kita akan membahas empat pilar clean code: penamaan bermakna, fungsi kecil, single responsibility, dan komentar yang menjelaskan mengapa. Kode demo akan membandingkan versi buruk dan versi clean untuk topik yang sama.

Empat Pilar Clean Code #

  • Meaningful names: nama variabel, fungsi, dan class harus mengungkapkan maksud.
  • Small functions: fungsi idealnya 5-15 baris, paling banyak satu level indentasi per logical block.
  • Single responsibility: setiap class dan fungsi melakukan satu hal.
  • Comments explain why: komentar menjelaskan keputusan, bukan mengulang kode.

Kode Sebelum dan Sesudah #

Program berikut membandingkan kode buruk (nama ambigu, fungsi panjang, komentar what) dengan versi clean (nama jelas, fungsi kecil, komentar why).

kotlin
// Demo clean code: meaningful names, small functions, single responsibility, comments explain why.

// === SEBELUM: nama buruk, fungsi panjang, komentar what ===
class Repo {
    fun p(items: List<Map<String, Any>>): Double {
        var t = 0.0
        for (i in items) {
            val p = i["price"] as Double
            val q = i["qty"] as Int
            t += p * q  // hitung total
        }
        return t  // kembalikan total
    }
}

// === SETELAH: clean code ===
data class ItemKeranjang(val harga: Double, val jumlah: Int)

class KeranjangBelanja {
    fun hitungTotal(items: List<ItemKeranjang>): Double {
        return items.sumOf { it.harga * it.jumlah }
    }
}

// === Single Responsibility: pisahkan perhitungan dari formatting ===
class PeringkasStruk {
    fun ringkas(items: List<ItemKeranjang>, total: Double): String {
        val daftarItem = items.joinToString("\n") { "- ${it.jumlah}x @ Rp${it.harga}" }
        return "$daftarItem\nTotal: Rp$total"
    }
}

// === Comments explain why, not what ===
class DiskonTahunan {
    // Diskon dinaikkan dari 5% menjadi 10% pada 2026 untuk kompetisi dengan platform lain
    // (lihat tiket PROJ-1234 untuk detail keputusan bisnis)
    fun persentaseDiskon(tahun: Int): Double = if (tahun >= 2026) 0.10 else 0.05
}

fun main() {
    println("=== Demo Clean Code ===")

    // Versi sebelum
    val repoLama = Repo()
    val dataLama = listOf(
        mapOf("price" to 15000.0, "qty" to 2),
        mapOf("price" to 5000.0, "qty" to 3)
    )
    println("Versi sebelum (buruk): ${repoLama.p(dataLama)}")

    println()
    // Versi sesudah
    val items = listOf(
        ItemKeranjang(15000.0, 2),
        ItemKeranjang(5000.0, 3)
    )
    val keranjang = KeranjangBelanja()
    val total = keranjang.hitungTotal(items)
    println("Versi sesudah (clean):")
    println(PeringkasStruk().ringkas(items, total))

    println()
    val diskon = DiskonTahunan()
    println("Diskon 2025: ${diskon.persentaseDiskon(2025)}")
    println("Diskon 2026: ${diskon.persentaseDiskon(2026)}")
}

Output yang Dihasilkan:

text
=== Demo Clean Code ===
Versi sebelum (buruk): 45000.0

Versi sesudah (clean):
- 2x @ Rp15000.0
- 3x @ Rp5000.0
Total: Rp45000.0

Diskon 2025: 0.05
Diskon 2026: 0.1

Penamaan Bermakna #

Pada versi “sebelum”, Repo.p(items) tidak menjelaskan apa yang dilakukan. Nama p, t, i, q adalah noise. Pada versi “sesudah”, KeranjangBelanja.hitungTotal(items: List<ItemKeranjang>) langsung menjelaskan maksud: class KeranjangBelanja punya method hitungTotal yang menerima list ItemKeranjang. Aturan praktis:

  • Pakai kata benda untuk class dan nilai: Pengguna, KeranjangBelanja.
  • Pakai kata kerja untuk method: hitungTotal, simpanPesanan.
  • Hindari singkatan: mgr jadi manajer, cfg jadi konfigurasi.
  • Pakai nama domain: Pesanan, Pelanggan, Faktur.

Fungsi Kecil #

Fungsi Repo.p melakukan tiga hal: iterasi, perhitungan, dan return. Pada versi clean, KeranjangBelanja.hitungTotal hanya menghitung total; formatting dipisah ke PeringkasStruk.ringkas. Fungsi kecil memiliki beberapa keuntungan:

  • Mudah diberi nama yang akurat.
  • Mudah di-test secara terpisah.
  • Mudah dipahami tanpa scroll.

Single Responsibility #

Class Repo menggabungkan logika bisnis (menghitung total) dan struktur data (Map<String, Any>). Pada versi clean, ItemKeranjang adalah data class terpisah, KeranjangBelanja hanya menghitung, PeringkasStruk hanya memformat. Bila requirement berubah (mis. format struk ke HTML), hanya PeringkasStruk yang berubah.

Komentar: Why, Not What #

Komentar // hitung total di versi buruk tidak menambah informasi — kode sudah menjelaskan. Komentar yang bermakna adalah yang menjelaskan mengapa sesuatu dilakukan, mis. “Diskon dinaikkan ke 10% untuk kompetisi”. Komentar semacam ini menyimpan konteks bisnis yang tidak ada di kode.

Referensi Lanjutan #

  • Kotlin coding conventions — konvensi penulisan Kotlin untuk nama function, struktur class, dan pemformatan yang mendukung clean code

Penutup #

Clean code adalah keterampilan yang berkembang dengan latihan dan review. Empat pilar — penamaan bermakna, fungsi kecil, single responsibility, dan komentar yang menjelaskan mengapa — adalah fondasi yang akan kita gunakan di seluruh chapter arsitektur berikutnya. Pada chapter berikutnya kita akan memperluas ke prinsip SOLID yang lebih formal.