Perjalanan 5 Tahun Backend Engineer: Dari PHP Legacy Code ke Go Microservices
Saya pernah spending 2 tahun maintenance codebase warisan. Setiap deploy adalah gambling. Sekarang saya bisa deploy 20 kali sehari tanpa worry. Perubahannya bukan cuma teknikal — tapi cara berpikir.
Starting Point: Awal Karir dengan PHP + MySQL
Semua bermula di tahun 2021. Saya lulus dari kampus, dapat kesempatan kerja di perusahaan finance kecil.-tech. Bukan startup keren — lebih ke perusahaan tradisional yang mulai go digital.
Tech stack waktu itu: PHP + MySQL. Bukan PHP 8 dengan modern tooling — tapi PHP 5.x yang sudah deprecated, codebase yang inherited dari vendor yang sudah tidak ada. Setiap file ada comment dalam Bahasa Indonesia campur aduk dengan English, kadang ada Bahasa Jawa.
Yang membuat saya bertahan bukan teknologinya — tapi opportunity untuk belajar. Di situ saya belajar hal yang tidak diajarkan di kampus:
- Production itu brutal — kode yang berjalan di laptop sendiri VS kode yang serve 1000 concurrent users itu berbeda dunia
- Technical debt itu real — setiap shortcut yang diambil developer sebelumnya, eventually akan ditagih
- Business context penting — tidak bisa bikin sistem yang bagus tanpa paham bagaimana bisnis berjalan
Saya spending 8 bulan pertama mostly reading code. Tidak ada dokumentasi — kode adalah dokumentasi. Ini坏事 tapi juga好事 — forcing saya untuk benar-benar memahami bagaimana setiap pieces connect.
Turning Point: Pertama Kali Touch Go dan Kenapa Switch
Di tahun 2022, perusahaan mulai plan rebuild system. Saya dapat kesempatan untuk evaluate Go sebagai primary language. Awalnya skeptis — PHP sudah bisa handle banyak case, kenapa harus switch?
Then I actually tried it.
Beberapa hal yang langsung terasa berbeda:
- Compilation error as a feature — di PHP, typo di variable name baru ketahuan saat runtime. Di Go, compiler catch semuanya sebelum kode jalan. Produktivitas naik significant.
- Concurrency yang first-class — goroutines dan channels membuat async programming approachable. Di PHP, kita limited dengan blocking I/O atau perlu setup worker queue yang complex.
- Standard library yang complete — net/http, encoding/json, database/sql — semua sudah ada. Tidak perlu install 50 composer packages.
- Deploy sebagai single binary — tidak ada PHP-FPM, tidak ada opcache configuration yang mystical. Satu binary, jalan di mana saja.
// Go's concurrency model — straightforward dan powerful
package main
import (
"context"
"fmt"
"sync"
"time"
)
func fetchUserData(ctx context.Context, userID string, wg *sync.WaitGroup) {
defer wg.Done()
select {
case <-ctx.Done():
fmt.Printf("Request cancelled for user %s\n", userID)
return
case <-time.After(2 * time.Second):
fmt.Printf("Fetched data for user %s\n", userID)
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var wg sync.WaitGroup
userIDs := []string{"user1", "user2", "user3"}
for _, id := range userIDs {
wg.Add(1)
go fetchUserData(ctx, id, &wg)
}
wg.Wait()
fmt.Println("All fetches completed")
}
Tapi yang paling saya appreciate: Go forces you to think about errors explicitly. Tidak ada exception mechanism yang memungkinkan kamu untuk mengabaikan error sampai somewhere else catch it. Every error is a value, dan kamu harus handle it.
// Error handling yang explicit di Go — tidak bisa diabaikan
func GetUser(id string) (*User, error) {
user, err := userRepo.FindByID(id)
if err != nil {
if err == ErrNotFound {
return nil, fmt.Errorf("user %s not found: %w", id, err)
}
return nil, fmt.Errorf("get user: %w", err) // Wrapped untuk tracing
}
return user, nil
}
Nightmare Story: Hardest Legacy Migration
Di PrivyID, saya dapat tugas yang levelnya berbeda: migration dari monolith PHP ke microservices Go. Bukan hanya technical challenge — tapi juga organizational challenge.
System yang existing sudah 5 tahun, dipakai oleh 50+ enterprise clients,每天Processing 1 juta+ transactions. Business tidak bisa stop — downtime budget adalah zero.
Masalah paling challenging:
1. Data Migration yang Tidak Boleh Gagal
Migrasi data dari schema lama ke schema baru dengan zero downtime. Solusi: dual-write dengan transactional outbox, seperti yang saya ceritakan di article sebelumnya tentang Kafka migration.
2. Zero-Downtime Deployment dengan Legacy Dependency
Services baru harus bisa talk ke services lama yang belum di-migrate, dan vice versa. Mismatch di API contract adalah daily occurrence.
// API Gateway untuk handle新旧 systems communication
package gateway
type APIRouter struct {
legacyHandler *LegacyHandler // PHP services
newHandler *NewHandler // Go services
}
func (r *APIRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
path := req.URL.Path
// Route ke legacy atau new berdasarkan feature flag
if featureflags.IsEnabled(req.Context(), "new-user-service", req.Header.Get("X-Client-ID")) {
r.newHandler.ServeHTTP(w, req)
return
}
r.legacyHandler.ServeHTTP(w, req)
}
3. Team yang Belum Familiar dengan Microservices
Tidak semua tim bisa langsung adapt. Ada resistance — "Kenapa harus ubah yang sudah jalan?" Komunikasi dan incremental demonstration of value itu krusial untuk get buy-in.
Hardest moment: saat production incident di week kedua migration. Data inconsistency between old dan new system. 3 hari tidak tidur, hotfix di production,dan aku harus объяснять ke manajemen kenapa ini happened dan bagaimana kita prevent di masa depan.
"That incident taught me more about distributed systems than any book or course ever could. Real production problems are the best teachers."
Breakthrough: Moment Ketika System Finally "Click"
Setelah 8 bulan migration, akhirnya paham. Bukan tiba-tiba — gradual accumulation of understanding. Tapi ada moment spesifik:
Saat pertama kali kita deploy feature baru (notification preference management) sebagai independent service. Tim notification bisa develop, test, dan deploy tanpa coordinated release dengan tim user management. Dalam 2 minggu mereka deliver feature yang sebelumnya butuh 2 bulan kalau integrated ke monolith.
Itu moment where I realized: ini bukan tentang technology. Ini tentang bagaimana team работает.
Beberapa insight yang baru "click":
- Microservices is not about size — it's about team autonomy and bounded contexts
- Distributed systems are fundamentally different — CAP theorem bukan theoretical concept, tapi practical constraint
- Observability bukan optional — kalau tidak bisa lihat apa yang terjadi, tidak bisa debug
- Incremental migration beats big bang rewrite — setiap step harus deliver value dan bisa di-rollback
Sekarang di Indodax, saya apply semua lessons itu. Setiap architectural decision kita tanya: "Apakah ini membantu team move faster dan lebih reliable?" Kalau tidak, why bother.
Advice Practicals untuk Engineers yang Mau Level Up
Dari 5 tahun pengalaman, berikut advice yang saya wish somebody told me earlier:
1. Learn Systems Thinking, Not Just Syntax
Bisa bahasa pemrograman itu hanya small part. Yang lebih penting: bagaimana system bekerja secara keseluruhan. Baca tentang:
- Distributed systems fundamentals (CAP theorem, consensus algorithms)
- Database internals (how B-tree indexes work, transaction isolation levels)
- Operating system concepts (I/O models, memory management)
- Networking (TCP congestion control, HTTP/2, gRPC)
// Belajar dari kode Open Source yang bagus
// Baca source code Kubernetes, Docker, atau library yang kamu pakai daily
// Contoh: bagaimana golang.org/x/net/http2 handle connection pooling?
// github.com/golang/net/http2/hpack/hpack.go
// Understanding HPACK compression bisa help kamu optimize API performance
// Belajar dari production incidents
// Postmortem dari Google, Netflix, Amazon — learn from others' mistakes
// aws.amazon.com/message/producer-amazon-kinesis/
2. Build Side Projects that Solve Real Problems
Tutorials itu bagus untuk syntax, tapi tidak teach you how to think. Build something yang kamu actually want to use:
- Personal finance tracker dengan real bank API integration
- Monitoring tool untuk self-hosted services
- Automation scripts untuk repetitive tasks
Saya build notification aggregator untuk replace semua email subscriptions yang tidak pernah dibaca. Simple project, tapi I learned about background jobs, rate limiting, dan email parsing.
3. Find Mentors and Community
Software engineering is a team sport. Tidak bisa belajar semuanya sendiri.
- Carilah senior yang mau share knowledge — tidak harus formal mentorship
- Gabung komunitas — saya aktif di Golang Indonesia Slack dan beberapa tech meetups
- Read code dari developers yang lebih baik dari kamu
- Write about what you learn — teaching is the best way to learn
4. Prioritize Debugging Skills Over Writing Code Skills
Real world: 70% of time spent debugging and maintaining, 30% writing new code. Skills yang paling valuable:
- Reading existing code dan understanding it quickly
- Debugging production issues dengan limited information
- Reading logs dan traces effectively
- Formulating hypotheses dan testing them systematically
// Debugging tool yang wajib dikuasai:
//
// 1. pprof untuk Go performance profiling
// go tool pprof http://localhost:6060/debug/pprof/heap
//
// 2. Delve untuk debugging Go code
// dlv debug main.go
//
// 3. Structured logging dengan correlation IDs
// Gunakan slog atau zap dengan request ID tracking
//
// 4. Distributed tracing dengan OpenTelemetry
// traceID dari request headers — follow request across services
5. Understand Business Context
Technical skills without business understanding gives you solutions to wrong problems. Sebelum mulai coding, tanya:
- Siapa end user dari fitur ini?
- Как влияет бизнес, если эта функция сломается?
- Apa metrics yang akan measure success?
- Apakah ada compliance atau regulatory requirements?
Saya pernah spend 2 minggu membangun fitur yang technically elegant, tapi business tidak pernah launch karena priority change. Waktu yang terbuang. Dengan better business context understanding, kita bisa prioritize better.
Penutup
5 tahun sounds lama kalau ditulis di CV, tapi sebenarnya masih early stage. Masih banyak yang perlu dipelajari — distributed systems di scale yang lebih besar, machine learning infrastructure, platform engineering.
Yang paling berubah dari awal karir sampai sekarang bukan teknisnya — tapi mindset. Dulu saya think of myself sebagai "PHP developer" atau "Go developer". Sekarang think of myself sebagai problem solver yang happens to use technology sebagai tool.
Technology akan terus berubah. PHP sudah berganti jadi Go, mungkin 5 tahun lagi ada new language. Tapi fundamental skills — systems thinking, debugging, communication, business context — akan tetap relevant.
"Invest di fundamentals, bukan di specific technologies. Technologies are tools, not identities."
Terima kasih sudah baca. Mungkin cerita ini resonate dengan kamu yang juga sedang di perjalanan yang sama. Mari kita continue learning together. 🚀