Loading creator page
rekomendasi

AI sekarang bisa membantu generate adapter payment provider dalam hitungan menit. Jelaskan interface-nya. Berikan contoh request dan response. Minta agent menulis mapping, validation, dan test dasarnya.
Selesai.
Tapi di banyak tim yang pernah saya lihat dan pimpin, onboarding provider baru masih membutuhkan waktu berbulan-bulan.
Bukan karena engineer-nya tidak kompeten. Bukan karena API provider terlalu sulit. Bukan karena tooling-nya kurang canggih.
Timnya lambat karena ada pertanyaan paling mendasar yang tidak pernah benar-benar dijawab.
Saya mempelajari pola ini dengan cara yang cukup mahal: mengulang kesalahan yang sama di beberapa domain. Payment. Risk. Platform. Commerce.
Perusahaannya berbeda. Timnya berbeda. Niatnya selalu sama. Kami ingin membangun sistem yang fleksibel dan siap untuk masa depan.
Jadi kami mendesain:
multiple provider sejak awal karena nanti pasti membutuhkan lebih dari satu,
workflow yang bisa dikonfigurasi karena setiap use case mungkin berbeda,
adapter yang extensible supaya sistem tidak terkunci pada satu pola,
interface generik yang bisa menampung kebutuhan berikutnya.
Di atas kertas, arsitekturnya terlihat matang. Clean. Fleksibel. Siap scale.
Di realita, semuanya tetap lambat.
Payment adalah contoh yang paling mudah dilihat. Interface provider sudah didefinisikan. Adapter sudah dipisahkan. Secara teknis, menambah provider baru seharusnya tinggal mengimplementasikan kontrak yang tersedia.
Tapi setiap integrasi baru kembali membuka diskusi yang sama:
Kapan sebuah payment dianggap berhasil?
Siapa yang menangani retry?
State disimpan di mana?
Siapa yang bertanggung jawab ketika transaksi berhenti di tengah?
Flow mana yang harus konsisten dan mana yang boleh mengikuti behavior provider?
Setiap diskusi berakhir dengan kalimat yang terdengar masuk akal:
Tergantung use case-nya.
Tidak ada yang salah dengan kalimat itu. Memang ada use case yang berbeda. Masalahnya, tidak ada yang memilih use case mana yang harus menjadi standar.
Akibatnya, perubahan kecil ikut menjadi mahal. Menambah satu field atau validation rule membutuhkan koordinasi beberapa tim. Semua orang sibuk. Sprint velocity terlihat baik. Tapi provider berikutnya tetap membutuhkan waktu berbulan-bulan.
Lama-lama polanya menjadi jelas. Masalahnya bukan di engineer. Masalahnya juga bukan karena abstraksi yang kami buat kurang canggih.
Masalahnya adalah tidak ada yang benar-benar memutuskan:
apa lifecycle payment dari awal sampai akhir,
siapa yang memiliki setiap transisi,
flow mana yang wajib distandardisasi,
state mana yang menjadi single source of truth,
dan variasi mana yang memang layak dibayar sebagai exception.
Pertanyaan itu tidak nyaman untuk dijawab. Menjawabnya berarti menutup kemungkinan lain. Berkomitmen pada satu arah. Menolak permintaan yang terdengar wajar. Mengakui bahwa sistem tidak akan mendukung semua variasi sejak hari pertama.
Tidak ada yang ingin menjadi orang yang berkata tidak. Jadi kami melakukan hal yang terasa paling produktif bagi engineer.
Kami menambah abstraksi.
Abstraksi terasa seperti kemajuan. Interface yang generik. Adapter yang pluggable. Configuration yang fleksibel. Dari luar, semuanya terlihat seperti engineering yang matang.
Tapi abstraksi yang dibuat sebelum keputusan diambil bukan menyelesaikan ketidakpastian. Dia menyimpan ketidakpastian itu di dalam sistem.
Ketika sebuah interface bisa menampung semua kemungkinan, tim belum tentu sudah memahami problem-nya. Bisa jadi tim hanya belum berani memilih kemungkinan mana yang benar-benar penting.
Setiap perubahan berikutnya akan membuka kembali pertanyaan yang sama. Ketidakpastian muncul sebagai debat. Debat berubah menjadi koordinasi. Koordinasi membuat onboarding kembali membutuhkan waktu berbulan-bulan.
Saya menyebutnya abstraction debt: hutang keputusan yang disembunyikan di balik layer teknis.
Debt ini tidak selalu terlihat di code review. Tidak selalu muncul di architecture diagram. Tapi terasa setiap kali tim kembali mendebat pertanyaan yang seharusnya selesai beberapa kuartal lalu.
Saya baru memahami pola ini secara penuh ketika tim kami membangun ulang payment core. Situasinya familiar. Sistem lama. Banyak provider. Onboarding yang konsisten lambat. Semua orang tahu masalahnya, tapi tidak ada yang tahu harus mulai dari mana.
Kali ini, saya meminta tim menjawab pertanyaan dasarnya sebelum membuat desain baru:
1. Apa kontrak minimum yang harus dipenuhi setiap provider?
2. Di mana canonical state disimpan?
3. Lifecycle event mana yang wajib ada dan mana yang opsional?
4. Service mana yang memiliki setiap transisi state?
5. Kondisi apa yang secara sadar akan kita perlakukan sebagai exception?
Prosesnya tidak mulus. Ada yang merasa kami membatasi sistem terlalu awal. Ada yang khawatir provider berikutnya tidak cocok dengan model yang dipilih. Beberapa perdebatan membutuhkan waktu lebih lama daripada yang kami harapkan.
Tapi kali ini kami menutup perdebatan dengan keputusan. Keputusannya ditulis sebagai decision record, bukan dibiarkan menjadi asumsi di kepala masing-masing.
Hasilnya, onboarding provider baru menjadi empat kali lebih cepat.
Bukan karena sistemnya lebih pintar. Bukan karena engineer-nya tiba-tiba lebih senior. Bukan karena adapter-nya menghasilkan lebih sedikit code.
Tim bergerak lebih cepat karena pertanyaan mendasar tidak perlu dibuka kembali setiap kali provider baru datang.
Selama ini kami menganggap keputusan yang membatasi variasi akan mengurangi fleksibilitas. Yang terjadi justru sebaliknya.
Kontrak minimum membuat tim tahu bagian mana yang stabil. Canonical state membuat semua integrasi berbicara dalam bahasa yang sama. Ownership membuat exception punya jalur keputusan yang jelas.
Provider tetap boleh berbeda. Sistem tidak perlu berpura-pura bahwa QRIS, virtual account, kartu, dan transfer manual memiliki behavior yang identik. Tapi perbedaannya tidak lagi bebas menyebar ke seluruh codebase.
Constraint tidak menghilangkan fleksibilitas. Constraint menentukan tempat yang aman untuk fleksibilitas.
Payment system dengan banyak provider memang membutuhkan abstraksi. Masalahnya bukan apakah abstraction layer boleh ada. Masalahnya kapan dan mengapa dia dibuat.
Abstraksi yang lahir dari pola nyata dan keputusan yang jelas menghasilkan leverage. Abstraksi yang lahir dari kemungkinan hipotetis dan keputusan yang dihindari menghasilkan debt.
Perbedaannya tidak selalu terlihat dari bentuk code. Keduanya bisa memakai interface, adapter, dan configuration yang sama. Yang membedakan adalah apakah setiap layer melindungi keputusan yang sudah dibuat atau menyembunyikan keputusan yang belum ada.
Sekarang, sebelum menambah kompleksitas ke sistem, saya memakai empat pertanyaan:
1. Evidence: apakah ini menyelesaikan use case nyata atau skenario hipotetis yang hanya terasa logis?
2. Decision: keputusan apa yang sedang dihindari dengan abstraction ini?
3. Present cost: kalau layer ini dihapus hari ini, apa yang benar-benar rusak sekarang?
4. Boundary: apakah kita sedang memperjelas batas hari ini atau membeli fleksibilitas untuk masa depan yang belum terbukti?
Pertanyaan terakhir paling sering membuka masalahnya. “Fleksibilitas untuk masa depan” adalah alasan yang sulit ditolak. Terdengar bertanggung jawab. Terdengar seperti engineering yang matang.
Tapi fleksibilitas tanpa batas adalah ketidakpastian yang harus dibayar tim di setiap sprint berikutnya.
Kalau payment system terasa lebih kompleks daripada seharusnya, kemungkinan besar bukan hanya karena problem-nya sulit. Ada keputusan yang belum diambil, lalu sistem diminta menanggung ketidakpastian itu.
AI bisa generate adapter lebih cepat. Bisa membaca dokumentasi provider. Bisa membuat mapping dan test dalam hitungan menit.
Tapi kalau lifecycle, ownership, canonical state, dan kontrak minimum belum diputuskan, yang bergerak lebih cepat hanya kompleksitasnya.
Putuskan dulu. Baru build.
Jangan #Overbuild.
Enjoy Arya Nugroho's content?
Support the creator so they can keep making things.