DRY (Don't Repeat Yourself) — Clean Code

DRY (Don't Repeat Yourself): Setiap bagian pengetahuan harus punya satu representasi di dalam sistem. Jika kamu copy-paste kode, kemungkinan besar kamu…

DRY (Don't Repeat Yourself) artinya tiap pengetahuan di program punya satu tempat. Yang dihitung bukan teks yang mirip, tapi aturan yang sama. Bayangin harga kaos Raka yang ditulis di tiga tempat: katalog Instagram, halaman toko, dan nota. Harga naik, kamu ganti katalog dan halaman toko, notanya lupa. Pembeli lihat 80.000, nota bilang 75.000. Gak ada yang protes saat itu juga, tapi angkanya sudah gak cocok.

🧪 Tebakan cepat: cara menulis rupiah di struk ada di tiga fungsi. Raka minta formatnya diganti, dan kamu cuma mengubah dua dari tiga. Yang terjadi: error, atau struk yang campur aduk? Tebak dulu, jawabannya ada di bagian "Duplikasi gak bikin error".

Struk dengan tiga pemformat

Struk Toko Kaos Raka untuk keranjang A (2 kaos polos, 1 kaos sablon, voucher HEMAT10):

function itemLine(item) {
  return item.name + ' x' + item.qty + ': Rp ' + (item.price * item.qty).toLocaleString('id-ID');
}
function savingLine(amount) {
  return 'Hemat: Rp ' + amount.toLocaleString('id-ID');
}
function totalLine(amount) {
  return 'Total: Rp ' + amount.toLocaleString('id-ID');
}

function printReceipt(cart, saving, total) {
  for (const item of cart) console.log(itemLine(item));
  console.log(savingLine(saving));
  console.log(totalLine(total));
}

printReceipt(cart, 24500, 220500);
Kaos Polos Hitam x2: Rp 150.000
Kaos Sablon Kopi x1: Rp 95.000
Hemat: Rp 24.500
Total: Rp 220.500

Cara menulis rupiah, 'Rp ' + angka.toLocaleString('id-ID'), adalah satu pengetahuan. Di sini ia tertulis tiga kali.

Duplikasi gak bikin error

Raka mulai jualan ke luar negeri dan minta format IDR 220.500. Kamu mengubah itemLine dan totalLine, savingLine kelupaan:

Kaos Polos Hitam x2: IDR 150.000
Kaos Sablon Kopi x1: IDR 95.000
Hemat: Rp 24.500
Total: IDR 220.500

Gak ada error, programnya jalan terus. Itu jawaban tebakannya: duplikasi gak bikin program berhenti, cuma bikin hasilnya diam-diam gak konsisten. Makin banyak salinannya, makin besar peluang ada yang terlewat.

Satu fungsi, satu tempat

Pindahkan pengetahuannya ke satu fungsi, lalu semua tempat memanggil fungsi itu:

function formatRupiah(amount) {
  return 'Rp ' + amount.toLocaleString('id-ID');
}

function itemLine(item) {
  return item.name + ' x' + item.qty + ': ' + formatRupiah(item.price * item.qty);
}
function savingLine(amount) {
  return 'Hemat: ' + formatRupiah(amount);
}
function totalLine(amount) {
  return 'Total: ' + formatRupiah(amount);
}

Hal yang sama berlaku untuk angka. Kalau tarif pajak 0.11 ditulis di tiga baris (price * 0.11, shipping * 0.11, service * 0.11), ekstrak ke sebuah konstanta: const TAX_RATE = 0.11;, lalu ketiganya memakai TAX_RATE. Tarif berubah, satu baris yang diubah. Konstanta VOUCHER_DISCOUNT, FREE_SHIPPING_MIN dan SHIPPING_FEE di kasir Raka adalah contoh yang sama.

Mirip belum tentu sama

Raka juga punya reseller dengan potongan kontrak 10%. Kode kasirnya punya dua angka yang kebetulan sama:

const VOUCHER_DISCOUNT = 0.1;  // promo HEMAT10, diatur marketing
const RESELLER_DISCOUNT = 0.1; // kontrak reseller, tertulis di perjanjian

Godaannya: "dua-duanya 0.1, gabung jadi DISCOUNT_RATE". Bulan depan marketing menaikkan promo jadi 15% dan mengubah DISCOUNT_RATE jadi 0.15. Reseller yang belanja Rp 245.000 ikut dapat 15%: bayar Rp 208.250, padahal kontraknya 10%, Rp 220.500.

Dua kode yang terlihat sama tapi berubah karena alasan berbeda disebut coincidental duplication (duplikasi kebetulan). Menggabungnya bikin abstraksi yang salah: satu perubahan merembet ke tempat yang gak seharusnya. Sandi Metz menulis (2016): "duplication is far cheaper than the wrong abstraction", duplikasi jauh lebih murah daripada abstraksi yang salah.

Pertanyaan pengujinya: kalau aturan yang satu berubah, apakah yang lain wajib ikut berubah? Format rupiah di tiga baris struk: ya, jadi gabung. Promo marketing dan kontrak reseller: tidak, jadi biarkan dua. Kalau ragu, banyak developer menunggu pola yang sama muncul tiga kali (rule of three) sebelum menggabung.

Latihan: formatRupiah

Di latihan, showProduct, showCartTotal dan showDiscount masing-masing memformat sendiri. Buat function formatRupiah(amount) yang mengembalikan 'Rp ' + amount.toLocaleString('id-ID'), lalu ketiga fungsi memanggilnya. toLocaleString cuma boleh tertulis sekali di seluruh kode. Output-nya tetap:

Laptop: Rp 15.000.000
Total: Rp 400.000
Rp 200.000 -> Rp 150.000

formatRupiah juga diuji sendiri dengan angka lain: formatRupiah(1500) harus 'Rp 1.500'.

⚠️ Jebakan yang sering ketemu

TL;DR: DRY artinya tiap pengetahuan punya satu tempat. Cara menulis rupiah yang tersalin di tiga fungsi struk akan lupa diubah di salah satunya, dan hasilnya struk campur IDR dan Rp tanpa error. Pindahkan ke formatRupiah(amount), panggil dari semua tempat, dan pastikan toLocaleString cuma muncul sekali; angka yang berulang seperti 0.11 diekstrak ke konstanta (TAX_RATE). Tapi dua kode yang kebetulan mirip dan berubah karena alasan berbeda (promo vs kontrak reseller) jangan digabung: duplikasi lebih murah daripada abstraksi yang salah. Ujinya: kalau yang satu berubah, apakah yang lain wajib ikut?

Yang akan kamu pelajari