Regression gate yang tahan flaky test di CI bukan berarti melonggarkan standar kualitas, tetapi memisahkan sinyal regresi yang nyata dari noise test yang tidak stabil. Tujuannya sederhana: merge tetap lancar untuk perubahan yang sehat, sementara bug yang benar-benar berisiko tetap tertahan sebelum masuk ke rilis.
Banyak tim awalnya memilih dua ekstrem yang sama-sama buruk: semua test harus hijau tanpa pengecualian, atau flaky test dibiarkan hidup terlalu lama sampai hasil CI tidak lagi dipercaya. Keduanya mahal. Regression gate yang baik perlu mengenali jenis kegagalan, memberi bobot berbeda pada test di critical path, dan memiliki kebijakan operasional yang jelas untuk retry, quarantine, dan verifikasi sebelum release.
Ada sisi reflektif di sini. Saat komunitas teknologi mengenang figur-figur yang berpulang, yang tertinggal bukan sekadar produk, tetapi juga disiplin engineering yang membuat sistem tetap dapat dipercaya setelah orangnya tidak lagi ada. Regression gate yang rapi adalah bagian dari warisan itu: keputusan teknis yang mengurangi kebisingan dan menjaga kualitas dalam jangka panjang.
Mengapa flaky test merusak regression gate
Flaky test adalah test yang kadang lulus, kadang gagal, tanpa perubahan relevan pada kode yang diuji. Jika test seperti ini ikut menentukan boleh tidaknya merge, CI kehilangan fungsi utamanya sebagai alat pengambilan keputusan.
Dampaknya biasanya muncul dalam tiga bentuk:
- False positive regression: pipeline merah, padahal produk tidak rusak.
- Alert fatigue: engineer terbiasa mengulang job tanpa investigasi.
- Trust collapse: hasil CI tidak lagi dijadikan sinyal kualitas, sehingga bug nyata lebih mudah lolos.
Masalah ini bukan hanya teknis, tetapi juga operasional. Begitu tim menganggap “merah belum tentu masalah”, regression gate praktis mati walaupun pipeline masih berjalan.
Klasifikasi flaky test yang perlu dikenali
Jangan memperlakukan semua flaky test sebagai satu kategori. Penyebab yang berbeda perlu penanganan yang berbeda. Klasifikasi berikut cukup praktis untuk sebagian besar tim.
1. Flaky karena waktu dan concurrency
Contohnya race condition, timeout terlalu agresif, asumsi urutan eksekusi, atau proses background yang belum selesai saat assertion dijalankan. Gejalanya sering muncul acak di mesin CI yang lebih sibuk daripada lingkungan lokal.
Penanganan umum:
- Gunakan sinkronisasi yang eksplisit, bukan sleep statis.
- Pastikan assertion menunggu kondisi yang benar-benar relevan.
- Kurangi ketergantungan pada urutan eksekusi test.
2. Flaky karena ketergantungan eksternal
Misalnya test bergantung pada API pihak ketiga, DNS, clock system, filesystem bersama, atau shared database. Test seperti ini sering gagal karena faktor di luar perubahan kode.
Penanganan umum:
- Mock atau stub dependency eksternal bila tujuannya bukan menguji integrasi end-to-end.
- Untuk integration test, isolasi environment dan buat kontrak data yang deterministik.
- Bekukan waktu bila logic sensitif pada timestamp.
3. Flaky karena data atau state bocor
Ini umum pada suite yang memakai database atau cache bersama. Satu test meninggalkan state yang memengaruhi test lain. Kadang hanya gagal saat suite dijalankan paralel.
Penanganan umum:
- Reset state secara konsisten di setup/teardown.
- Gunakan nama resource unik per test run.
- Pastikan test tidak berbagi fixture mutable.
4. Flaky karena assertion lemah atau terlalu sensitif
Contohnya membandingkan string penuh yang memuat timestamp, memeriksa urutan list padahal urutan tidak dijamin, atau mengecek detail UI yang tidak relevan terhadap perilaku inti.
Penanganan umum:
- Assert pada invariant yang penting.
- Hindari assertion terhadap noise presentasional bila yang diuji adalah behavior.
- Bedakan toleransi untuk nilai yang secara natural bervariasi.
Desain regression gate: pisahkan critical path dan non-blocking suite
Cara paling efektif membangun gate yang tahan flaky test adalah tidak menjadikan semua test sebagai syarat merge dengan bobot yang sama. Bagi suite menjadi beberapa lapisan dengan aturan yang berbeda.
Critical path: wajib lulus, kualitas sinyal harus sangat tinggi
Suite ini bersifat blocking. Hanya masukkan test yang:
- Menguji alur bisnis paling penting.
- Stabil dan cepat.
- Punya dampak tinggi bila gagal di produksi.
Contoh isi critical path:
- Unit test untuk logic inti.
- Contract test yang memvalidasi kompatibilitas API internal.
- Smoke integration test untuk alur login, pembayaran, otorisasi, atau publish data.
Prinsip pentingnya: lebih baik sedikit tetapi sangat dapat dipercaya daripada banyak tetapi penuh noise.
Non-blocking suite: tetap dijalankan, tetapi tidak selalu memblokir merge
Suite ini cocok untuk test yang bernilai tetapi belum cukup stabil untuk jadi gate utama, misalnya browser test, integration test berat, atau test dependensi lingkungan. Hasilnya tetap harus terlihat, dipantau, dan ditindaklanjuti, tetapi tidak otomatis memblokir semua perubahan.
Agar non-blocking tidak berubah menjadi “diabaikan”, tetapkan aturan berikut:
- Kegagalan tetap membuat issue atau notifikasi.
- Ada owner yang jelas per suite.
- Ada target penurunan flake rate dari waktu ke waktu.
Model tiga lapis yang praktis
- Pre-merge blocking: unit test inti, lint, static analysis, smoke integration yang stabil.
- Post-merge non-blocking: suite lebih besar yang memberi sinyal risiko tambahan.
- Pre-release verification: validasi lebih luas terhadap kandidat rilis.
Model ini cocok untuk tim kecil karena sederhana, tetapi tetap memisahkan keputusan merge dari observabilitas kualitas yang lebih luas.
Retry yang terukur, bukan tombol “coba lagi” tanpa batas
Retry sering dipakai untuk mengurangi dampak flaky test, tetapi jika diterapkan sembarangan, ia menutupi regresi nyata. Retry seharusnya menjadi alat diagnosis dan peredam noise terbatas, bukan alat memaksa pipeline hijau.
Kapan retry layak dipakai
- Test berada di area yang memang sensitif terhadap latensi atau orchestration.
- Penyebab flakiness sudah dikenali, tetapi perbaikan permanen butuh waktu.
- Jumlah retry kecil dan hasil retry tetap dicatat sebagai sinyal kualitas.
Aturan retry yang sehat
- Batasi jumlah retry, misalnya satu kali untuk suite tertentu.
- Catat flaky pass: jika gagal lalu lulus saat retry, jangan anggap sepenuhnya hijau.
- Jangan retry semua jenis test. Unit test inti idealnya tidak perlu retry rutin.
- Bedakan infra failure dan test failure. Gangguan runner, jaringan, atau provisioning agent sebaiknya diklasifikasikan terpisah.
Yang penting bukan hanya “akhirnya lulus”, tetapi juga berapa sering perlu diulang. Angka itu adalah metrik kualitas test.
policy:
blocking_suites:
- name: unit-core
retry: 0
- name: smoke-critical
retry: 1
mark_flaky_pass: true
non_blocking_suites:
- name: browser-regression
retry: 1
notify_on_failure: true
- name: extended-integration
retry: 1
notify_on_failure: trueContoh di atas bukan format tool tertentu, tetapi menunjukkan kebijakan yang sebaiknya eksplisit: suite mana yang boleh retry, berapa kali, dan bagaimana hasilnya dihitung.
Quarantine policy: pisahkan test bermasalah tanpa menghapus sinyalnya
Salah satu kesalahan umum adalah membiarkan flaky test tetap blocking terlalu lama, atau sebaliknya langsung menonaktifkannya tanpa jejak. Quarantine policy adalah jalan tengah.
Apa itu quarantine
Test yang di-quarantine tetap dijalankan, tetapi tidak ikut menentukan status merge utama. Statusnya dilacak terpisah sampai stabil kembali atau diperbaiki.
Kapan test masuk quarantine
- Gagal intermiten dengan pola yang berulang.
- Sudah diverifikasi bukan karena regresi produk.
- Sudah ada issue dan owner yang bertanggung jawab.
Aturan quarantine yang sehat
- Wajib punya tiket perbaikan.
- Wajib punya owner dan tanggal review.
- Tidak boleh dibiarkan permanen tanpa SLA internal.
- Jumlah test dalam quarantine dipantau sebagai utang kualitas.
Tanpa aturan umur dan owner, quarantine mudah berubah menjadi tempat pembuangan test rusak.
quarantine_rules:
require_issue: true
require_owner: true
review_after_days: 7
max_quarantined_ratio: 0.05
release_block_if_quarantined_critical: truePrinsip pentingnya: test yang di-quarantine tetap terlihat di dashboard dan tetap dihitung dalam metrik kesehatan suite.
Gunakan baseline failure rate, bukan keputusan biner semata
Regression gate yang matang tidak hanya melihat status satu pipeline, tetapi juga konteks historis. Di sinilah baseline failure rate berguna. Jika sebuah suite secara historis punya tingkat kegagalan non-deterministik tertentu, keputusan gate bisa mempertimbangkan apakah kegagalan saat ini berada di luar baseline normal.
Metrik baseline yang perlu dicatat
- Failure rate per test: seberapa sering test gagal dalam sejumlah run terakhir.
- Flaky pass rate: seberapa sering test gagal lalu lulus setelah retry.
- Suite stability: persentase run bersih tanpa retry.
- Mean time to fix flaky test: durasi dari terdeteksi sampai diperbaiki atau dikarantina dengan benar.
- Quarantine count: jumlah dan tren test yang dikarantina.
Cara memakai baseline dalam gate
Beberapa keputusan praktis yang aman:
- Jika test blocking yang biasanya stabil tiba-tiba gagal, anggap sebagai regresi sampai terbukti sebaliknya.
- Jika suite non-blocking gagal dalam pola yang sesuai baseline flake historis, jangan blok merge, tetapi buat notifikasi.
- Jika failure rate sebuah test meningkat tajam dibanding baseline, naikkan prioritas investigasi meski masih non-blocking.
Yang perlu dihindari adalah ambang yang terlalu pintar tetapi tidak transparan. Untuk banyak tim, aturan sederhana yang bisa dijelaskan lebih berharga daripada model skoring yang sulit diaudit.
Workflow verifikasi sebelum release
Walaupun pre-merge gate dibuat lebih tahan flaky test, kandidat rilis tetap perlu verifikasi tambahan. Kuncinya adalah membedakan keputusan merge dari keputusan release.
Tujuan pre-release verification
- Memastikan perubahan yang sudah terkumpul tetap aman dalam kombinasi terbaru.
- Memberi kesempatan menjalankan suite yang lebih luas atau lebih lambat.
- Menilai risiko release dengan konteks yang lebih lengkap daripada satu pull request.
Workflow yang praktis
- Bangun artefak kandidat rilis dari commit yang sudah lolos pre-merge gate.
- Jalankan smoke test blocking terhadap artefak yang sama dengan yang akan dirilis.
- Jalankan suite tambahan: integration, contract, browser, migrasi, dan health check environment.
- Tinjau hasil quarantine: pastikan tidak ada test kritis yang sedang di-quarantine tanpa mitigasi.
- Validasi perubahan berisiko tinggi secara manual bila perlu, misalnya migrasi data atau perubahan auth.
Dalam praktiknya, release tidak harus menunggu semua test non-kritis hijau total, tetapi harus punya keputusan sadar berbasis risiko, bukan sekadar “pipeline tadi sempat hijau”.
Contoh kebijakan CI yang bisa langsung diadopsi
Berikut contoh kebijakan yang cukup realistis untuk tim kecil sampai menengah.
ci_policy:
merge_gate:
must_pass:
- lint
- static-analysis
- unit-core
- api-contract-core
- smoke-critical
allowed_retries:
smoke-critical: 1
fail_on:
- any_failure_in_must_pass
- flaky_pass_rate_spike_in_smoke_critical
observe_only:
- browser-regression
- extended-integration
- data-migration-simulation
quarantine:
allowed: true
conditions:
- issue_exists
- owner_assigned
- review_date_set
restrictions:
- critical_tests_require_release_approval
pre_release:
required:
- artifact-smoke
- core-integration
- rollback-check
- database-migration-check
manual_review_for:
- auth_changes
- payment_changes
- schema_changesKebijakan seperti ini bekerja karena setiap suite punya peran yang jelas. Ia juga memaksa tim mendokumentasikan apa yang benar-benar dianggap risiko release.
Metrik yang wajib dipantau
Tanpa metrik, regression gate cepat memburuk tanpa terlihat. Minimal pantau hal berikut:
- Merge-block rate: berapa banyak merge tertahan oleh gate blocking.
- False block rate: berapa banyak blok yang ternyata disebabkan flaky test atau masalah infrastruktur.
- Retry rate per suite: indikasi kualitas sinyal test.
- Top flaky tests: daftar test dengan intermitensi tertinggi.
- Quarantine backlog: jumlah test yang masih dikarantina dan usianya.
- Escaped regression: bug yang lolos ke staging atau produksi walau CI hijau.
Jika hanya memilih dua metrik untuk mulai, pilih false block rate dan escaped regression. Keduanya langsung menunjukkan apakah gate terlalu ketat atau terlalu longgar.
Anti-pattern yang sering terjadi
1. Menjadikan seluruh suite end-to-end sebagai gate merge utama
Ini sering membuat pipeline lambat dan rapuh. E2E bernilai tinggi, tetapi biasanya paling rentan terhadap flakiness dan paling mahal saat gagal.
2. Retry tanpa pencatatan
Pipeline tampak hijau, tetapi kualitas suite diam-diam memburuk. Jika retry tidak terlihat di laporan, tim kehilangan sinyal degradasi.
3. Quarantine tanpa owner atau tenggat
Hasilnya adalah kuburan test. Jumlah test non-tepercaya terus bertambah sampai coverage efektif menurun.
4. Menganggap semua kegagalan sama
Infra issue, flaky test, dan regresi produk adalah tiga kelas insiden berbeda. Campur ketiganya, dan tindakan perbaikan akan salah sasaran.
5. Mengukur kesuksesan hanya dari persentase pipeline hijau
Pipeline bisa hijau karena retry berlebih, test dinonaktifkan, atau suite berisiko dipindah ke observe-only tanpa kontrol. Hijau tidak otomatis berarti sehat.
Tips debugging saat flaky test mulai sering muncul
- Jalankan test yang sama berulang kali dengan logging tambahan untuk mencari pola intermiten.
- Bandingkan perilaku saat serial vs paralel untuk mendeteksi state leakage.
- Rekam durasi per langkah untuk melihat timeout yang terlalu ketat.
- Catat dependency eksternal yang terlibat: network, clock, file, queue, database.
- Pastikan failure artifact disimpan: log, screenshot, trace, response body, atau dump state.
Debugging flaky test sering gagal bukan karena bug-nya sulit, tetapi karena artefak kegagalan tidak disimpan saat kegagalan pertama terjadi.
Checklist implementasi untuk tim kecil
Jika tim Anda belum punya sistem yang matang, mulai dari checklist ini:
- Daftar semua suite test yang ada, lalu tandai mana yang benar-benar kritis untuk merge.
- Pindahkan hanya test paling stabil ke blocking pre-merge.
- Tetapkan maksimal retry yang eksplisit, dan laporkan flaky pass secara terpisah.
- Buat label atau folder quarantine yang wajib punya issue, owner, dan review date.
- Mulai catat failure rate per test dan retry rate per suite.
- Pisahkan failure infrastruktur dari failure test di dashboard CI.
- Terapkan workflow pre-release terpisah untuk kandidat rilis.
- Tinjau top flaky tests setiap minggu, bukan hanya saat insiden.
- Tetapkan aturan bahwa test baru di critical path harus memenuhi standar stabilitas tertentu.
- Evaluasi setiap bulan: apakah gate terlalu sering false block, atau justru terlalu sering meloloskan regresi.
Penutup
Membangun regression gate yang tahan flaky test di CI pada dasarnya adalah pekerjaan desain sinyal. Anda ingin CI cukup tegas untuk menghentikan regresi nyata, tetapi cukup realistis untuk tidak lumpuh oleh test yang belum stabil. Itu dicapai bukan dengan satu trik, melainkan dengan kombinasi: klasifikasi flaky test, pemisahan critical path dan non-blocking suite, retry yang terukur, quarantine policy yang disiplin, baseline failure rate, dan verifikasi khusus sebelum release.
Jika ada satu prinsip yang layak diingat, ini dia: jangan biarkan semua test punya hak veto yang sama. Kualitas release meningkat ketika setiap test diberi peran yang tepat, dan setiap kegagalan menghasilkan keputusan yang dapat dipercaya.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!