Dari Kode ke Paket Siap Jalankan: Apa yang Terjadi Sebelum Deployment

Kamu baru saja selesai menulis fitur baru. Kode berhasil dikompilasi secara lokal, pengujian lolos di mesin kamu, dan kamu merasa percaya diri. Tapi saat kamu push kode dan mencoba melakukan deployment, terjadi masalah. Server mengeluh karena library tidak ditemukan. Binary crash karena dikompilasi di versi OS yang berbeda. Script deployment tidak bisa menemukan artifact yang tepat.

Situasi ini lebih umum dari yang diakui kebanyakan tim. Kesenjangan antara "kode berfungsi di mesin saya" dan "kode berjalan di production" dipenuhi dengan langkah-langkah yang mudah terlewatkan. Memahami apa yang terjadi antara menulis kode dan memiliki paket yang siap di-deploy sangat penting bagi setiap engineer yang terlibat dalam pengiriman perangkat lunak.

Apa Sebenarnya Build Itu?

Ketika seorang developer menulis kode, kode tersebut belum siap dijalankan di server. Kode perlu diubah menjadi sesuatu yang dapat dieksekusi. Proses transformasi itu disebut build. Keluaran dari build disebut artifact.

Artifact hadir dalam dua bentuk umum:

  • File binary: Sebuah file eksekusi tunggal yang bisa langsung dijalankan oleh sistem operasi. Contohnya program Go yang sudah dikompilasi atau file JAR Java.
  • Container image: Sebuah paket yang berisi binary plus semua yang dibutuhkan untuk menjalankannya: library, file konfigurasi, variabel lingkungan, dan runtime itu sendiri. Container image digunakan saat melakukan deployment ke lingkungan seperti Docker atau Kubernetes.

Pilihan antara binary dan container image tergantung pada infrastruktur deployment kamu. Container menawarkan konsistensi yang lebih baik antar lingkungan, sementara binary lebih ringan dan lebih cepat untuk dijalankan. Either way, tujuannya sama: menghasilkan paket mandiri yang bisa di-deploy secara andal.

Empat Tahapan Build

Setiap backend service, terlepas dari bahasa atau framework yang digunakan, melewati tahapan-tahapan ini. Mari kita bahas satu per satu.

Diagram di bawah menunjukkan pipeline build lengkap dari source code hingga artifact yang tersimpan.

flowchart TD A[Source Code] --> B[Compilation] B --> C[Dependency Bundling] C --> D[Artifact Creation] D --> E{Binary or Container?} E --> F[Binary File] E --> G[Container Image] F --> H[Artifact Storage] G --> H H --> I[Registry / Repository]

Tahap 1: Kompilasi

Jika kode kamu ditulis dalam bahasa yang dikompilasi seperti Go, Java, Rust, atau C++, kode tersebut harus dikompilasi menjadi machine code atau bytecode sebelum bisa dijalankan. Bahasa seperti Python, Node.js, atau Ruby melewati langkah ini karena mereka diinterpretasi saat runtime.

Detail kritis di sini adalah konsistensi lingkungan. Jika kamu mengompilasi di laptop developer lalu menyalin binary ke server production, kamu berisiko mengalami masalah kompatibilitas. Server production mungkin memiliki versi sistem operasi yang berbeda, library sistem yang berbeda, atau arsitektur CPU yang berbeda. Selalu kompilasi di lingkungan yang semirip mungkin dengan target production kamu.

Untuk bahasa yang diinterpretasi, kompilasi tidak diperlukan, tetapi kamu tetap perlu menyiapkan lingkungan runtime. Itu mengarah ke tahap berikutnya.

Tahap 2: Bundling Dependensi

Aplikasi backend hampir tidak pernah berjalan hanya dengan kode mereka sendiri. Mereka bergantung pada library eksternal untuk akses database, penanganan HTTP, autentikasi, logging, dan puluhan kebutuhan lainnya. Dependensi ini harus dikumpulkan dan dikemas bersama dengan kode kamu.

Mekanisme pastinya tergantung pada ekosistem bahasa yang digunakan:

  • Python: Jalankan pip install dan kumpulkan semua paket ke folder tertentu atau virtual environment.
  • Node.js: Jalankan npm install dan pastikan direktori node_modules lengkap.
  • Java: Dependensi dibundel di dalam file JAR atau WAR selama proses build.
  • Go: Dependensi dikompilasi langsung ke dalam binary, jadi tidak perlu bundling terpisah.

Prinsipnya sederhana: ketika artifact berjalan di server, semua yang dibutuhkan harus sudah ada di dalamnya. Tidak boleh ada yang harus menginstal dependensi secara manual saat deployment. Itu adalah resep untuk inkonsistensi dan kegagalan.

Tahap 3: Membuat Artifact

Sekarang kamu perlu menghasilkan paket final. Jika tim kamu menggunakan container, ini berarti membangun Docker image menggunakan Dockerfile. Image tersebut berisi binary yang sudah dikompilasi atau kode aplikasi, semua dependensi, konfigurasi dasar, dan perintah untuk memulai aplikasi.

Berikut adalah contoh praktis Dockerfile multi-stage yang mengompilasi aplikasi Go dan menghasilkan container image yang minimal:

# Stage 1: Compilation and dependency bundling
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server .

# Stage 2: Create the final artifact
FROM alpine:3.19
RUN apk --no-cache add ca-certificates tzdata
COPY --from=builder /app/server /server
EXPOSE 8080
CMD ["/server"]

Jika tim kamu tidak menggunakan container, output build adalah file binary atau folder terkompresi yang bisa langsung disalin ke server. Binary lebih sederhana dan lebih cepat untuk di-deploy, tetapi menawarkan isolasi dan portabilitas yang lebih rendah dibandingkan container image.

Either way, artifact harus bersifat immutable. Setelah dibangun, artifact tidak boleh dimodifikasi. Perubahan apa pun berarti build baru dan artifact baru. Ini menjamin bahwa apa yang kamu uji adalah persis apa yang kamu deploy.

Tahap 4: Menyimpan Artifact

Artifact yang sudah jadi membutuhkan tempat penyimpanan permanen di mana proses deployment dapat mengaksesnya. Tempat penyimpanan ini disebut registry atau repository.

  • Container images disimpan di container registry: Docker Hub, Amazon ECR, Google Artifact Registry, atau opsi self-hosted seperti Harbor.
  • File binary disimpan di artifact repository: Nexus, Artifactory, atau bahkan object storage seperti S3.

Setiap artifact harus memiliki pengenal unik. Ini biasanya berupa nomor versi, hash commit Git, atau kombinasi keduanya. Dengan pengenal unik, tim kamu selalu bisa melacak versi kode mana yang berjalan di production. Ketika terjadi masalah, kamu bisa melihat ID artifact dan tahu persis apa yang di-deploy.

Mengapa Otomatisasi Penting

Setiap tahapan ini harus terjadi dengan cara yang sama setiap saat. Jika kamu melakukan build secara manual, pada akhirnya kamu akan melewatkan satu langkah, menggunakan versi dependensi yang salah, atau mengompilasi di mesin yang salah. Hasilnya adalah artifact yang berperilaku berbeda dari yang kamu harapkan.

Pipeline CI mengotomatiskan seluruh proses. Pipeline dipicu pada setiap perubahan kode, menjalankan tahapan build secara berurutan, dan menyimpan artifact dengan pengenal yang tepat. Pipeline memastikan bahwa setiap artifact dibangun dengan cara yang sama, dengan alat yang sama, di lingkungan yang sama.

Konsistensi inilah yang membuat deployment menjadi dapat diprediksi. Ketika kamu melakukan deployment artifact dari pipeline CI, kamu tahu persis apa yang kamu dapatkan. Tidak ada kejutan dari langkah manual atau perbedaan lingkungan.

Checklist Praktis untuk Proses Build Kamu

Sebelum kamu menyiapkan pipeline build, verifikasi poin-poin berikut:

  • Lingkungan build cocok dengan lingkungan production (versi OS, library sistem, arsitektur).
  • Semua dependensi dideklarasikan secara eksplisit (lock files, requirements.txt, go.mod, dll.).
  • Artifact dibangun tepat satu kali per perubahan kode dan disimpan secara immutable.
  • Setiap artifact memiliki pengenal unik yang dapat dilacak (tag versi atau hash commit).
  • Proses build berjalan otomatis pada setiap push atau merge ke branch utama.
  • Artifact registry dapat diakses oleh proses deployment tanpa intervensi manual.

Apa yang Terjadi Selanjutnya

Setelah artifact siap dan tersimpan, pertanyaan selanjutnya adalah apakah aman untuk di-deploy. Di sinilah pengujian otomatis dan pemindaian keamanan berperan. Unit test, integration test, vulnerability scan, dan pemeriksaan lainnya dijalankan terhadap artifact sebelum mencapai production.

Tapi semua pemeriksaan itu tidak berarti jika build itu sendiri rusak. Proses build yang andal adalah fondasi dari setiap pipeline CI/CD. Tanpanya, kamu melakukan deployment berdasarkan tebakan, bukan perangkat lunak.

Kesimpulannya: Build bukan sekadar langkah teknis. Ini adalah momen di mana kode berubah menjadi aset yang siap di-deploy. Perlakukan dengan ketelitian yang sama seperti yang kamu terapkan pada sistem production. Otomatiskan, standarisasi, dan buat setiap artifact dapat dilacak. Dirimu di masa depan, yang sedang debugging masalah production jam 2 pagi, akan berterima kasih.