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:
- Mudah dibaca — nama fungsi menjelaskan apa yang dilakukan
- Mudah dites — test satu fungsi tanpa setup kompleks
- Mudah reuse —
calculatePricing()bisa dipakai di tempat lain - Mudah debug — error di
validateOrderlangsung 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
- Pecah jadi terlalu kecil —
function add(a, b) { return a + b }overkill kalau cuma dipake sekali. Inline-able lebih clear. - Nama fungsi yang gak jelaskan apa-apa —
helper1(),processData(),doStuff(). Pecah TIDAK BANTU kalau nama gak deskriptif. - Premature extract — extract function di awal sebelum tahu pattern. Tunggu sampai pola berulang muncul (rule of three: 3x kemunculan = saatnya extract).
- Fungsi dengan banyak parameter (4+) — tanda fungsi ngerjain banyak hal, atau missing abstraksi. Pecah atau bungkus parameter dalam object.
- Fungsi dengan side effect tersembunyi —
validateOrder()yang DIAM-DIAM kirim email = mengejutkan. Nama harus reveal side effect:validateOrderAndNotifyAdmin(). - Mixing levels of abstraction — fungsi top-level mix high-level logic + low-level details. Pisahin: top fungsi panggil sub-fungsi yang detail.
- Lupa "Do One Thing" ambiguous — "validate" itu satu hal (cek kondisi), "validate dan throw" itu dua. Test: bisa diceritain dalam 1 kalimat tanpa "DAN".
🎯 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).