Skip to content
Workflow DevOps

Release Workflow: Development to Production

By Romi Fadli, S.Kom. 9 min read Environments & Deployment
On this page
Three stages in a row — development, staging and production — with an image passing from development to staging, configuration promoted from staging to production, staging fed with real data restored from a production backup, and production marked done once its core function is proven

Release Workflow: Development to Production adalah prosedur yang ditempuh tiap perubahan saya, dari laptop tempat ia ditulis sampai environment production tempat orang memakainya. Prosedur ini berjalan tiap kali versi baru salah satu service siap, dan baru selesai ketika sistem production sudah sekali menjalankan fungsi intinya dengan data sungguhan.

Halaman ini bukan tutorial deployment. Isinya cara saya berpikir tentang pekerjaan yang sering saya ulang: langkah mana yang saya otomatiskan, mana yang sengaja saya serahkan ke manusia, dan kenapa tiap langkah berada di tempatnya.

Sekilas

PemicuVersi baru salah satu service siap dirilis
FrekuensiSekali per rilis, untuk satu atau beberapa service
MasukanKode yang berubah, backup production terbaru, akses ke environment staging
HasilProduction menjalankan versi baru, dengan konfigurasi dan database yang sudah dimigrasikan
Selesai bilaProduction sudah sekali menjalankan fungsi intinya dari awal sampai akhir dengan data sungguhan
Kapan workflow ini berjalan, apa yang dibutuhkannya, dan bagaimana saya tahu ia selesai.

Pekerjaan berulangnya

Rilis bukan satu tindakan, melainkan rangkaian tindakan, dan tiap mata rantainya bisa gagal dengan cara yang baru tampak belakangan: image yang sudah diuji tapi tak pernah diterbitkan, file konfigurasi yang tersalin menimpa secret production, migrasi database yang diam-diam tak berjalan, sistem yang melaporkan dirinya sehat padahal fitur utamanya rusak. Mengerjakannya dengan tangan sekali masih bisa. Mengerjakannya untuk tiap rilis tiap service, dengan jarak berbulan-bulan, adalah cara langkah-langkahnya terlewat.

Karena itu saya menjadikan rilis sebagai prosedur dengan urutan tetap. Tiap langkah menyingkirkan satu risiko sebelum langkah berikutnya sempat mengubahnya menjadi gangguan, dan langkah yang gampang keliru dijadikan script, bukan ingatan.

Alurnya

  1. Kembangkan di laptop dengan domain asli, periksa layanan luar, jalankan test.
  2. Bangun image, lalu terbitkan sebagai langkah terpisah.
  3. Lahirkan environment staging dari backup production terbaru, lalu uji rilisnya di sana.
  4. Promosikan konfigurasi ke production — dry run lebih dulu.
  5. Nyalakan production, migrasikan, dan periksa kesehatannya.
  6. Buktikan fungsi inti sekali dengan data sungguhan, lalu bebaskan atau hapus staging.

Langkah dan keputusan di baliknya

  1. Kembangkan dengan domain asli. Laptop saya melayani domain yang sama dengan sertifikat yang sama seperti production, jadi cookie, redirect, dan callback berperilaku di development persis seperti nanti di production. Saya memutuskan meniru production apa adanya di lokal, bukan sekadar mendekatinya, karena bug yang lahir dari nama yang berbeda baru muncul sesudah rilis, di tempat yang paling buruk untuk men-debug-nya.

  2. Periksa layanan luar sebelum kode bergantung padanya. Ketika rilis mulai memakai storage server, identity provider, atau akun email, saya menjalankan pemeriksaan kesiapan atas credential-nya lebih dulu. Kegagalan yang tertangkap di sini jelas milik layanannya; kegagalan yang sama bila tertangkap sesudah rilis tampak seperti bug di kode saya.

  3. Jalankan test otomatis. Test wajib lolos sebelum image dibangun. Image yang tak lolos test-nya sendiri bukan calon rilis, jadi tak ada alasan menghabiskan sisa prosedur untuknya.

  4. Bangun image, lalu terbitkan secara terpisah. Tiap service punya nomor versinya sendiri, jadi rilis biasanya hanya membangun ulang service yang berubah. Script build menaikkan versi dan membangun image-nya, tapi sengaja tidak mem-push-nya. Penerbitan adalah langkah tersendiri yang terlihat, karena uji atas image yang tak pernah diterbitkan tak membuktikan apa pun tentang yang akan dijalankan server.

    bash
    build <service>                   # naikkan versi, bangun image — tanpa push
    docker push <image>:<version>     # penerbitan adalah langkahnya sendiri
  5. Lahirkan staging dari backup production terbaru. Satu perintah merakit stack untuk versi ini, memulihkan data dari backup, menyalakan service-service-nya, menjalankan migrasi database, lalu memeriksa kesehatannya. Datanya berasal dari backup, tak pernah langsung dari production, jadi staging bisa dilahirkan ulang sesering apa pun tanpa menyentuh production. Memakai backup dengan cara ini juga membuktikan, tiap kali rilis, bahwa backup-nya memang bisa dipulihkan.

  6. Uji rilisnya di staging. Staging memegang data sungguhan dan menjalankan image yang sudah diterbitkan, tak pernah image lokal, jadi uji di sana menjalankan persis yang akan dijalankan production terhadap jenis data yang sama. Langkah ini tetap manual: memutuskan bahwa rilis berperilaku benar adalah penilaian, bukan pemeriksaan yang bisa dikerjakan script.

  7. Promosikan konfigurasi — dry run lebih dulu. Konfigurasi tidak disalin dari staging ke production. Script promosi memindahkan strukturnya: key yang ditambah atau dibuang, serta nilai yang bukan rahasia. Secret dan alamat tetap milik production sendiri, secret yang hanya ada di staging menghentikan script sampai saya mengisi nilai production-nya dengan tangan, dan tiap file yang ditulisnya di-backup lebih dulu. Dry run memperlihatkan tiap perubahan sebelum apa pun ditulis.

    bash
    promote --from staging/<version> --dry-run   # tampilkan yang akan berubah
    promote --from staging/<version>             # lalu terapkan
  8. Nyalakan production, migrasikan, dan periksa kesehatannya. Migrasi yang sama seperti di staging dijalankan secara eksplisit, diikuti pemeriksaan kesehatan yang sama. Migrasi yang tak dijalankan service saat ia menyala mendapat langkahnya sendiri, supaya perubahan skema tak pernah bisa bersembunyi di balik container yang menyala dengan mulus.

  9. Buktikan fungsi inti sekali. Container yang sehat tidak membuktikan bahwa sistemnya mengerjakan tugasnya. Rilis belum selesai sampai production menjalankan fungsi intinya sekali, dari awal sampai akhir, dengan data sungguhan — di platform tanda tangan dokumen, misalnya, berarti menandatangani satu dokumen sungguhan.

  10. Bebaskan atau hapus staging. Begitu pengujian selesai, staging dihentikan supaya memori server lega. Ketika versi itu tak lagi dibutuhkan, ia dihapus seluruhnya: container, folder, state, dan konfigurasi web server, supaya environment lama tak menumpuk tanpa disadari.

Yang saya otomatiskan, dan yang sengaja tidak

LangkahOtomatis atau manualAlasan
Pemeriksaan kesiapan, testotomatispertanyaannya sama tiap kali; mesin mengajukannya lebih cepat dan tak pernah melewatkan satu pun
Buildotomatismenaikkan versi dan membangun image harus identik di tiap rilis
Penerbitanmanual, disengajapush image adalah titik tak bisa kembali untuk yang dijalankan server; ia harus jadi tindakan sadar
Melahirkan stagingotomatis, satu perintahrestore, nyalakan, migrasi, dan periksa terlalu banyak untuk diulang dengan tangan tanpa ada yang terlewat
Uji di stagingmanualbenar tidaknya perilaku rilis adalah penilaian
Promosi konfigurasiotomatis, dengan dry runmenyalin file dengan tangan adalah cara secret tertimpa; dry run membuat saya tetap ikut memutuskan
Secret barumanualnilai secret harus dipilih untuk production, tak pernah diwarisi dari staging
Migrasi dan pemeriksaan kesehatanotomatismigrasi yang terlupa gagal tanpa suara
Membuktikan fungsi intimanualhanya transaksi sungguhan yang membuktikan rilisnya; inilah definisi selesai
Menghapus stagingotomatis, satu perintahbersih-bersih gampang terlupa dan melelahkan bila dikerjakan tuntas
Tiap langkah, apakah ia berjalan sendiri, dan alasan di balik pilihan itu.

Saat gagal

Sebelum memilih jalan kembali, saya mencari tahu persis di mana rilisnya patah, dan tempat saya mencari bergantung pada cara ia gagal.

Bila production tak menyala atau tak sehat, penyebabnya hampir selalu tercetak di tempat kegagalan itu terjadi: di keluaran terminal saat promosi dan saat menyalakan service-service-nya. Variable yang hilang, migrasi yang berhenti, container yang keluar — semua itu tampak di sana, bukan di log sistem yang memang tak pernah menyala.

Bila semuanya menyala dan sehat tapi ada fitur yang gagal, misalnya tanda tangan yang tak tuntas, saya menelusuri request-nya lewat log dan trace yang dikirim service-service ke OpenTelemetry Collector lalu ke Elasticsearch, untuk menemukan titik persis tempat ia keliru.

Baru sesudah itu saya memilih cara pulih, sesuai yang ditunjukkan diagnosisnya:

  • Jalankan image versi sebelumnya bila kode barunya yang salah dan datanya utuh.
  • Pulihkan dari backup bila migrasi sudah terlanjur mengubah atau merusak data.
  • Perbaiki maju (fix forward) bila masalahnya kecil dan jelas: betulkan, bawa lewat staging, lalu rilis versi baru.

Kenapa urutan ini

Urutan ini memindahkan tiap risiko ke tempat yang paling murah untuk menghadapinya. Masalah layanan luar tertangkap di laptop saya, sebelum ada kode yang bergantung padanya. Data uji berasal dari backup, jadi staging bisa dilahirkan ulang dengan bebas dan backup-nya ikut terbukti. Konfigurasi dipromosikan menurut strukturnya, jadi rilis tak pernah bisa menimpa secret production. Migrasi dijalankan secara eksplisit di kedua sisi, jadi perubahan skema tak bisa bersembunyi di balik pemeriksaan kesehatan yang hijau. Dan langkah terakhirnya transaksi sungguhan, karena itulah satu-satunya alasan sistem itu ada.

Variasi

Hanya satu service yang berubah. Inilah kasus yang biasa. Hanya service itu yang dibangun dan diterbitkan dengan versi baru; service lain tetap di versi yang sudah dijalankannya, dan staging dilahirkan dengan kombinasi itu.

Perbaikan darurat. Hotfix menempuh jalur yang sama, bukan jalan pintas di sekitarnya. Yang dipersingkat adalah uji di staging, yang hanya mencakup bagian yang diperbaiki, bukan seluruh rilis. Melewati staging sepenuhnya menghemat beberapa menit dan mempertaruhkan persis jenis kegagalan yang hendak dicegah prosedur ini.

Istilah

Staging
Environment yang meniru production, lengkap dengan salinan datanya, dipakai untuk menguji rilis sebelum ia tayang.
Image
Salinan service yang dikemas dan diberi versi, lengkap dengan semua yang dibutuhkannya untuk berjalan, tempat container dinyalakan.
Migrasi
Perubahan skema database yang diberi versi, dijalankan berurutan dan dicatat supaya hanya berjalan sekali.
Dry run
Menjalankan script sehingga ia melaporkan apa yang akan diubahnya tanpa mengubah apa pun.
Pemeriksaan kesehatan
Uji otomatis bahwa service sudah menyala dan bisa menjawab, yang tak mengatakan apa pun tentang berfungsi tidaknya fitur-fiturnya.
Fix forward
Pulih dari rilis yang gagal dengan merilis versi yang sudah dibetulkan, bukan kembali ke versi sebelumnya.

Technologies covered

  • Docker

About the author

Photo of Romi Fadli

Romi Fadli, S.Kom.

Solution Architect · Backend Engineer · Embedded Systems

In my day job I work as a solution architect. I design the system, lead the team that builds it — quality assurance, backend developers, a UI developer, DevOps and DevSecOps — and write the most critical modules myself, such as a certificate authority, a long-term PDF signer, and the core API of a personnel system.

I work across the whole stack that a running system actually needs. On the hardware side that means PCB design, microcontrollers, and embedded firmware — including RISC-V boards running FreeRTOS and Embassy, speaking MQTT over mutually authenticated TLS on a microcontroller with 64 KB of SRAM.

Most of my day-to-day work is software: Rust backends and API modules built on Clean Architecture, an OAuth2/OpenID Connect authorization server written from scratch, PAdES document signing, and certificate authorities — root CAs, timestamping authorities, and OCSP responders. I also build the frontends that sit on top of them.

Running all of that means infrastructure: Docker Compose stacks, networking, Mikrotik and RADIUS, observability with OpenTelemetry, and a steady habit of writing command-line tools and utilities for Linux when the existing ones do not fit.

Share

Enjoyed this?

Get an email when a new article is published.

Referenced by

Three image layer stacks, each with a version tag of its own colour, blue, orange, and green, each leading to an archive file banded in the same colour

Perintah yang memberi versi tersendiri pada tiap modul stack, mem-build image-nya di bawah tag yang berarti tepat satu build, menolak memakai ulang versi atau menelan compile yang gagal, dan mengarsipkan image menurut nama dan tag supaya versi tak saling menimpa.

7 min read Read more →
Four identical grey keys on the left each become a key of its own colour on the right, every new key marked with a green check; below, a purple key stays behind a red bar

Satu perintah yang merotasi kredensial internal satu environment kunci demi kunci: dry run sebagai default, backup lebih dulu, nilai unik untuk tiap kunci, login sungguhan untuk membuktikan tiap kredensial yang bisa dipakai login, dan rollback bagi yang gagal.

8 min read Read more →
A browser window sends one orange path into a single web server, which routes it on to the orange staging environment; dashed paths to the blue development and green production environments stay inactive

Dua perintah yang menjaga tiga environment tetap terpisah di satu mesin dan satu web server: satu menetapkan alamat environment dan menyelaraskan setiap file yang menyimpannya, merender nginx dari template dan menolak bila bentrok; satu lagi mengarahkan browser ke satu environment.

11 min read Read more →
A staging configuration with an orange banner and a production configuration with a green banner; ordinary keys cross over with arrows, while a locked secret is stopped halfway by a red cross and production keeps its own lock

Tiga perintah yang membawa rilis teruji ke production tanpa menimpa apa yang dimilikinya: mempromosikan struktur konfigurasi tapi mempertahankan rahasia dan alamatnya, menolak menyalin rahasia baru, dan menerapkan migration yang dibawa image.

10 min read Read more →
Three sources on the left — configuration, container images, and a backup — flowing into one staging environment with an orange banner, its services, and a healthy check mark

Tiga perintah yang membangun salinan penuh production untuk satu rilis — konfigurasi dari repository, image dari registry, data dari backup terbaru — me-restore-nya berurutan, menjalankan migration rilis terhadap data itu, dan membuangnya tanpa jejak.

13 min read Read more →
A database, an object storage bucket, and a telemetry store on the left, each flowing into a stack of restore points on the right, the front one showing a timeline and a check mark

Tiga perintah yang mem-backup stack Docker yang berjalan — PostgreSQL, MinIO, dan Elasticsearch — tanpa downtime, me-restore-nya ke titik mana pun, dan menjaga retensi tetap aman, dengan exit code yang menyatakan bobot kegagalannya.

12 min read Read more →
A purple root circle linked to a blue and an orange intermediate circle, which lead to a signed document with a green seal and to a clock.

Aurograf

DevOps

Platform tanda tangan digital Pemerintah Kota Tangerang Selatan: penanda tangan membuktikan identitasnya, menandatangani PDF hingga PAdES B-LTA dengan sertifikat dari otoritas milik platform sendiri, dan siapa pun bisa memverifikasinya — versi 2, yang saya rancang, bangun, dan operasikan.

10 min read Read more →
A terminal window listing passing checks, connected to a blue storage bucket with a padlock and a green storage bucket without one.

Satu script Python yang membuktikan storage kompatibel S3 siap sebelum aplikasi bergantung padanya, dengan memakai tiap bucket persis seperti client sungguhan dan menangkap kegagalan yang tetap menjawab HTTP 200.

12 min read Read more →

Related Articles

Three sources on the left — configuration, container images, and a backup — flowing into one staging environment with an orange banner, its services, and a healthy check mark

Tiga perintah yang membangun salinan penuh production untuk satu rilis — konfigurasi dari repository, image dari registry, data dari backup terbaru — me-restore-nya berurutan, menjalankan migration rilis terhadap data itu, dan membuangnya tanpa jejak.

13 min read Read more →
A database, an object storage bucket, and a telemetry store on the left, each flowing into a stack of restore points on the right, the front one showing a timeline and a check mark

Tiga perintah yang mem-backup stack Docker yang berjalan — PostgreSQL, MinIO, dan Elasticsearch — tanpa downtime, me-restore-nya ke titik mana pun, dan menjaga retensi tetap aman, dengan exit code yang menyatakan bobot kegagalannya.

12 min read Read more →