CI/CD Anti Pattern #
Pipeline CI/CD yang buruk lebih berbahaya dari tidak ada pipeline sama sekali — ia memberikan rasa aman yang palsu. Tim percaya bahwa karena ada “pipeline”, deployment sudah “otomatis aman”, padahal pipeline yang dirancang dengan buruk bisa menyebarkan perubahan yang tidak tervalidasi ke production lebih cepat dari deployment manual. Artikel ini membahas anti pattern CI/CD yang paling sering menyebabkan masalah serius.
Anti Pattern 1: Deploy Langsung ke Production tanpa Staging #
# ANTI-PATTERN: pipeline yang langsung ke production
# .github/workflows/deploy.yml
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy ke production
run: ansible-playbook -i inventory/production/ site.yml
# Push ke main langsung ke production — tidak ada staging, tidak ada validasi!
# BENAR: selalu ada staging sebagai gate
jobs:
deploy-staging:
runs-on: ubuntu-latest
steps:
- run: ansible-playbook -i inventory/staging/ site.yml
verify-staging:
needs: deploy-staging
steps:
- run: |
curl -f https://staging.company.com/health
# Tambahkan smoke test, integration test
deploy-production:
needs: verify-staging
environment:
name: production # Membutuhkan manual approval
steps:
- run: ansible-playbook -i inventory/production/ site.yml
Anti Pattern 2: Credential Hardcode atau Tidak Aman #
# ANTI-PATTERN: credential di file pipeline yang di-commit ke Git
# .gitlab-ci.yml
deploy:
script:
- ansible-playbook site.yml
-e "vault_password=super_secret" # HARDCODE! Masuk Git!
-e "ssh_key_content=-----BEGIN RSA..." # HARDCODE! Masuk Git!
# ATAU: credential di-echo ke file dari variable, tapi tanpa masking
deploy:
script:
- echo "$VAULT_PASSWORD" > /tmp/.vault_pass # Muncul di log jika VAULT_PASSWORD tidak di-mask!
# BENAR: credential melalui masked CI/CD variables
# Di GitLab Settings → CI/CD → Variables:
# VAULT_PASS_PROD → Masked + Protected
# PROD_SSH_KEY → Masked + Protected + File type
deploy:
script:
- |
# SSH key tersedia sebagai file jika tipe "File" diset di GitLab
chmod 600 "$PROD_SSH_KEY"
echo "$VAULT_PASS_PROD" > /tmp/.vault_pass
chmod 600 /tmp/.vault_pass
- ansible-playbook -i inventory/production/ site.yml
--vault-password-file /tmp/.vault_pass
--private-key "$PROD_SSH_KEY"
after_script:
- rm -f /tmp/.vault_pass # Cleanup selalu
Anti Pattern 3: Pipeline yang Tidak Bisa Di-Retry dengan Aman #
# ANTI-PATTERN: pipeline dengan state yang tidak bisa direset
# Scenario: pipeline gagal di tengah deployment
# - 5 dari 10 server sudah diupdate
# - 5 server masih menjalankan versi lama
# - Pipeline tidak bisa langsung di-retry karena state tidak konsisten
# Ini terjadi karena:
# 1. Tidak ada health check setelah setiap batch
# 2. Tidak ada rollback otomatis saat gagal
# 3. Tidak ada mekanisme untuk melanjutkan dari titik gagal
# BENAR: playbook yang aman di-retry
# 1. Idempoten — bisa dijalankan ulang tanpa efek samping
# 2. Health check per batch
# 3. Rollback otomatis jika health check gagal
- hosts: appservers
serial: 2
max_fail_percentage: 0 # Hentikan jika ada yang gagal
tasks:
- block:
- name: Deploy versi baru
git:
repo: https://github.com/company/app.git
dest: /opt/app
version: "{{ version }}"
- name: Health check setelah deploy
uri:
url: "http://localhost:{{ app_port }}/health"
status_code: 200
retries: 6
delay: 10
rescue:
- name: Rollback jika gagal
git:
repo: https://github.com/company/app.git
dest: /opt/app
version: "{{ rollback_version }}"
- name: Gagalkan pipeline setelah rollback
fail:
msg: "Deployment gagal, rollback ke {{ rollback_version }} dilakukan"
Anti Pattern 4: Tidak Ada Testing di Pipeline #
# ANTI-PATTERN: pipeline hanya deploy tanpa validasi apapun
# .github/workflows/deploy.yml
on:
push:
branches: [main]
jobs:
deploy:
steps:
- uses: actions/checkout@v4
- run: ansible-playbook site.yml
# Tidak ada lint, tidak ada syntax check, tidak ada test!
# Push typo ke main → langsung ke production
# BENAR: validate sebelum deploy
jobs:
validate:
steps:
- run: |
ansible-lint site.yml
ansible-playbook site.yml --syntax-check -i inventory/staging/
molecule:
steps:
- run: |
cd roles/myapp && molecule test
deploy-staging:
needs: [validate, molecule]
steps:
- run: ansible-playbook -i inventory/staging/ site.yml
smoke-test:
needs: deploy-staging
steps:
- run: |
curl -f https://staging.company.com/health
curl -f https://staging.company.com/api/version
Anti Pattern 5: Pipeline yang Terlalu Kompleks #
# ANTI-PATTERN: pipeline dengan 15 job, puluhan dependency, dan logika kondisional yang
# membingungkan — tidak ada yang bisa memahami alurnya tanpa diagram
# (dan diagramnya tidak up-to-date)
#
# Masalah:
# - Tidak ada yang berani memodifikasi karena takut merusak
# - Debugging saat failure sangat sulit
# - Onboarding engineer baru membutuhkan berhari-hari hanya untuk memahami pipeline
# BENAR: pipeline yang bisa dipahami dalam 5 menit
# Prinsip: setiap orang di tim harus bisa membaca pipeline
# dan memahami apa yang terjadi tanpa bantuan
# Tiga pertanyaan untuk setiap job di pipeline:
# 1. Apa yang dilakukan job ini?
# 2. Kapan job ini berjalan?
# 3. Apa yang terjadi jika job ini gagal?
#
# Jika tidak bisa dijawab dengan jelas → sederhanakan
jobs:
# Jelas: validasi kode sebelum merge
validate:
if: github.event_name == 'pull_request'
# Jelas: deploy ke staging setelah merge ke main
deploy-staging:
if: github.ref == 'refs/heads/main'
needs: validate
# Jelas: deploy ke production setelah approval manual
deploy-production:
environment: production # Membutuhkan approval
needs: deploy-staging
Anti Pattern 6: Menyimpan Artifacts Pipeline Terlalu Lama (atau Terlalu Sebentar) #
# ANTI-PATTERN: artifacts disimpan selamanya (atau tidak sama sekali)
# .gitlab-ci.yml
# Masalah 1: tidak ada artifact — debugging jadi sulit
deploy:
script: ansible-playbook site.yml
# Tidak ada artifacts — jika gagal, tidak ada log untuk diinvestigasi
# Masalah 2: artifacts disimpan selamanya — storage membengkak
build:
artifacts:
paths:
- build/
# Tidak ada expire_in — disimpan selamanya!
# BENAR: artifact dengan retention yang tepat
build:
artifacts:
paths:
- build/
- deployment-report.txt
expire_in: 30 days # Cukup untuk investigasi, tidak membengkak storage
when: always # Simpan bahkan jika job gagal (untuk debug)
reports:
dotenv: build.env # Teruskan variabel ke downstream jobs
Ringkasan #
- Selalu ada staging sebelum production — tidak ada alasan yang cukup kuat untuk deploy langsung ke production tanpa validasi di lingkungan yang serupa.
- Semua credential harus melalui CI/CD masked variables — tidak ada yang hardcode di file pipeline yang masuk Git.
- Playbook deployment harus idempoten dan bisa di-retry — jika pipeline gagal di tengah jalan,
re-runharus aman tanpa state yang rusak.- Validate sebelum deploy:
ansible-lint, syntax check, dan Molecule test harus jadi gate sebelum deployment bahkan ke staging.- Pipeline yang bisa dipahami dalam 5 menit oleh semua anggota tim — kompleksitas berlebihan adalah hutang teknis yang sangat mahal saat debugging insiden.
- Simpan artifact pipeline dengan
expire_inyang tepat — cukup lama untuk investigasi (7-30 hari), tidak selamanya yang menghabiskan storage.
← Sebelumnya: Performance Anti Pattern Berikutnya: Project Structure →