Refactoring Techniques — Clean Code

Refactoring adalah mengubah struktur internal kode tanpa mengubah behavior-nya. Tujuan: membuat kode lebih mudah dibaca, dipahami, dan di-maintain. Aturan emas:

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:

  1. Sebelum menambah fitur — bersihkan dulu, baru tambah
  2. Saat code review — "ini bisa lebih bersih"
  3. Saat fix bug — perbaiki kode yang menyebabkan bug, bukan cuma gejala
  4. 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

Pattern Refactor yang Paling Sering Dipake

  1. Extract Function — pecah blok kode ke fungsi baru
  2. Inline Function — hapus fungsi yang gak nambah value
  3. Rename Variable/Function — kasih nama yang lebih jelas
  4. Extract Variable — kasih nama ke ekspresi kompleks
  5. Replace Magic Number with Constant429HTTP_TOO_MANY_REQUESTS
  6. Replace Conditional with Polymorphism — banyak if (type === ...) → polymorphism
  7. Move Function — pindahin fungsi ke class/module yang lebih cocok
  8. Decompose Conditional — kondisi kompleks → fungsi terpisah
  9. Replace Loop with Pipelinefor + pushmap/filter/reduce
  10. 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.

Yang akan kamu pelajari