Anti-patterns & Code Smells — Design Patterns

Design pattern sering menyelamatkan kode. Tapi salah pakai pattern (atau pakai pattern padahal tidak perlu) menghasilkan anti-pattern. Kenali gejalanya…

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:

Pattern melukai saat:

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:

Yang akan kamu pelajari