Deploy aman untuk porting aplikasi lama menuntut pendekatan berbeda dari rilis aplikasi yang dibangun untuk stack modern sejak awal. Masalah utamanya bukan hanya apakah aplikasi berhasil start, tetapi apakah ia tetap benar saat berinteraksi dengan dependency, driver, file system, jaringan, cache, dan pola beban nyata di environment baru.

Pada proyek porting atau migrasi kompatibilitas, banyak error hanya muncul saat runtime: library yang ternyata punya perilaku sedikit berbeda, timeout yang berubah, encoding yang tidak konsisten, atau urutan inisialisasi yang sebelumnya "kebetulan bekerja". Karena itu, strategi rilis harus menekankan canary release, health check yang benar-benar merepresentasikan fungsi penting, observabilitas untuk mendeteksi mismatch, serta rollback yang cepat dan aman.

Konteksnya mirip saat software lama akhirnya bisa berjalan di platform baru: keberhasilan start bukan jaminan kompatibilitas penuh. Aplikasi mungkin hidup, tetapi perilaku runtime-nya masih rapuh. Di sinilah disiplin deploy dan observabilitas menjadi pembeda antara rilis terkendali dan insiden produksi.

Mengapa porting aplikasi lama berisiko saat deploy

Pada aplikasi lama, banyak asumsi tertanam yang tidak pernah didokumentasikan karena selama bertahun-tahun environment-nya stabil. Begitu dipindahkan ke OS, container base image, hypervisor, runtime, atau dependency yang berbeda, asumsi tersebut mulai pecah.

Sumber risiko yang paling sering muncul

  • Asumsi kompatibilitas rapuh: aplikasi mengandalkan perilaku lama dari library, resolver DNS, locale, timezone, atau API OS tertentu.
  • Regresi driver atau dependency: query database tetap valid, tetapi planner berbeda; library TLS menerima cipher berbeda; driver file system mengubah locking atau buffering.
  • Error hanya muncul saat runtime: startup sukses, tetapi request tertentu gagal, job background macet, atau memory leak muncul setelah beberapa menit.
  • Data dan traffic riil lebih kompleks dari test: staging sering tidak mereplikasi volume, variasi payload, concurrency, dan kegagalan dependency eksternal.
  • Observabilitas warisan minim: aplikasi lama sering tidak punya metric, trace, atau structured logging yang cukup untuk isolasi masalah cepat.

Konsekuensinya, deploy besar langsung ke 100% traffic adalah langkah berisiko tinggi. Anda perlu mekanisme untuk memverifikasi perilaku runtime secara bertahap dan membatasi blast radius jika terjadi mismatch.

Strategi deploy aman: canary dulu, baru perluas

Canary release adalah pendekatan paling praktis untuk porting aplikasi lama. Ide dasarnya sederhana: kirim sebagian kecil traffic ke versi hasil porting, bandingkan sinyal operasionalnya dengan versi lama, lalu naikkan proporsi traffic hanya jika indikator tetap sehat.

Kapan canary lebih tepat daripada blue-green penuh

Blue-green bagus untuk switch cepat, tetapi pada porting aplikasi lama, masalah sering baru terlihat setelah traffic nyata masuk. Canary lebih cocok jika Anda ingin:

  • menguji kompatibilitas runtime dengan risiko terbatas,
  • membandingkan error rate dan latency antarversi,
  • mengamati dependency yang sensitif seperti database, storage, auth, atau message broker,
  • menghentikan rollout sebelum seluruh pengguna terdampak.

Blue-green tetap berguna jika rollback harus sangat cepat, tetapi idealnya digabung dengan canary: deploy versi baru ke environment terpisah, alirkan traffic kecil dulu, lalu lanjutkan cutover bertahap.

Pola rollout yang realistis

  1. 0% traffic: deploy artefak, jalankan startup check dan smoke test internal.
  2. 1-5% traffic: pantau error, latency, saturation, serta log mismatch.
  3. 10-25%: verifikasi perilaku endpoint dan background worker utama.
  4. 50%: pastikan tidak ada degradasi bertahap seperti memory leak atau queue backlog.
  5. 100%: lanjutkan hanya bila sinyal tetap konsisten selama jendela observasi yang cukup.

Durasi tiap tahap bergantung pada pola traffic dan jenis risiko. Untuk aplikasi dengan job periodik atau batch, observasi harus cukup panjang agar kasus periodik sempat dieksekusi.

Contoh aturan keputusan canary

Jangan naikkan traffic hanya karena pod hidup. Definisikan kriteria lanjut dan kriteria stop, misalnya:

  • error rate versi canary tidak melebihi baseline secara signifikan,
  • latency p95 endpoint utama tidak memburuk di luar toleransi yang disepakati,
  • tidak ada lonjakan restart, OOM, atau timeout dependency,
  • hasil smoke test pascadeploy tetap lulus,
  • queue backlog, dead-letter, atau retry tidak meningkat abnormal.

Health check yang bermakna, bukan sekadar port terbuka

Kesalahan umum pada deploy porting adalah menganggap health check sederhana sudah cukup. Endpoint seperti /healthz yang hanya mengembalikan 200 OK tidak akan menangkap incompatibility pada jalur bisnis penting.

Bedakan liveness, readiness, dan startup

  • Liveness: apakah proses masih hidup dan tidak deadlock.
  • Readiness: apakah instance siap menerima traffic.
  • Startup: apakah inisialisasi berat sudah selesai, terutama untuk aplikasi lama yang butuh warm-up atau migrasi cache.

Untuk porting aplikasi lama, readiness check sebaiknya memverifikasi lebih dari sekadar proses hidup. Ia perlu memastikan dependency kritis yang dibutuhkan untuk request benar-benar bisa dipakai.

Apa yang sebaiknya diuji oleh readiness check

  • koneksi dasar ke database atau cache, tanpa membuat query berat,
  • akses ke file path, shared volume, atau storage yang wajib ada,
  • kemampuan memuat konfigurasi atau secret penting,
  • validasi kompatibilitas minimal terhadap dependency runtime penting,
  • status komponen internal seperti worker registry, template cache, atau plugin loader bila relevan.

Namun jangan menjadikan health check terlalu mahal. Health check yang memanggil banyak layanan eksternal atau query kompleks justru bisa menambah beban dan menghasilkan false positive.

Contoh endpoint readiness sederhana

GET /readyz

{
  "status": "ok",
  "checks": {
    "database": "ok",
    "cache": "ok",
    "config": "ok",
    "storage": "ok"
  }
}

Jika aplikasi lama memiliki fitur yang sangat sensitif, lebih baik tambahkan synthetic transaction terpisah untuk menguji alur penting, bukan membebani readiness endpoint dengan semua logika bisnis.

Observabilitas untuk mendeteksi mismatch saat runtime

Pada porting aplikasi lama, observabilitas bukan pelengkap. Ia adalah alat utama untuk menjawab pertanyaan: versi baru benar-benar kompatibel atau hanya terlihat hidup?

Metrik yang perlu dipantau selama canary

Mulailah dari sinyal inti yang membantu membedakan bug aplikasi, mismatch dependency, dan masalah infrastruktur.

  • Request rate, error rate, duration per endpoint atau route group.
  • Status code distribution, terutama 5xx, timeout upstream, dan lonjakan 4xx yang tidak lazim.
  • Resource saturation: CPU, memory, file descriptor, thread, goroutine, connection pool, disk I/O.
  • Dependency metrics: latency database, pool exhaustion, cache miss spike, broker lag, retry rate.
  • Worker metrics: queue depth, job retry, dead-letter, processing time, stuck consumer.
  • Crash indicators: restart count, OOM kill, segfault, failed probe.

Sinyal mismatch yang sering terlewat

  • Perubahan pola error pada subset endpoint lama yang menggunakan kode warisan tertentu.
  • Lonjakan retry ke dependency eksternal karena format request atau timeout berubah.
  • Perbedaan output bisnis walau status code tetap 200, misalnya payload kosong, sorting berubah, atau field default berbeda.
  • Log warning baru dari runtime, TLS, serializer, atau driver yang sebelumnya tidak muncul.
  • Perubahan distribusi latency tanpa kenaikan traffic, tanda adanya lock contention, DNS issue, atau fallback code path.

Structured logging untuk isolasi cepat

Log bebas format sulit dipakai saat canary. Minimal, tambahkan field yang membantu membandingkan versi lama dan versi baru:

  • service
  • version atau build_id
  • release_channel seperti stable atau canary
  • request_id / trace_id
  • endpoint
  • dependency
  • error_code internal
{
  "level": "error",
  "service": "legacy-api",
  "version": "ported-2026-06-25",
  "release_channel": "canary",
  "trace_id": "8f2a...",
  "endpoint": "/api/orders/submit",
  "dependency": "postgres",
  "error_code": "DB_TIMEOUT",
  "message": "query exceeded deadline"
}

Dengan format seperti ini, Anda bisa memfilter apakah error hanya terjadi pada canary, hanya pada endpoint tertentu, atau hanya ketika menyentuh dependency tertentu.

Trace untuk menemukan perbedaan perilaku antarversi

Distributed tracing sangat membantu jika porting melibatkan beberapa service atau adapter baru. Trace memudahkan Anda melihat apakah versi hasil porting:

  • melakukan panggilan tambahan yang tidak perlu,
  • menghabiskan waktu lebih lama di layer auth, serializer, DNS, atau koneksi TLS,
  • sering retry ke service lain,
  • masuk ke fallback path yang sebelumnya jarang dipakai.

Bila tracing penuh belum tersedia, mulai dari propagasi trace_id dan pengukuran latency per dependency sudah memberi nilai besar.

Rollback cepat: wajib disiapkan sebelum deploy, bukan saat insiden

Pada deployment aplikasi hasil porting, rollback bukan rencana cadangan opsional. Ia harus menjadi jalur standar yang teruji.

Prinsip rollback yang efektif

  • Rollback harus cepat: idealnya cukup mengubah traffic routing atau memilih release sebelumnya.
  • Rollback harus aman: hindari perubahan yang membuat data tidak kompatibel dengan versi lama.
  • Rollback harus terdokumentasi: siapa yang menjalankan, perintahnya apa, dan kapan eskalasi dilakukan.

Masalah rollback yang sering menjebak

  • Migrasi database tidak backward-compatible: versi lama tidak bisa membaca schema baru.
  • Message format berubah: consumer lama gagal memproses event dari producer baru.
  • Cache poisoning: cache dari versi baru merusak asumsi versi lama.
  • Shared storage side effects: file hasil versi baru tidak dikenali versi sebelumnya.

Karena itu, untuk porting aplikasi lama, gunakan perubahan kompatibel maju-mundur sejauh mungkin. Jika perlu migrasi schema, prioritaskan pola bertahap seperti expand and contract: tambah struktur baru dulu, gunakan bersama, lalu hapus yang lama setelah stabil.

Contoh langkah rollback operasional

  1. Hentikan rollout dan bekukan perubahan lanjutan.
  2. Alihkan traffic canary kembali ke versi stabil.
  3. Matikan job background versi baru bila berisiko menulis data tidak kompatibel.
  4. Verifikasi metrik pulih ke baseline.
  5. Simpan artefak log, trace, dan snapshot dashboard untuk analisis setelah insiden.
# Contoh pseudocode operasional, sesuaikan dengan platform Anda
release freeze legacy-api
traffic shift legacy-api canary=0 stable=100
scale worker legacy-api-ported --replicas=0
verify dashboards legacy-api
annotate incident "rollback executed due to canary regression"

Perintah nyata akan berbeda tergantung orchestrator, service mesh, atau pipeline Anda. Yang penting, prosedurnya konsisten, diuji, dan bisa dijalankan tanpa improvisasi.

Feature flag untuk mengurangi blast radius

Feature flag sangat berguna ketika hasil porting mencakup perubahan perilaku yang tidak harus diaktifkan sekaligus. Dengan flag, Anda bisa memisahkan deploy dari activation.

Kapan feature flag paling membantu

  • saat adapter baru ke storage atau API eksternal masih ingin diuji bertahap,
  • saat hanya sebagian endpoint yang telah tervalidasi di environment baru,
  • saat ingin mematikan fitur bermasalah tanpa rollback penuh,
  • saat perlu membandingkan perilaku lama dan baru untuk subset tenant atau pengguna.

Praktik aman memakai feature flag

  • default ke mode aman atau perilaku lama,
  • beri pemilik yang jelas untuk setiap flag,
  • catat status flag dalam log dan incident timeline,
  • hapus flag yang sudah tidak diperlukan agar kompleksitas tidak menumpuk.

Kesalahan umum adalah menggunakan feature flag sebagai pengganti desain rollback. Flag membantu, tetapi tidak menyelesaikan risiko perubahan schema, format pesan, atau efek samping data.

Smoke test pascadeploy yang benar-benar berguna

Smoke test setelah deploy harus meniru alur yang paling mungkin rusak akibat porting. Fokus pada jalur tipis namun kritis, bukan rangkaian test panjang yang lambat.

Contoh smoke test yang layak dijalankan

  • login atau handshake auth dasar,
  • request baca ke endpoint utama,
  • satu transaksi tulis non-destruktif atau ke tenant uji,
  • enqueue dan proses satu job background,
  • akses file atau asset penting yang sebelumnya sensitif terhadap kompatibilitas,
  • validasi integrasi ke dependency utama seperti database dan cache.

Jika aplikasi hasil porting berkaitan dengan software lama yang banyak bergantung pada perilaku runtime tertentu, buat smoke test yang memverifikasi output, bukan hanya status sukses. Misalnya, cek field tertentu, ukuran respons, atau event yang benar-benar muncul di broker.

Contoh checklist smoke test

[ ] /readyz lulus pada semua instance canary
[ ] endpoint baca utama mengembalikan payload valid
[ ] transaksi tulis uji berhasil dan dapat dibaca kembali
[ ] job background uji selesai tanpa retry
[ ] tidak ada error baru pada log channel canary
[ ] metrik latency dan error tetap dalam toleransi

Checklist sebelum deploy porting aplikasi lama

Checklist berikut bisa dipakai tim DevOps/SRE sebelum rollout.

Sebelum rollout

  • Artefak rilis dapat direproduksi dan source commit jelas.
  • Runbook rollback tersedia, singkat, dan sudah diuji.
  • Migrasi data/schema ditinjau untuk kompatibilitas rollback.
  • Dependency matrix diketahui: database, cache, broker, TLS, file system, locale, timezone, shared library.
  • Health check memverifikasi dependency kritis tanpa terlalu berat.
  • Dashboard canary sudah siap sebelum deploy dimulai.
  • Log terstruktur memuat versi, channel, request/trace id.
  • Feature flag memiliki default aman.
  • Smoke test pascadeploy telah disiapkan dan dapat dijalankan otomatis.
  • Owner on-call dan jalur eskalasi jelas selama rollout.

Saat rollout

  • Mulai dari traffic kecil.
  • Bandingkan canary dengan baseline, bukan melihat angka mentah tanpa konteks.
  • Tahan setiap tahap cukup lama untuk menangkap error periodik.
  • Catat semua keputusan: naikkan traffic, tahan, rollback, atau matikan flag.

Setelah rollout

  • Jalankan smoke test lagi pada kondisi traffic yang lebih besar.
  • Periksa anomali yang tidak terlihat pada fase awal: memory growth, queue backlog, file lock, disk pressure.
  • Pastikan observabilitas tetap aktif beberapa jam atau beberapa siklus job setelah deploy.

Sinyal observabilitas yang perlu dipantau selama canary

Jika Anda butuh daftar singkat yang bisa langsung dijadikan dashboard, gunakan sinyal berikut:

  • Availability: success rate, 5xx rate, timeout rate.
  • Latency: p50, p95, p99 endpoint penting.
  • Dependency health: DB latency, pool exhaustion, cache error, broker lag.
  • Runtime stability: restart count, OOM, crash loop, failed probes.
  • Background processing: queue depth, retry, dead-letter, stuck jobs.
  • Correctness proxy: jumlah payload kosong, hasil validasi bisnis gagal, mismatch response shape.
  • Release comparison: stable vs canary per route, per tenant, atau per dependency.

Untuk aplikasi lama, correctness proxy sering lebih penting daripada sekadar error rate. Banyak regresi kompatibilitas tidak memunculkan crash, tetapi memproduksi hasil salah.

Postmortem ringan setelah insiden atau near miss

Jika canary menemukan masalah, anggap itu keberhasilan proses, bukan kegagalan tim. Tujuannya memang menangkap masalah sebelum dampaknya meluas. Namun pembelajaran tetap harus ditangkap lewat postmortem ringan.

Isi postmortem yang cukup

  • Apa yang berubah: binary, config, dependency, routing, flag, schema.
  • Gejala pertama: metrik, log, trace, atau smoke test mana yang pertama mendeteksi masalah.
  • Dampak: traffic terdampak, fitur terdampak, durasi, dan apakah data ikut terpengaruh.
  • Akar masalah sementara: mismatch library, timeout, format data, race condition, permission, driver behavior, dan sebagainya.
  • Mengapa lolos dari pengujian sebelumnya: staging tidak representatif, tidak ada trace, health check terlalu dangkal, test tidak menyentuh jalur itu.
  • Tindakan pencegahan: dashboard baru, smoke test baru, kontrak data lebih ketat, rollback guardrail, atau pembekuan fitur tertentu.

Tindakan pencegahan agar insiden serupa tidak terulang

  • Tambahkan test kompatibilitas untuk jalur runtime yang pernah gagal.
  • Perluas smoke test agar memverifikasi output, bukan hanya status berhasil.
  • Pastikan schema, event, dan cache key dirancang backward-compatible.
  • Tingkatkan log terstruktur dan korelasi trace untuk dependency kritis.
  • Buat dashboard per versi rilis agar perbandingan stable vs canary lebih cepat.
  • Dokumentasikan asumsi environment yang sebelumnya tersembunyi.
  • Kurangi coupling terhadap perilaku dependency yang tidak dijamin kontraknya.

Penutup

Pada proyek porting, deploy aman bukan soal berani merilis cepat, tetapi soal mengendalikan ketidakpastian runtime. Canary release membatasi blast radius, health check yang bermakna mencegah false confidence, observabilitas membantu mendeteksi mismatch yang tidak terlihat saat build, dan rollback cepat menjaga insiden tetap kecil.

Jika tim Anda sedang memindahkan aplikasi lama ke environment baru, anggap setiap keberhasilan startup hanya sebagai langkah awal. Validasi sebenarnya terjadi di production-like runtime: dependency nyata, traffic nyata, dan perilaku yang sering baru terlihat setelah deploy. Karena itu, siapkan checklist, dashboard, smoke test, feature flag, dan runbook rollback sebelum menekan tombol rilis.