Design pattern sering menyelamatkan kode. Tapi salah pakai pattern (atau pakai pattern padahal tidak perlu) menghasilkan anti-pattern. Kenali gejalanya, kenali obatnya.
Anti-pattern = solusi yang tampak wajar tapi justru memperparah masalah. Code smell = indikasi di kode yang mencurigakan dan sering menjadi sign anti-pattern atau design yang buruk.
1. God Object (alias Blob, God Class)
Satu class/file yang tahu terlalu banyak dan berbuat terlalu banyak. 2000+ baris, 50+ method, referensi ke hampir semua modul lain.
// BAD
class Application {
// 30 properti dari db sampai email config
handleLogin() { /* 200 baris */ }
renderDashboard() { /* 150 baris */ }
sendNotification() { /* 80 baris */ }
calculateTax() { /* 120 baris */ }
// ... 40 method lagi
}
Gejala: edit satu fitur = harus test semua; conflict merge Git konstan; bug di satu area propagate ke semua.
Obat: Single Responsibility Principle. Pecah ke service kecil (AuthService, NotificationService, TaxCalculator). Tiap class punya satu alasan untuk berubah.
2. Spaghetti Code
Control flow tidak terstruktur: ratusan goto-like jump, nested callback 8-level dalam, tidak ada lapisan abstraksi.
// BAD — callback hell spaghetti
getUser(id, (err, user) => {
if (err) return handleErr(err);
getOrders(user.id, (err, orders) => {
if (err) return handleErr(err);
orders.forEach(o => {
getItems(o.id, (err, items) => {
// ... nested 5 level lagi
});
});
});
});
Obat: async/await, small functions, flattening, guard clauses.
3. Premature Optimization
Optimasi tanpa data, berdasarkan asumsi. "Saya pakai bitwise karena lebih cepat." Padahal bottleneck sebenarnya query N+1 di loop.
"Premature optimization is the root of all evil." — Donald Knuth
Obat: profile dulu. Chrome DevTools Performance tab, performance.now(), Laravel Telescope. Optimasi di hot path terbukti, bukan tebakan.
4. Copy-Paste Programming (DRY violation)
Kode yang sama di 5 tempat berbeda. Saat bug ditemukan, harus fix 5 kali (dan satu selalu lupa).
Obat: extract ke function/class. Tapi hati-hati DRY ekstrim — jangan paksa share kode yang kebetulan mirip tapi berkembang beda. Rule of three: duplikasi 2 kali masih ok, 3 kali baru ekstrak.
5. Magic Numbers & Magic Strings
// BAD
if (user.type === 3 && user.status === "A") { /* ?? */ }
setTimeout(fn, 86400000); // apa ini?
// GOOD
const USER_TYPE_ADMIN = 3;
const STATUS_ACTIVE = "A";
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
if (user.type === USER_TYPE_ADMIN && user.status === STATUS_ACTIVE) { /* jelas */ }
Obat: konstanta bernama, enum (TypeScript), lookup table.
6. Long Parameter List
Function dengan 7+ parameter. Urutannya membingungkan, overload hampir mustahil.
// BAD
createUser(name, email, password, age, country, role, isActive, newsletter, referrer);
// GOOD
createUser({ name, email, password, age, country, role, isActive, newsletter, referrer });
Obat: parameter object (config object pattern) atau Builder Pattern kalau konstruksi berlangkah.
7. Feature Envy
Method di class A yang lebih banyak akses data class B daripada data sendiri.
// BAD — method di Order, tapi 80% akses data di Customer
class Order {
formatCustomerAddress(customer) {
return `${customer.street}, ${customer.city}, ${customer.province}, ${customer.postalCode}, ${customer.country}`;
}
}
Obat: pindahkan method ke class yang datanya lebih banyak diakses (di sini: Customer.formatAddress()).
8. Singleton abuse
Singleton seolah solusi "akses dari mana saja". Jadinya global state dengan nama lain — sulit di-test, hidden dependency.
Obat: Dependency Injection. Inject instance shared dari boot time.
9. Over-engineering / Design Pattern Madness
Bikin Factory-of-Factories-of-Abstract-Factory-Builders untuk bikin object dengan 2 field. Pattern yang seharusnya menyederhanakan malah jadi ritual.
// BAD — overkill untuk kebutuhan sederhana
class PointFactoryBuilderStrategyFacade { /* ... */ }
// GOOD
const p = { x: 10, y: 20 };
Obat: YAGNI (You Aren't Gonna Need It). Mulai dari yang sederhana, refactor ke pattern saat kompleksitas terbukti muncul — bukan berdasarkan ramalan.
Kapan pattern membantu vs melukai:
Pattern membantu saat:
- Masalah yang kamu hadapi sudah cocok dengan pattern standar
- Kompleksitas pattern < kompleksitas tanpa pattern
- Tim paham pattern itu (tidak perlu 1 jam menjelaskan ke reviewer PR)
Pattern melukai saat:
- Dipaksa ke problem kecil yang langsung sudah bisa
- Dipakai untuk "terlihat profesional" (ego-driven development)
- Menambah indirection yang tidak memberi nilai
Rule of thumb:
Kode sederhana tanpa pattern lebih baik dari kode rumit dengan pattern yang tidak perlu.
Tulis dulu kode paling langsung yang memecahkan masalah. Saat rasa sakit muncul (duplication nyata, perubahan sulit, test berat), baru refactor ke pattern yang sesuai. Pattern adalah solusi untuk rasa sakit yang sudah muncul, bukan asuransi untuk rasa sakit yang mungkin ada.
Bacaan lanjutan:
- Refactoring (Martin Fowler) — katalog code smell + refactoring teknik
- Clean Code (Robert C. Martin) — naming, function size, comment philosophy
- The Pragmatic Programmer — DRY, YAGNI, orthogonality