Saat membangun graph database baru, tantangan CI bukan sekadar “semua test hijau”. Anda perlu memastikan perubahan kecil tidak merusak CLI, server, format output, paket rilis, atau proses build lintas platform. Karena itu, CI untuk Graph DB baru sebaiknya dimulai dari pipeline minimum yang cepat: format/lint, unit test, smoke test CLI/server, build matrix terbatas, pemeriksaan lisensi, dan release flow berbasis tag.

Untuk proyek seperti HelixDB, pendekatan ini memberi dua manfaat langsung: feedback cepat untuk developer dan rilis yang lebih aman. Artikel ini membahas susunan job yang praktis, kriteria lulus/gagal, strategi cache dependency, dan guardrail agar merge tidak merusak branch utama. Fokusnya bukan pada tool tertentu, melainkan pada desain pipeline yang mudah dipelihara oleh tim kecil.

Mengapa CI untuk graph database berbeda dari layanan biasa

Database engine—termasuk graph DB—punya permukaan kegagalan yang lebih luas dibanding aplikasi CRUD biasa. Selain unit test, Anda hampir selalu perlu memverifikasi bahwa binary dapat dibangun, server dapat start, port atau socket dapat merespons, dan query dasar dapat dieksekusi.

Pada graph DB baru, tiga area paling sering rusak adalah:

  • Lapisan parser atau CLI: argumen berubah, help output rusak, subcommand gagal start.
  • Lapisan server/runtime: startup berhasil secara lokal tetapi gagal di CI karena path, permission, atau dependency runtime.
  • Distribusi rilis: binary terbangun, tetapi artefak tidak konsisten penamaannya, tidak terversi dengan benar, atau tidak cocok dengan tag rilis.

Karena itu, pipeline minimum yang berguna harus memeriksa lebih dari sekadar kompilasi.

Desain pipeline minimum yang benar-benar berguna

Untuk tim kecil, urutan yang masuk akal adalah:

  1. Format/lint — paling cepat, gagal lebih awal.
  2. Unit test — validasi logika inti tanpa biaya startup besar.
  3. Smoke test CLI/server — memastikan artefak yang dibangun benar-benar bisa dijalankan.
  4. Build matrix terbatas — memverifikasi target OS/arsitektur penting.
  5. License check — mencegah dependency bermasalah masuk ke branch utama.
  6. Release berbasis tag — hanya berjalan saat versi resmi dibuat.

Prinsip utamanya adalah cheap checks first, expensive checks later. Job yang murah harus menghentikan pipeline sedini mungkin agar waktu CI tidak terbuang.

Contoh struktur job

Struktur berikut cukup umum dan dapat diterapkan di GitHub Actions, GitLab CI, atau sistem CI lain:

stages:
  - verify
  - test
  - package
  - release

jobs:
  format_lint:
    stage: verify
    runs_on: linux

  unit_test:
    stage: test
    needs: [format_lint]
    runs_on: linux

  smoke_cli:
    stage: test
    needs: [format_lint]
    runs_on: linux

  smoke_server:
    stage: test
    needs: [format_lint]
    runs_on: linux

  build_matrix:
    stage: package
    needs: [unit_test, smoke_cli, smoke_server]
    matrix:
      - os: linux
      - os: macos
      - os: windows

  license_check:
    stage: verify
    runs_on: linux

  release:
    stage: release
    needs: [build_matrix, license_check]
    trigger: tag

Tidak semua job harus saling bergantung. Misalnya, license_check bisa berjalan paralel dengan lint agar feedback tetap cepat.

Job inti: format, lint, dan unit test

Format/lint sebagai gerbang pertama

Job ini harus cepat dan deterministik. Tujuannya bukan mencari semua bug, tetapi menangkap masalah yang paling murah diperbaiki:

  • format kode tidak konsisten,
  • import tidak terpakai,
  • warning yang diperlakukan sebagai error,
  • konvensi penamaan atau style yang melanggar standar tim.

Kriteria lulus/gagal untuk job ini sederhana:

  • Lulus: formatter tidak mengubah file, linter selesai tanpa error.
  • Gagal: ada file yang perlu diformat ulang, warning penting muncul, atau static analysis mendeteksi masalah yang dikonfigurasi sebagai blocking.

Jika bahasa yang dipakai mendukung mode check only, gunakan mode itu di CI. Hindari formatter yang menulis ulang file dalam pipeline karena hasilnya tidak kembali ke branch pengembang dan hanya membingungkan.

Unit test: fokus ke logika engine yang tidak butuh runtime penuh

Untuk graph DB, unit test idealnya mencakup:

  • parser query,
  • planner atau evaluator dasar,
  • struktur data graph,
  • encoding/decoding record,
  • error handling dan validasi input.

Jangan campurkan terlalu banyak I/O eksternal di tahap ini. Semakin independen unit test dari jaringan, filesystem berat, dan proses background, semakin cepat feedback yang didapat.

Catatan: Jika project masih sangat muda, coverage rendah tidak otomatis berarti buruk. Yang lebih penting adalah memastikan area paling rapuh—startup, parser, dan operasi query dasar—sudah punya test.

Smoke test CLI dan server: kecil, cepat, tetapi wajib

Smoke test berbeda dari integration test penuh. Tujuannya hanya menjawab pertanyaan: “Apakah binary yang baru dibangun benar-benar bisa dipakai untuk skenario dasar?”

Smoke test CLI

Untuk proyek seperti HelixDB, smoke test CLI sebaiknya memeriksa hal berikut:

  • binary berhasil dijalankan,
  • subcommand --help atau version berfungsi,
  • perintah inisialisasi data directory berjalan,
  • query atau operasi dasar memberi exit code yang benar.

Contoh shell script sederhana:

set -euo pipefail

./bin/helixdb --help > /tmp/help.txt
grep -qi "usage" /tmp/help.txt

./bin/helixdb version
./bin/helixdb init --data-dir /tmp/helixdb-data

test -d /tmp/helixdb-data

Mengapa ini berguna? Karena banyak regresi nyata terjadi pada level paling dasar: binary salah nama, flag berubah, direktori data gagal dibuat, atau packaging tidak menyertakan file yang dibutuhkan.

Smoke test server

Untuk server, targetnya bukan benchmark atau uji beban. Cukup verifikasi bahwa proses bisa start, siap menerima koneksi, dan merespons query minimal.

set -euo pipefail

./bin/helixdb server --data-dir /tmp/helixdb-data --port 7777 > /tmp/helixdb.log 2>&1 &
PID=$!

cleanup() {
  kill $PID || true
}
trap cleanup EXIT

for i in $(seq 1 20); do
  if nc -z 127.0.0.1 7777; then
    break
  fi
  sleep 1
done

nc -z 127.0.0.1 7777
./bin/helixdb query --port 7777 "RETURN 1"

Jika protokol query belum stabil, Anda tetap bisa memakai health endpoint, TCP connect check, atau log readiness sebagai indikasi minimal. Jangan menunggu protokol sempurna untuk mulai menulis smoke test.

Kriteria lulus/gagal smoke test

  • Lulus: proses start dalam batas waktu, port terbuka, query dasar atau health check berhasil, proses dapat dihentikan bersih.
  • Gagal: startup timeout, panic saat inisialisasi, exit code non-zero, port tidak tersedia, atau query dasar gagal.

Tambahkan timeout eksplisit. Tanpa timeout, CI sering macet karena server menunggu input atau deadlock saat startup.

Build matrix: cukup luas untuk aman, cukup sempit untuk cepat

Build matrix penting untuk database engine karena perbedaan OS sering memunculkan bug pada path file, socket, line ending, atau dependency sistem. Namun, matrix yang terlalu besar bisa membuat pipeline lambat dan mahal.

Rekomendasi awal untuk tim kecil

  • Pull request: build dan test penuh di Linux, build saja di macOS/Windows.
  • Tag release: build artefak untuk semua platform yang benar-benar didukung.

Pendekatan ini menjaga feedback harian tetap cepat, tanpa mengabaikan kompatibilitas platform.

matrix:
  include:
    - os: ubuntu-latest
      run_tests: true
    - os: macos-latest
      run_tests: false
    - os: windows-latest
      run_tests: false

Trade-off-nya jelas:

  • Matrix sempit: lebih cepat, lebih murah, tetapi bug platform-spesifik bisa lolos.
  • Matrix lengkap: lebih aman untuk release, tetapi memperlambat iterasi harian.

Untuk proyek baru, prioritas utama biasanya adalah kecepatan iterasi di PR. Lengkapi validasi lintas platform pada event release atau job terjadwal.

Artifact versioning dan release flow berbasis tag

Versi artefak harus dapat ditelusuri

Binary hasil CI sebaiknya menyimpan informasi versi minimal:

  • tag versi jika build berasal dari tag,
  • commit SHA pendek,
  • waktu build jika diperlukan untuk audit internal.

Tujuannya agar saat ada bug dari pengguna atau QA, Anda bisa melacak artefak tepat yang dijalankan.

APP_VERSION=${CI_TAG:-0.0.0-dev}
GIT_SHA=$(git rev-parse --short HEAD)
ARTIFACT_NAME="helixdb-${APP_VERSION}-${TARGET_OS}-${TARGET_ARCH}"

echo "Building ${ARTIFACT_NAME}"

Hindari menamai artefak hanya dengan latest atau nama generik tanpa versi. Itu menyulitkan rollback, debugging, dan validasi hasil distribusi.

Release hanya dari tag

Untuk keamanan rilis, gunakan aturan sederhana: job release hanya boleh berjalan dari tag yang cocok dengan pola versi tim. Contohnya, tag seperti v0.4.0 atau pola lain yang Anda sepakati.

Alurnya bisa seperti ini:

  1. Developer merge ke branch utama setelah semua job wajib lulus.
  2. Maintainer membuat tag versi.
  3. Pipeline release memverifikasi tag, build artefak final, menjalankan smoke test singkat, lalu mengunggah artefak ke halaman release atau registry internal.

Keuntungan pendekatan ini adalah pemisahan yang jelas antara validasi perubahan dan publikasi rilis. Tidak setiap merge harus menghasilkan release publik.

Guardrail untuk release aman

  • Release job hanya jalan pada tag, bukan pada push biasa.
  • Tag hanya boleh dibuat oleh maintainer atau akun terotorisasi.
  • Artefak release harus berasal dari commit yang sama dengan tag.
  • Jalankan smoke test lagi pada artefak final, bukan hanya pada hasil build sementara.

Pemeriksaan lisensi dependency

Pada proyek database, dependency bisa cepat bertambah: parser, networking, serialization, logging, hingga tool build. Pemeriksaan lisensi berguna untuk mencegah library dengan lisensi yang tidak sesuai kebijakan tim masuk terlalu jauh.

Job ini umumnya memeriksa:

  • daftar dependency langsung dan transitif,
  • lisensi yang diizinkan atau dilarang,
  • dependency tanpa metadata lisensi yang jelas.

Kriteria lulus/gagal:

  • Lulus: semua dependency cocok dengan allowlist atau kebijakan lisensi internal.
  • Gagal: ditemukan lisensi terlarang, metadata tidak lengkap, atau sumber paket tidak diketahui.

Trade-off-nya: pemeriksaan lisensi kadang menghasilkan false positive pada paket internal atau metadata lama. Karena itu, simpan allowlist secara eksplisit dalam repository dan review perubahannya seperti kode biasa.

Strategi cache dependency agar feedback tetap cepat

Cache dapat memangkas waktu build secara signifikan, tetapi cache yang salah sering justru membuat CI tidak stabil. Prinsip aman:

  • Cache berdasarkan file lock atau manifest dependency, bukan hanya branch.
  • Pisahkan cache dependency dari cache artefak build jika toolchain Anda sensitif terhadap perubahan compiler.
  • Jangan meng-cache output yang mudah korup atau tidak deterministik jika belum dipahami perilakunya.

Apa yang layak di-cache

  • direktori package manager,
  • registry dependency,
  • hasil kompilasi incremental bila toolchain mendukung dengan aman.

Contoh pola key cache

cache-key: build-${OS}-${HASH_OF_LOCKFILE}
restore-keys:
  - build-${OS}-

Mengapa pola ini bekerja? Karena saat dependency berubah, hash lockfile ikut berubah dan cache lama tidak dipakai secara keliru. Sementara restore-keys membantu memanfaatkan cache terdekat jika cache persis belum tersedia.

Debugging tip: jika build di CI tiba-tiba gagal dengan error aneh yang tidak dapat direproduksi lokal, salah satu langkah pertama adalah menonaktifkan cache sementara untuk memastikan masalah bukan berasal dari cache basi atau korup.

Guardrail agar merge tidak merusak branch utama

Pipeline yang bagus tetap tidak cukup tanpa aturan merge yang jelas. Branch utama harus diperlakukan sebagai jalur yang selalu bisa dibangun.

Guardrail minimum yang disarankan

  • Required checks: format/lint, unit test, smoke test wajib hijau sebelum merge.
  • Protected branch: larang push langsung ke branch utama.
  • Review minimal: setidaknya satu reviewer untuk perubahan pada engine, query layer, atau packaging.
  • Linear history atau merge policy yang konsisten: memudahkan pelacakan regresi.
  • Concurrency control: batalkan job lama di PR yang sama saat commit baru datang, agar runner tidak habis untuk build usang.

Jika tim kecil dan perubahan sering, pertimbangkan juga:

  • menjalankan job berat hanya pada file tertentu yang berubah,
  • memisahkan test cepat di PR dan test lebih lengkap secara terjadwal,
  • memakai label atau trigger manual untuk test yang mahal.

Kriteria branch utama sehat

Branch utama bisa dianggap sehat jika:

  • setiap commit di branch utama dapat dibangun,
  • binary dasar bisa dijalankan,
  • tag release bisa dibuat tanpa perbaikan darurat di menit terakhir.

Itu lebih penting daripada mengejar pipeline sangat kompleks tetapi sering dimatikan karena terlalu lambat.

Pipeline sederhana vs lengkap: mana yang cocok untuk tim kecil?

Kapan pipeline sederhana lebih baik

Pilih pipeline sederhana jika:

  • produk masih dini dan API/query language sering berubah,
  • jumlah maintainer sedikit,
  • waktu build masih menjadi bottleneck utama,
  • Anda butuh guardrail dasar secepat mungkin.

Pipeline sederhana biasanya mencakup lint, unit test, satu smoke test server, satu smoke test CLI, dan build Linux. Ini sudah cukup untuk mencegah banyak kegagalan memalukan.

Kapan pipeline lebih lengkap layak ditambahkan

Tambahkan kompleksitas jika:

  • proyek mulai didistribusikan ke banyak platform,
  • release sudah rutin dan dipakai pihak luar,
  • ketergantungan lisensi makin banyak,
  • biaya bug release lebih tinggi daripada biaya CI yang lambat.

Pipeline lengkap bisa menambah:

  • matrix OS/arsitektur lebih luas,
  • integration test multi-node,
  • packaging installer atau container image,
  • scan keamanan dependency,
  • verifikasi migrasi data atau kompatibilitas format file.

Trade-off utamanya adalah maintenance. Setiap job tambahan berarti lebih banyak sumber kegagalan, lebih banyak waktu runner, dan lebih banyak konfigurasi yang harus dipahami tim.

Template alur CI/CD yang realistis

Jika Anda ingin memulai hari ini, gunakan baseline berikut:

  1. PR pipeline
    • format/lint
    • unit test
    • smoke test CLI
    • smoke test server
    • build Linux wajib
    • build macOS/Windows opsional atau tanpa test
  2. Main branch pipeline
    • semua job PR
    • license check
    • publish artefak internal non-release bila diperlukan
  3. Tag release pipeline
    • verifikasi pola tag
    • build matrix final
    • smoke test artefak final
    • penamaan artefak berversi
    • upload ke release page atau registry

Dengan struktur ini, Anda mendapat kombinasi yang seimbang: feedback cepat saat development dan release aman saat versi resmi dibuat.

Kesalahan umum yang sebaiknya dihindari

  • Semua test dijalankan di semua platform pada setiap PR meski belum perlu, sehingga feedback terlalu lambat.
  • Tidak ada smoke test karena merasa unit test sudah cukup, padahal binary tidak pernah benar-benar dijalankan di CI.
  • Release dari branch utama tanpa tag, membuat pelacakan versi kacau.
  • Cache terlalu agresif hingga bug cache sulit dibedakan dari bug kode.
  • Job wajib terlalu banyak di fase awal, lalu developer mulai mencari jalan pintas untuk melewati CI.

Penutup

CI untuk Graph DB baru tidak harus rumit agar efektif. Untuk proyek seperti HelixDB, pipeline minimum yang paling bernilai biasanya terdiri dari format/lint, unit test, smoke test CLI/server, build matrix terbatas, pemeriksaan lisensi, dan release flow berbasis tag. Susunan ini cukup kecil untuk dipelihara tim kecil, tetapi cukup kuat untuk menjaga branch utama tetap stabil dan rilis tetap dapat dipercaya.

Mulailah dari pipeline yang cepat dan tegas, lalu tambahkan kedalaman hanya ketika risiko produk memang meningkat. Dalam konteks database engine, kemampuan untuk mendeteksi startup gagal, binary rusak, atau artefak salah versi sering jauh lebih berharga daripada menambah banyak job yang jarang dibaca hasilnya.