Merilis Perubahan Frontend Tanpa Merusak Segalanya

Kamu baru saja mendorong versi baru frontend-mu. Build berhasil, pengujian hijau, dan pipeline deployment mengatakan "sukses." Tapi saat kamu memeriksa metrik produksi, ada yang tidak beres. Beberapa pengguna melihat tata letak yang rusak. Yang lain melaporkan bahwa sebuah tombol tidak berfungsi. Dan beberapa pengguna masih menggunakan versi lama, karena browser mereka belum mengambil file baru.

Inilah realitas rilis frontend. Tidak seperti layanan backend di mana kamu bisa me-restart server dan langsung mengganti versi, kode frontend hidup di browser pengguna. Kamu tidak bisa memaksa setiap pengguna untuk memuat ulang. Kamu tidak bisa mengontrol kapan cache mereka kedaluwarsa. Dan jika terjadi kesalahan, kamu tidak bisa begitu saja menekan tombol rollback dan berharap semua orang mendapatkan perbaikan dengan segera.

Mengapa Rilis Frontend Berbeda

Masalah utamanya adalah aset frontend didistribusikan ke ribuan browser, masing-masing dengan status cache-nya sendiri. Saat kamu menyebarkan versi baru, beberapa pengguna langsung mendapatkannya, beberapa mendapatkannya setelah cache mereka kedaluwarsa, dan beberapa mungkin terjebak dengan versi yang rusak sampai mereka me-refresh secara manual.

Untuk frontend statis yang disajikan melalui CDN, tantangannya adalah kamu tidak menyebarkan ke server yang kamu kendalikan. Kamu mengunggah file ke bucket penyimpanan dan membiarkan CDN mendistribusikannya. CDN mungkin menyajikan file lama ke beberapa pengguna dan file baru ke pengguna lain, tergantung pada header cache dan perilaku edge node.

Untuk frontend yang di-render di sisi server (SSR), tantangannya berbeda. Kamu berurusan dengan server aktual yang perlu diperbarui tanpa memutus koneksi aktif. Pengguna yang mengisi formulir atau berpindah antar halaman seharusnya tidak kehilangan sesi mereka karena kamu mengganti versi.

Diagram alir berikut membantu kamu memilih strategi rilis yang tepat berdasarkan tipe frontend dan toleransi risiko:

flowchart TD A[Jenis frontend?] --> B[Statis] A --> C[SSR] B --> D[Rilis bertahap via CDN] D --> E[Pantau error] E --> F{Error rendah?} F -->|Ya| G[Tambah traffic] F -->|Tidak| H[Rollback: ganti pointer] G --> I[Rilis 100%] C --> J{Toleransi risiko?} J -->|Rendah| K[Canary release] J -->|Sangat rendah| L[Blue-green deployment] K --> M[Deploy ke sedikit instance] M --> N[Pantau kesehatan] N --> O{Sehat?} O -->|Ya| P[Tambah instance bertahap] O -->|Tidak| Q[Hapus instance baru] L --> R[Deploy env green] R --> S[Alihkan load balancer] S --> T{Ada masalah?} T -->|Ya| U[Kembali ke blue] T -->|Tidak| V[Pertahankan green]

Rilis Bertahap untuk Frontend Statis

Cara teraman untuk merilis frontend statis adalah dengan mengontrol berapa banyak pengguna yang melihat versi baru. Sebagian besar CDN mendukung distribusi tertimbang, di mana kamu dapat mengirim persentase permintaan ke file baru dan sisanya ke file lama.

Berikut cara kerjanya:

  1. Unggah versi baru ke bucket penyimpanan dengan jalur unik, seperti app-v2/ atau menggunakan nama file dengan hash konten.
  2. Konfigurasikan CDN untuk mengarahkan 5% permintaan ke file baru.
  3. Pantau tingkat error, waktu muat halaman, dan masalah yang dilaporkan pengguna.
  4. Jika semuanya terlihat baik, tingkatkan persentase secara bertahap menjadi 25%, lalu 50%, lalu 100%.

Kamu juga dapat menggunakan feature flag di sisi klien. Sajikan shell aplikasi yang sama untuk semua orang, tetapi muat komponen atau fitur baru secara kondisional berdasarkan cookie, parameter URL, atau ID pengguna. Ini memberi kontrol yang lebih terperinci tentang siapa yang melihat apa, tanpa perlu pemisahan traffic di tingkat CDN.

Keuntungan utama dari rilis bertahap adalah kamu dapat berhenti kapan saja. Jika tingkat error melonjak di 10%, kamu turunkan persentase kembali ke nol. Pengguna yang sudah memuat versi baru mungkin masih melihat masalah, tetapi kamu telah membatasi radius ledakan.

Canary Release untuk Frontend SSR

Untuk frontend SSR, rilis bertahap bekerja seperti deployment canary di backend. Kamu menjalankan beberapa instance aplikasi di belakang load balancer. Saat kamu ingin merilis versi baru, deploy ke satu atau dua instance sementara sisanya tetap menjalankan versi lama.

Load balancer dikonfigurasi untuk mengirim persentase kecil traffic ke instance baru. Jika health check gagal atau tingkat error meningkat, instance tersebut dihapus dari rotasi. Rollback semudah mematikan instance baru dan membiarkan semua traffic kembali ke instance lama.

Pendekatan ini bekerja dengan baik karena aplikasi SSR mempertahankan status di sisi server. Sesi pengguna terikat ke instance tertentu, jadi kamu perlu memastikan data sesi disimpan di lokasi bersama seperti Redis atau database. Jika tidak, mengalihkan traffic antar versi akan membuat pengguna logout.

Untuk mengotomatiskan proses ini, kamu dapat mendefinisikan canary rollout di konfigurasi deployment. Berikut contoh menggunakan Argo Rollouts di Kubernetes:

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: frontend-ssr
spec:
  replicas: 10
  strategy:
    canary:
      steps:
        - setWeight: 10
        - pause: { duration: 5m }
        - setWeight: 50
        - pause: { duration: 5m }
        - setWeight: 100
      analysis:
        templates:
          - templateName: success-rate
        startingStep: 0
        args:
          - name: service-name
            value: frontend-ssr
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  metrics:
    - name: error-rate
      successCondition: result < 0.01
      provider:
        prometheus:
          query: |
            sum(rate(http_requests_total{status=~"5.."}[5m])) /
            sum(rate(http_requests_total[5m]))

Konfigurasi ini mengarahkan 10% traffic ke versi baru, menunggu 5 menit untuk memantau tingkat error, lalu melanjutkan ke 50% dan akhirnya 100% hanya jika tingkat error tetap di bawah 1%.

Blue-Green Deployment untuk Zero Downtime

Jika kamu perlu menjamin bahwa tidak ada pengguna yang mengalami downtime selama rilis, blue-green deployment adalah pilihan yang solid. Kamu memelihara dua lingkungan yang identik: blue (versi saat ini) dan green (versi baru). Semua traffic menuju blue. Setelah green siap dan lulus semua health check, kamu alihkan load balancer untuk mengirim semua traffic ke green.

Jika terjadi kesalahan setelah pengalihan, kamu flip kembali ke blue. Seluruh proses memakan waktu detik, dan pengguna tidak melihat gangguan selama status sesi ditangani dengan benar.

Kendalanya adalah kamu membutuhkan infrastruktur dua kali lipat. Untuk tim kecil atau aplikasi dengan traffic rendah, ini mungkin berlebihan. Tapi untuk sistem produksi dengan traffic tinggi di mana downtime beberapa detik pun merugikan uang atau kepercayaan, blue-green sepadan dengan biaya overhead.

Tantangan Nyata: Rollback Frontend Statis

Di sinilah segalanya menjadi rumit. Rollback frontend statis secara teknis sederhana tetapi praktisnya berantakan. Kamu dapat mengembalikan file di CDN ke versi lama, tetapi pengguna yang sudah mengunduh file baru tidak akan otomatis mendapatkan file lama. Cache browser mereka masih menyimpan versi baru, dan akan terus menyajikannya sampai cache kedaluwarsa.

Solusinya adalah jangan pernah menimpa file. Setiap versi harus memiliki jalur unik, biasanya berdasarkan hash konten. Saat kamu melakukan deploy, unggah file baru di samping file lama. CDN menyajikan versi yang ditunjuk oleh aplikasi. Untuk rollback, kamu mengubah pointer dari versi baru ke versi lama. Pengguna yang sudah memiliki cache versi baru akan terus menggunakannya sampai cache mereka bersih, tetapi pengguna baru dan pengguna yang me-refresh akan mendapatkan versi lama.

Pendekatan ini berarti kamu perlu memikirkan rollback sebelum kamu membutuhkannya. Jika pipeline kamu dirancang untuk menimpa file di tempat, kamu sedang mempersiapkan pemulihan yang menyakitkan ketika terjadi kesalahan.

Daftar Periksa Praktis untuk Rilis Frontend

Sebelum kamu mendorong rilis berikutnya, jalankan daftar periksa singkat ini:

  • Apakah semua jalur file unik per versi (berdasarkan hash konten atau versi)?
  • Bisakah kamu mengarahkan persentase kecil traffic ke versi baru?
  • Apakah kamu memiliki cara untuk memantau tingkat error dan waktu muat halaman secara real-time?
  • Apakah rencana rollback-mu sudah diuji, bukan hanya didokumentasikan?
  • Untuk SSR: apakah status sesi disimpan di luar server aplikasi?
  • Untuk statis: apakah kamu memiliki mekanisme untuk mengganti pointer versi tanpa melakukan deploy ulang?

Yang Paling Penting

Rilis bertahap dan strategi rollback bukanlah fitur yang bisa kamu tambahkan setelah pipeline dibangun. Mereka perlu dirancang ke dalam proses deployment sejak awal. Jika pipeline kamu hanya tahu cara men-deploy satu versi ke satu lokasi, kamu akan kesulitan melakukan canary release atau blue-green deployment. Jika kamu tidak memiliki manajemen traffic di tingkat CDN atau load balancer, kamu tidak punya cara untuk membatasi dampak dari rilis yang buruk.

Tujuannya bukan untuk mencegah semua masalah. Masalah akan terjadi. Tujuannya adalah untuk memastikan bahwa ketika masalah terjadi, kamu dapat merespons tanpa panik. Strategi rilis yang dirancang dengan baik memberi kamu kendali atas radius ledakan, jalur rollback yang jelas, dan kepercayaan diri untuk mengirimkan perubahan secara sering. Tanpa itu, setiap rilis terasa seperti perjudian.