Katalog anti-pattern: god object, smart UI, fat controller, anemic domain misuse, leaky abstraction, premature microservices, distributed monolith, over-abstraction YAGNI — strategi refactoring legacy: strangler fig, seams dan characterization test, incremental extract module — praktik refactor repo legacy mini bertahap dengan pagar test

Series ini mengajarkan pola yang baik; episode ini mengajarkan mengenali kebalikannya — karena di dunia nyata, tugas paling umum seorang senior engineer bukan membangun dari nol, melainkan menyelamatkan codebase yang sudah berjalan. Membongkar sistem hidup itu seni tersendiri dengan alatnya sendiri.
| Anti-Pattern | Gejala | Obat (episode) |
|---|---|---|
| God object/class | Satu class 500+ baris melayani semua aktor | SRP + pecah service (ep.3) |
| Smart UI | Logika bisnis di controller/template | Service layer (ep.10) |
| Fat controller | Validasi+SQL+email dalam satu method | Controller tipis (ep.3/10/12) |
| Anemic domain misuse | Model getter-setter, invarian tak terjaga | Rich aggregate (ep.19) |
| Leaky abstraction | Repository return QueryBuilder ORM mentah | Mapping di adapter (ep.9/15) |
| Premature microservices | 6 service untuk 2 dev & traffic kecil | Modular monolith (ep.14) |
| Distributed monolith | Microservices tapi deploy harus serentak & saling query DB | Boundary + event (ep.18–21) |
| Over-abstraction / YAGNI | Interface untuk setiap class "siapa tahu nanti" | Pragmatisme ep.9/17 |
Warning
Distributed monolith adalah anti-pattern paling mahal: seluruh biaya distribusi (latency, konsistensi, debugging terdistribusi) tanpa satu pun manfaat independensinya. Ia lahir dari microservices yang dibuat dengan batas modul salah.
Pola Martin Fowler untuk migrasi bertahap ala pohon beringin: kode baru tumbuh membungkus sistem lama sampai lama mati sendiri.
Langkah demi langkah: rute baru diarahkan ke implementasi baru; fitur dipindah per kapabilitas; sistem lama menyusut hingga bisa dihapus — tanpa big-bang rewrite yang berisiko mati total.
Sebelum menyentuh kode legacy tanpa test, bangun pagarnya:
1. Tulis characterization test pada unit yang akan disentuh
(capture output nyata -> jadikan assertion)
2. Jalankan -> hijau = pagar terpasang
3. Refactor KECIL satu langkah -> jalankan pagar -> tetap hijau?
4. Commit. Ulangi. Perubahan besar = banyak langkah kecil hijau.
5. Setelah struktur benar, PERBAIKI assertion yang membekukan bug
(ubah perilaku secara EKSPLISIT + catat di changelog)Tiga skenario legacy dari outline:
// SEBELUM: OrderController@store 180 baris (validasi+SQL+Midtrans+PDF)
// LANGKAH STRANGLER:
final class PlaceOrderAction { // logika pindah utuh
public function __construct(private OrderRepository $orders,
private PaymentGateway $payments) {}
public function execute(CheckoutData $d): Order { /* ... */ }
}
// controller kini 4 baris; SQL masih via Eloquent DI DALAM repo impl.Target outline: refactor repo legacy mini bertahap dengan characterization test sebagai pagar.
Buat sengaja (atau ambil project lama): aplikasi order dengan
- 1 file/controller raksasa berisi validasi+SQL+diskon+email
- TIDAK ADA test sama sekali
Target akhir: struktur service-repository (ep.10) dengan pagar test.Tip
Aturan emas refactoring legacy: JANGAN ubah perilaku dan struktur dalam commit yang sama. Satu commit = satu jenis perubahan, sehingga saat merah, penyebabnya pasti tunggal.
Rangkuman episode ini:
Episode 25 adalah puncak series: Capstone — satu domain order management dibangun bertahap dari layered sampai hexagonal+DDD+CQRS+outbox di ketiga framework, lengkap dengan Docker compose dan perbandingan DX/perf. Sampai jumpa!