CI/CD Anti Pattern

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-run harus 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_in yang tepat — cukup lama untuk investigasi (7-30 hari), tidak selamanya yang menghabiskan storage.

← Sebelumnya: Performance Anti Pattern   Berikutnya: Project Structure →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact