Refactoring adalah mengubah struktur internal kode tanpa mengubah behavior-nya. Tujuan: membuat kode lebih mudah dibaca, dipahami, dan di-maintain.
Aturan emas: Refactor dalam langkah kecil. Setiap langkah, pastikan kode masih berjalan (test masih pass).
Teknik 1: Extract Function
// SEBELUM
function printInvoice(invoice) {
console.log("===== INVOICE =====");
console.log(`Customer: ${invoice.customer}`);
let total = 0;
for (const item of invoice.items) {
const lineTotal = item.price * item.qty;
total += lineTotal;
console.log(`${item.name}: ${item.qty} x ${item.price} = ${lineTotal}`);
}
const tax = total * 0.11;
console.log(`Subtotal: ${total}`);
console.log(`Tax (11%): ${tax}`);
console.log(`Total: ${total + tax}`);
}
// SESUDAH: extract perhitungan dan formatting
function printInvoice(invoice) {
printHeader(invoice.customer);
const subtotal = printLineItems(invoice.items);
printTotals(subtotal);
}
function printHeader(customer) {
console.log("===== INVOICE =====");
console.log(`Customer: ${customer}`);
}
function printLineItems(items) {
let total = 0;
for (const item of items) {
const lineTotal = item.price * item.qty;
total += lineTotal;
console.log(`${item.name}: ${item.qty} x ${item.price} = ${lineTotal}`);
}
return total;
}
function printTotals(subtotal) {
const tax = subtotal * 0.11;
console.log(`Subtotal: ${subtotal}`);
console.log(`Tax (11%): ${tax}`);
console.log(`Total: ${subtotal + tax}`);
}
Teknik 2: Replace Conditional with Object/Map
// SEBELUM: switch panjang
function getStatusLabel(status) {
switch (status) {
case "pending": return "Menunggu";
case "processing": return "Diproses";
case "shipped": return "Dikirim";
case "delivered": return "Selesai";
case "cancelled": return "Dibatalkan";
default: return "Tidak diketahui";
}
}
// SESUDAH: lookup object
const STATUS_LABELS = {
pending: "Menunggu",
processing: "Diproses",
shipped: "Dikirim",
delivered: "Selesai",
cancelled: "Dibatalkan",
};
function getStatusLabel(status) {
return STATUS_LABELS[status] ?? "Tidak diketahui";
}
Teknik 3: Replace Nested Conditionals with Guard Clauses
// SEBELUM: deeply nested
function getPayAmount(employee) {
let result;
if (employee.isSeparated) {
result = { amount: 0, reason: "separated" };
} else {
if (employee.isRetired) {
result = { amount: employee.pension, reason: "retired" };
} else {
result = { amount: employee.salary, reason: "active" };
}
}
return result;
}
// SESUDAH: guard clauses — flat dan jelas
function getPayAmount(employee) {
if (employee.isSeparated) return { amount: 0, reason: "separated" };
if (employee.isRetired) return { amount: employee.pension, reason: "retired" };
return { amount: employee.salary, reason: "active" };
}
Teknik 4: Decompose Complex Expression
// SEBELUM: kondisi yang sulit dibaca
if (user.age >= 17 && user.hasId && !user.isBanned && (user.balance > 0 || user.hasCredit)) {
allowPurchase();
}
// SESUDAH: extract ke variable dengan nama bermakna
const isAdult = user.age >= 17;
const isVerified = user.hasId && !user.isBanned;
const canAfford = user.balance > 0 || user.hasCredit;
if (isAdult && isVerified && canAfford) {
allowPurchase();
}
Teknik 5: Replace Magic Numbers with Constants
// SEBELUM
if (response.status === 429) { ... }
setTimeout(retry, 3000);
if (items.length > 50) { ... }
// SESUDAH
const HTTP_TOO_MANY_REQUESTS = 429;
const RETRY_DELAY_MS = 3000;
const MAX_ITEMS_PER_PAGE = 50;
if (response.status === HTTP_TOO_MANY_REQUESTS) { ... }
setTimeout(retry, RETRY_DELAY_MS);
if (items.length > MAX_ITEMS_PER_PAGE) { ... }
Kapan refactor:
- Sebelum menambah fitur — bersihkan dulu, baru tambah
- Saat code review — "ini bisa lebih bersih"
- Saat fix bug — perbaiki kode yang menyebabkan bug, bukan cuma gejala
- Boy Scout Rule — tinggalkan kode lebih bersih dari saat kamu temukan
🎭 Analogi sehari-hari
Refactoring itu kayak rapihin kamar tanpa membeli apa-apa baru. Barangnya tetap sama (behavior gak berubah), tapi penataan ulang bikin lebih nyaman dipake — buku di rak alphabetical, baju dipisah berdasar musim, alat tulis di meja. Refactor besar sekaligus = "ngerapiin SELURUH rumah dalam 1 weekend" → exhausted, pasti ada yang miss, bisa jadi tambah berantakan. Refactor kecil bertahap (Boy Scout Rule) = tiap kali masuk kamar (sentuh kode), rapihin sedikit. Setahun kemudian, rumah otomatis lebih rapi tanpa effort besar. Refactor tanpa test = pindahin barang sambil mata tutup — pasti ada yang rusak, gak ketauan sampai kemudian hari.
⚠️ Jebakan yang sering ditemui
- Refactor + tambah fitur barengan — ngga jelas mana yang nyebabin bug. Pisahin: refactor dulu (gak ubah behavior), commit, baru tambah fitur.
- Refactor tanpa test — pasti break behavior diam-diam. Tulis test dulu kalau belum ada (Learning Tests).
- Big bang refactor — refactor seluruh module dalam 1 commit besar = review-able, merge conflict horror, rollback susah. Pecah ke commit kecil.
- Premature refactor — refactor pas pattern belum jelas (cuma 1-2x duplicate). Tunggu Rule of Three.
- Skip refactor "karena ada deadline" — short-term win, long-term mahal (technical debt menumpuk). Boy Scout Rule: refactor kecil setiap hari.
- Refactor untuk preference style — ganti
if/elsejadi ternary cuma karena "lebih elegant" tanpa improve readability. Pursue clarity, not "elegance". - Refactor sebelum paham kode — nge-rape kode yang gak kamu paham = banyak bug. Baca + tambah test dulu, baru refactor.
Pattern Refactor yang Paling Sering Dipake
- Extract Function — pecah blok kode ke fungsi baru
- Inline Function — hapus fungsi yang gak nambah value
- Rename Variable/Function — kasih nama yang lebih jelas
- Extract Variable — kasih nama ke ekspresi kompleks
- Replace Magic Number with Constant —
429→HTTP_TOO_MANY_REQUESTS - Replace Conditional with Polymorphism — banyak
if (type === ...)→ polymorphism - Move Function — pindahin fungsi ke class/module yang lebih cocok
- Decompose Conditional — kondisi kompleks → fungsi terpisah
- Replace Loop with Pipeline —
for + push→map/filter/reduce - Introduce Parameter Object — fungsi dengan 5 param → bungkus jadi object
🎯 Refactor sekarang atau nanti?
- Lagi sentuh kode area itu → refactor (Boy Scout Rule)
- Punya test coverage bagus → refactor aman
- Pattern udah duplicate 3+ kali → refactor (Rule of Three)
- Bug fix di kode messy → refactor + fix bareng (cuma bug fix nambah technical debt)
- Mau tambah fitur ke kode messy → refactor DULU, tambah fitur kemudian
- Tanpa test + kode legacy critical → tulis test dulu (characterization test), baru refactor
- Deadline besok + kode jelek → workaround dulu, JADWAL refactor di sprint berikut
Aturan: refactor kecil setiap hari > big bang refactor sekali setahun. Pakai test sebagai safety net — tanpa test, refactor = adventure tinggi risiko.
TL;DR: Refactor = ubah struktur kode TANPA ubah behavior. Aturan emas: langkah kecil, test setelah tiap step, commit per step. Pattern utama: Extract Function, Rename, Replace Magic Number, Decompose Conditional, Replace Loop with Pipeline. Boy Scout Rule = tinggalin kode lebih bersih dari pas kamu temuin. JANGAN refactor + add feature barengan (pisah commit). Refactor tanpa test = adventure — tulis test dulu kalau belum ada.