Migrasi Legacy PHP ke Go: Kenapa, Bagaimana, dan Pelajarannya



Codebase PHP yang saya tangani makin susah dipelihara. Fitur baru susah ditambahkan, bug makin sering muncul, dan setiap deploy rasanya seperti lempar koin. Bukan berarti PHP yang salah. Sistemnya tumbuh bertahun-tahun tanpa arsitektur yang jelas. Sampai suatu hari saya harus rollback tiga kali dalam sehari gara-gara deploy yang seharusnya sederhana. Lumayan bikin stres.

Saya tidak sedang bilang “Go lebih baik dari PHP” atau “PHP sudah mati”. Ini catatan pengalaman saya memigrasikan sistem backend dari PHP (CodeIgniter 3 dan Laravel) ke Go. Bukan tutorial, apalagi ajakan pindah bahasa. Siapa tahu cerita dan kesalahan saya berguna buat yang sedang menghadapi situasi serupa.

Yang Bikin Repot: Arsitekturnya #

Sistem yang saya tangani punya profil seperti ini:

PHP-nya sendiri bukan biang masalah. PHP juga bisa dipakai dengan baik. Yang bikin repot di sistem ini antara lain:

  1. Tidak ada pemisahan layer: controller langsung mengakses database, sementara business logic bercampur dengan presentation logic. Kodenya seperti nasi goreng campur, sulit membedakan bagian yang satu dari yang lain.
  2. Sulit di-test: kode yang tightly coupled membuat unit testing jadi sulit. Untuk menguji satu fungsi, saya harus menyiapkan database, mock tiga dependency, lalu berharap semuanya berjalan.
  3. Developer experience menurun: onboarding developer baru semakin lama dan bug fix semakin berisiko. Seorang developer baru pernah bilang, “Mas, ini kodenya… ehem, menarik sekali arsitekturnya.” Maksudnya, chaotic.

Jadi, mengganti bahasa saja tidak akan cukup. Arsitekturnya juga perlu dibereskan.

Kenapa Saya Pilih Go? #

Kalau ditanya kenapa tidak refactor saja di PHP, jawabannya: bisa saja. Tapi untuk kasus yang saya hadapi, Go terasa lebih cocok karena beberapa alasan. Bukan cuma karena lagi ramai dibicarakan.

1. Type Safety Menghemat Waktu Debug #

PHP bersifat dynamically typed2, sehingga banyak bug baru diketahui saat runtime. Static typing di Go dapat menangkap error terkait tipe saat compile time, sebelum error tersebut mencapai production.

2. Goroutine Membantu Urusan Concurrency #

Sistem ini menangani banyak request sekaligus, mulai dari tracking GPS real-time hingga report generation. Dibandingkan pendekatan lain yang pernah saya coba, goroutine membuat concurrent programming lebih sederhana. Saat pertama kali menggunakannya, saya sempat berpikir, “Kok bisa sesederhana ini?”

3. Urusan Deploy Lebih Ringkas #

Go menghasilkan satu binary. Tidak perlu composer install di server atau khawatir soal konflik versi PHP. Dulu, deploy PHP mengharuskan saya memastikan composer dependencies terpasang, versi PHP cocok, dan semua extension tersedia. Sekarang saya cukup melakukan build di lokal lalu mengirim satu berkas dengan scp.

4. Resource Lebih Mudah Diperkirakan #

Yang saya pertimbangkan bukan angka benchmark, melainkan penggunaan resource yang dapat diprediksi. Penggunaan memori Go yang konsisten memudahkan perencanaan kapasitas. Sistem PHP sebelumnya bisa mengalami memory spike tanpa sebab yang jelas, sedangkan penggunaan memori sekarang lebih rata dan dapat diprediksi.

Go tentu tidak sempurna. Error handling-nya verbose (semua if err != nil itu)3, dan ecosystem-nya lebih kecil daripada PHP. Untuk kebutuhan proyek ini, trade-off-nya sepadan.

Cara Migrasinya: Per Modul, Bukan Sekaligus #

Salah satu risiko migrasi adalah menulis ulang semuanya sekaligus. Saya pernah dengar cerita tim yang menghabiskan dua tahun untuk big-bang rewrite, tapi akhirnya tidak selesai. Ada yang burnout, ada scope creep, ada juga kebutuhan bisnis yang berubah di tengah jalan. Ya, banyak cara proyek bisa melenceng.

Pendekatan yang saya pakai:

1. Pemetaan Modul #

Pertama, saya memetakan semua endpoint dan entity ke modul yang jelas. Dari 370+ endpoint, saya mengelompokkannya ke beberapa domain seperti authentication, tracking, reporting, dan user management. Saya menghabiskan beberapa hari untuk membuat spreadsheet pemetaan endpoint. Pekerjaan itu cukup membosankan, tetapi membantu proses berikutnya.

2. Service-Repository Pattern #

Setiap modul saya design dengan pola service-repository:

Pembagian ini lumayan membantu:

Dengan layer yang jelas, tiap bagian punya urusannya sendiri. Awalnya pola ini terasa seperti over-engineering. Setelah dipakai beberapa bulan, saya mulai merasakan gunanya.

3. Satu Endpoint pada Satu Waktu #

Prosesnya kurang lebih:

  1. Ambil satu endpoint dari PHP
  2. Tulis test case berdasarkan behavior yang sudah ada4
  3. Implementasi di Go dengan pola service-repository
  4. Test sampai pass
  5. Deploy dan monitor
  6. Ulangi

Proses ini memang lambat, tetapi terukur dan dapat diprediksi. Setiap endpoint yang selesai menjadi kemajuan yang bisa dilihat stakeholder. Laporan “Minggu ini berhasil migrasi 5 endpoint” lebih jelas daripada berbulan-bulan mengatakan “kami masih mengerjakan migrasi” tanpa hasil yang terlihat.

Hasilnya Sejauh Ini #

Ini beberapa angka dari proyeknya:

Hal yang tidak tercatat dalam angka, tetapi terasa, adalah lebih tenangnya proses deploy. Saya tidak lagi berharap semoga tidak ada yang rusak. Deploy tetap membuat sedikit gugup, tetapi tidak sampai mengganggu tidur.

Hal yang Saya Pelajari #

Ada beberapa hal yang saya pelajari selama proses ini:

Kenali Sistem Lama Dulu #

Sebelum menulis kode Go, saya coba pahami dulu behavior sistem lama. Saya baca kodenya sambil mencari tahu kenapa keputusan tertentu pernah dibuat. Kode yang kelihatan “aneh” mungkin punya alasan yang masuk akal pada masanya. Jadi, jangan buru-buru menyebut kode legacy jelek. Bisa saja ada constraint yang sekarang sudah tidak kelihatan.

Jangan Langsung Tulis Ulang Semuanya #

_Kalau bisa dicicil, kenapa harus sekaligus? Incremental memang terasa pelan, tapi risikonya lebih kecil. Tiap endpoint yang selesai bisa ditunjukkan ke stakeholder, sementara sistem lama tetap berjalan.

Tes Sebelum Pindah #

Menulis test untuk sistem legacy memang membosankan (saya tahu). Tapi test jadi jaring pengaman supaya behavior tidak berubah diam-diam setelah migrasi. Tanpanya, kita bisa saja mengubah sesuatu yang sudah diandalkan pengguna.

Jangan Keburu Membuatnya Rumit #

Awalnya saya tergoda memakai microservices, message queue, dan macam-macam arsitektur rumit lainnya. Tapi sistem yang arsitekturnya belum stabil butuh fondasi dulu, bukan tambahan komponen. Monolith yang well-structured lebih enak dirawat daripada microservices yang chaotic. Prinsip KISS (Keep It Simple, Stupid)6 masih kepakai.

Jadi, Perlukah Pindah ke Go? #

Buat saya, migrasi PHP ke Go bukan lomba “bahasa A lebih baik dari bahasa B”. Yang penting memilih tool yang pas untuk masalahnya. Di proyek ini, static typing, model concurrency, dan deployment yang lebih sederhana membuat Go terasa cocok.

Apakah semua sistem PHP perlu pindah ke Go? Tidak juga. Kalau sistemnya berjalan baik, arsitekturnya jelas, dan tim nyaman dengan PHP, pindah cuma karena hype malah menambah pekerjaan. Menurut saya, migrasi baru masuk akal kalau business value-nya jelas dan manfaatnya bisa diukur.

Kalau sistem legacy yang kamu tangani mulai sulit dikembangkan dan kamu ingin mendiskusikan pendekatan migrasinya, hubungi saya. Kamu juga bisa melihat layanan yang saya tawarkan.


Tag: go · php · arsitektur · migration


  1. Serius, saya sendiri sempat double-check angkanya waktu pertama kali ngitung. Ternyata memang segitu banyak hahaha. ↩︎

  2. PHP 7+ memang sudah punya type hints dan strict types, tapi tetap tidak se-strict Go yang static typed sejak awal. ↩︎

  3. Di awal-awal pakai Go, saya sempat annoyed sama banyaknya if err != nil. Tapi setelah terbiasa, ya udah accept aja. Lagian lebih baik verbose tapi jelas daripada concise tapi error handling-nya hidden. ↩︎

  4. Menulis test berdasarkan behavior yang ada itu penting. Jangan sampai migrasi malah mengubah behavior yang sudah diharapkan user. ↩︎

  5. Migrasi dari MySQL ke PostgreSQL itu cerita tersendiri. Someday mungkin saya tulis juga hehe. ↩︎

  6. Keep It Simple, Stupid, prinsip yang sering dilupakan saat kita terlalu excited sama teknologi baru. ↩︎