Fungsi Kecil & Fokus — Clean Code

Fungsi yang baik melakukan satu hal, melakukannya dengan baik, dan hanya satu hal itu saja. Jika fungsi terlalu panjang atau melakukan terlalu banyak, pecah…

Fungsi yang baik melakukan satu hal, melakukannya dengan baik, dan hanya satu hal itu saja. Jika fungsi terlalu panjang atau melakukan terlalu banyak, pecah jadi fungsi-fungsi kecil.

Seberapa kecil? Idealnya 5-20 baris. Kalau perlu scroll untuk membaca seluruh fungsi, kemungkinan bisa dipecah.

Sebelum — fungsi monster:

// BURUK: satu fungsi melakukan 5 hal sekaligus
function processOrder(order) {
  // Validasi
  if (!order.items || order.items.length === 0) {
    throw new Error("Order kosong");
  }
  if (!order.customer || !order.customer.email) {
    throw new Error("Customer tidak valid");
  }

  // Hitung total
  let subtotal = 0;
  for (const item of order.items) {
    subtotal += item.price * item.quantity;
  }
  const tax = subtotal * 0.11;
  const shipping = subtotal > 500000 ? 0 : 15000;
  const total = subtotal + tax + shipping;

  // Simpan ke database
  const savedOrder = db.orders.insert({
    ...order, subtotal, tax, shipping, total,
    status: "pending",
    createdAt: new Date()
  });

  // Kirim email
  sendEmail(order.customer.email, "Order Confirmation", `Total: ${total}`);

  // Update inventory
  for (const item of order.items) {
    db.products.update(item.id, {
      stock: db.products.get(item.id).stock - item.quantity
    });
  }

  return savedOrder;
}

Sesudah — fungsi kecil dan fokus:

// BERSIH: setiap fungsi punya satu tugas
function processOrder(order) {
  validateOrder(order);
  const pricing = calculatePricing(order.items);
  const savedOrder = saveOrder(order, pricing);
  sendOrderConfirmation(order.customer.email, pricing.total);
  updateInventory(order.items);
  return savedOrder;
}

function validateOrder(order) {
  if (!order.items?.length) throw new Error("Order kosong");
  if (!order.customer?.email) throw new Error("Customer tidak valid");
}

function calculatePricing(items) {
  const subtotal = items.reduce(
    (sum, item) => sum + item.price * item.quantity, 0
  );
  const tax = subtotal * 0.11;
  const shipping = subtotal > 500000 ? 0 : 15000;
  return { subtotal, tax, shipping, total: subtotal + tax + shipping };
}

function saveOrder(order, pricing) {
  return db.orders.insert({
    ...order, ...pricing,
    status: "pending",
    createdAt: new Date()
  });
}

function updateInventory(items) {
  for (const item of items) {
    const product = db.products.get(item.id);
    db.products.update(item.id, { stock: product.stock - item.quantity });
  }
}

Keuntungan fungsi kecil:

  1. Mudah dibaca — nama fungsi menjelaskan apa yang dilakukan
  2. Mudah dites — test satu fungsi tanpa setup kompleks
  3. Mudah reusecalculatePricing() bisa dipakai di tempat lain
  4. Mudah debug — error di validateOrder langsung ketemu

Aturan praktis:

// TANDA fungsi perlu dipecah:
// 1. Kamu menulis komentar "// step 1", "// step 2"
// 2. Ada blank line pemisah di dalam fungsi
// 3. Fungsi menerima lebih dari 3 parameter
// 4. Ada nested if/for lebih dari 2 level

// BURUK: nested terlalu dalam
function findCheapProducts(categories) {
  const results = [];
  for (const cat of categories) {
    if (cat.isActive) {
      for (const product of cat.products) {
        if (product.price < 50000) {
          if (product.stock > 0) {
            results.push(product);
          }
        }
      }
    }
  }
  return results;
}

// BERSIH: flatten dengan early return dan helper
function findCheapProducts(categories) {
  return categories
    .filter(cat => cat.isActive)
    .flatMap(cat => cat.products)
    .filter(isCheapAndAvailable);
}

function isCheapAndAvailable(product) {
  return product.price < 50000 && product.stock > 0;
}

Extract till you drop — terus pecah sampai setiap fungsi benar-benar hanya melakukan satu hal. Jangan takut punya banyak fungsi kecil — itu jauh lebih baik dari sedikit fungsi raksasa.

🎭 Analogi sehari-hari

Fungsi besar yang ngerjain banyak hal itu kayak alat masak "Swiss Army Knife" — gunting, pisau, pembuka botol, garpu, semua nempel jadi satu. Kelihatan praktis, tapi pas mau pake guntingnya, gak nyaman karena bagian lain ngeganggu. Susah dipertajam (test). Kalo satu rusak, bawaannya ikut rusak. Fungsi kecil = alat dapur spesialis: 1 pisau khusus daging, 1 pisau sayur, 1 gunting buah. Tiap alat sempurna di tugasnya, gampang diganti kalau patah, gampang diasah. Tukang masak profesional pakai alat spesialis. Kode profesional = fungsi spesialis kecil yang ngerjain satu hal dengan sempurna.

⚠️ Jebakan yang sering ditemui

🎯 Kapan pecah fungsi?

  • Fungsi > 30 baris → cek apakah pattern internal bisa di-extract
  • Tulisan komentar "// Step 1", "// Step 2" → tiap step jadi fungsi
  • Indentasi nested > 2 level → flatten dengan early return atau extract
  • Parameter > 3 → bungkus dalam object atau pecah ke fungsi yang lebih spesifik
  • Test setup butuh banyak mock → tanda fungsi ngerjain terlalu banyak
  • Susah kasih nama fungsi → tanda ngerjain banyak hal beda

Aturan praktis: kalau kamu mulai scroll buat baca 1 fungsi, terlalu panjang. Pecah.

TL;DR: Fungsi baik = melakukan SATU hal, dengan sempurna. Idealnya 5-20 baris. Tanda perlu pecah: butuh komentar "step 1/2/3", parameter > 3, nested > 2 level, scroll perlu. Aturan: extract till you drop. Banyak fungsi kecil dengan nama jelas > sedikit fungsi raksasa. Mixing abstraction levels = jelek (high-level fungsi gak boleh ke-mix dengan low-level details).

Yang akan kamu pelajari