Mengapa Urutan Deployment Lebih Penting daripada Pipeline Anda
Anda memiliki versi baru aplikasi yang siap. Pipeline berwarna hijau. Tim sedang mengamati. Anda menekan tombol deploy. Beberapa menit kemudian, error mulai muncul di log. Pengguna melaporkan bahwa mereka tidak dapat menyelesaikan pembelian. Tim database mengatakan perubahan skema diterapkan setelah aplikasi mulai berjalan, bukan sebelumnya.
Skenario ini tidaklah aneh. Ini terjadi karena deployment jarang hanya tentang satu aplikasi yang melakukan satu hal. Ini tentang bagaimana aplikasi tersebut terhubung dengan semua hal lain yang menjadi dependensinya. Memahami koneksi-koneksi itu -- dan risiko yang dibawanya -- adalah yang membedakan deployment yang mulus dari insiden produksi.
Dependensi Bukan Hanya Sekadar Kode
Aplikasi modern hampir tidak pernah berjalan sendirian. Ia membaca dari database. Ia memanggil API untuk pemrosesan pembayaran. Ia menggunakan pustaka pihak ketiga untuk mengubah ukuran gambar. Ia bergantung pada antrean pesan untuk mengirim notifikasi. Masing-masing adalah dependensi, dan masing-masing bisa rusak saat Anda men-deploy versi baru.
Dependensi yang paling umum adalah database. Aplikasi Anda menyimpan data pengguna di PostgreSQL atau MySQL. Saat Anda men-deploy versi baru, versi tersebut membaca dan menulis data secara berbeda dari versi lama. Mungkin versi lama menyimpan alamat pengguna dalam satu kolom, dan versi baru memisahkannya menjadi jalan, kota, dan kode pos. Jika versi baru mulai berjalan sebelum skema database diperbarui, ia akan gagal membaca data yang ada. Ini adalah breaking change -- versi lama dan baru tidak kompatibel satu sama lain.
Dependensi juga mencakup API dari tim lain atau layanan eksternal. Bayangkan aplikasi Anda memanggil API pembayaran. Versi baru mengharapkan respons dalam format JSON yang berbeda. Jika API tersebut belum diperbarui, aplikasi Anda akan menerima data yang tidak dapat diuraikan, dan transaksi akan gagal. Masalahnya adalah Anda tidak selalu mengontrol kapan API itu berubah. Tim lain mungkin memiliki jadwal deployment sendiri, dan mereka mungkin tidak tahu bahwa aplikasi Anda bergantung pada format respons tertentu.
Pustaka dan paket pihak ketiga juga merupakan dependensi. Saat Anda memperbarui pustaka dari versi 1.0 ke 2.0, fungsi mungkin telah diganti namanya atau tanda tangannya berubah. Jika aplikasi Anda masih memanggil nama fungsi lama, kode akan error saat runtime. Inilah sebabnya tim yang matang melacak dependensi mereka dengan jelas -- dalam file seperti requirements.txt, package.json, atau go.mod -- dan menguji versi pustaka baru sebelum menggunakannya di produksi.
Risiko Tersembunyi: Urutan Deployment
Dependensi secara langsung memengaruhi urutan di mana Anda men-deploy sesuatu. Jika aplikasi A bergantung pada database yang memerlukan perubahan skema, Anda harus memperbarui database terlebih dahulu, lalu men-deploy aplikasi A. Jika aplikasi B memanggil API dari aplikasi C, Anda harus men-deploy aplikasi C terlebih dahulu, lalu aplikasi B. Urutan ini penting karena jika Anda membaliknya, aplikasi yang baru di-deploy akan mencari data atau layanan yang belum tersedia. Hasilnya adalah error, permintaan yang gagal, atau aplikasi yang benar-benar rusak.
Diagram urutan berikut menunjukkan urutan deployment yang benar dan apa yang terjadi jika dibalik:
Risiko bertambah seiring jumlah dependensi. Semakin banyak layanan yang perlu sinkron, semakin tinggi kemungkinan salah satunya tidak sinkron. Tim yang menangani ini dengan baik memetakan semua dependensi sebelum deployment, mengonfirmasi urutan yang benar, dan menyiapkan rencana rollback jika ada yang tidak cocok. Mereka juga menggunakan teknik seperti backward compatibility -- membuat versi baru tetap berfungsi dengan versi lama -- untuk mengurangi risiko breaking change.
Breaking Change Tidak Selalu Jelas
Breaking change tidak harus dramatis. Bisa saja halus. Misalnya, versi baru aplikasi Anda mungkin mulai mengirimkan bidang tambahan dalam permintaan ke API internal. Layanan penerima mengabaikan bidang yang tidak dikenal, jadi semuanya tampak baik-baik saja. Tetapi beberapa minggu kemudian, layanan penerima itu diperbarui, dan sekarang mengharapkan bidang tambahan tersebut ada. Versi lama aplikasi Anda, yang masih berjalan di beberapa lingkungan, berhenti berfungsi. Kerusakannya tertunda, dan akar penyebabnya sulit dilacak.
Inilah sebabnya pemetaan dependensi bukanlah aktivitas satu kali. Ini perlu diperbarui seiring evolusi sistem. Setiap kali dependensi baru ditambahkan atau dependensi yang ada berubah, urutan deployment dan profil risikonya juga berubah.
Cara Mengurangi Risiko Dependensi
Ada langkah-langkah praktis yang dapat Anda ambil untuk membuat deployment lebih aman saat melibatkan dependensi.
Pertama, dokumentasikan dependensi Anda. Ini tidak berarti menulis dokumen panjang yang tidak dibaca siapa pun. Ini berarti memiliki catatan yang jelas dan dapat dibaca mesin tentang apa yang menjadi dependensi aplikasi Anda dan apa yang bergantung padanya. Alat seperti grafik dependensi, katalog layanan, atau bahkan README sederhana di repositori Anda dapat membantu.
Kedua, uji integrasinya, bukan hanya unitnya. Pengujian unit yang mengejek (mock) setiap layanan eksternal tidak akan menangkap masalah yang disebabkan oleh perilaku dependensi nyata. Pengujian integrasi yang berjalan terhadap database, API, atau antrean pesan yang sebenarnya akan memunculkan masalah sebelum mencapai produksi.
Ketiga, gunakan feature flag atau API versi untuk mempertahankan backward compatibility. Jika versi baru Anda masih dapat melayani permintaan dari klien lama, Anda memiliki lebih banyak fleksibilitas dalam urutan deployment. Anda dapat men-deploy versi baru terlebih dahulu, memverifikasinya berfungsi, lalu memperbarui layanan yang bergantung.
Keempat, latih rollback Anda. Ketahui dengan tepat apa yang perlu terjadi jika deployment gagal karena ketidakcocokan dependensi. Bisakah Anda mengembalikan perubahan skema database? Bisakah Anda mengarahkan aplikasi kembali ke versi API lama? Memiliki rencana rollback yang teruji mengurangi tekanan selama deployment.
Sebagai contoh, skrip deployment sekuensial sederhana berikut menerapkan urutan yang benar dan berhenti jika gagal:
#!/bin/bash
# deploy.sh - Menerapkan urutan deployment yang benar
set -e # Keluar jika terjadi error
echo "Langkah 1: Deploy skema database"
./deploy_database.sh || { echo "Deploy database gagal. Membatalkan."; exit 1; }
echo "Langkah 2: Deploy backend API"
./deploy_api.sh || { echo "Deploy API gagal. Melakukan rollback database..."; ./rollback_database.sh; exit 1; }
echo "Langkah 3: Deploy frontend"
./deploy_frontend.sh || { echo "Deploy frontend gagal. Melakukan rollback API dan database..."; ./rollback_api.sh; ./rollback_database.sh; exit 1; }
echo "Semua deployment berhasil diselesaikan."
Daftar Periksa Praktis Sebelum Deployment Berikutnya
Sebelum Anda men-deploy, jalankan daftar periksa singkat ini:
- Sudahkah Anda mendaftar setiap dependensi yang digunakan aplikasi Anda (database, API, pustaka)?
- Tahukah Anda urutan deployment yang benar untuk setiap dependensi?
- Sudahkah Anda menguji versi baru terhadap versi aktual dari dependensi tersebut?
- Apakah ada rencana rollback untuk setiap dependensi yang mungkin rusak?
- Sudahkah Anda mengomunikasikan urutan deployment ke setiap tim yang memiliki dependensi?
Daftar periksa ini tidak lengkap, tetapi mencakup titik kegagalan yang paling umum. Jika Anda dapat menjawab ya untuk kelima pertanyaan, posisi Anda jauh lebih baik daripada kebanyakan tim.
Intisari
Deployment bukan hanya tentang mendorong kode. Ini tentang mengelola hubungan antara aplikasi Anda dan semua yang disentuhnya. Dependensi menentukan urutan deployment, risiko kegagalan, dan kesulitan pemulihan. Tim yang memahami dependensinya dan merencanakannya akan memiliki lebih sedikit insiden, pemulihan yang lebih cepat, dan lebih percaya diri dalam setiap rilis. Pipeline itu penting, tetapi peta dependensi lah yang mencegah pipeline berubah menjadi fire drill.