Code Quality

Code Quality #

Kualitas kode Ansible tidak hanya tentang “apakah ia berjalan” — tapi tentang apakah ia bisa dipahami, di-maintain, dan dipercaya oleh semua orang di tim. Kode yang hanya dipahami oleh penulisnya adalah hutang teknis yang menunggu waktu untuk jadi masalah. Investasi dalam code quality — linting, review yang struktural, dokumentasi yang tepat — adalah investasi yang membayar diri sendiri berkali-kali lipat saat tim tumbuh dan infrastruktur menjadi lebih kompleks.

Konfigurasi ansible-lint yang Ketat #

# .ansible-lint
profile: production    # Profil paling ketat — rekomendasikan untuk semua project

# Aturan yang di-skip hanya jika ada alasan yang sangat kuat
skip_list:
  - yaml[line-length]    # Baris panjang kadang tidak terhindarkan untuk command

# Aturan yang jadi warning, bukan error (untuk migrasi bertahap)
warn_list:
  - no-changed-when      # Sebaiknya diperbaiki, tapi belum blocking

# Path yang dikecualikan dari linting
exclude_paths:
  - .cache/
  - molecule/
  - vendor/

# Aktifkan aturan experimental
use_default_rules: true
rulesdir:
  - .ansible-lint-rules/   # Custom rules (lihat di bawah)

# Format output
verbosity: 1

Custom Lint Rules #

Untuk aturan spesifik organisasi yang tidak ada di ansible-lint bawaan:

# .ansible-lint-rules/NoPlaintextSecrets.py
"""Aturan custom: cegah variabel yang terlihat seperti secret tanpa vault."""

from ansiblelint.rules import AnsibleLintRule
import re

SECRET_PATTERNS = [
    r'password\s*:\s*["\']?[^{][^"\']+["\']?$',
    r'api_key\s*:\s*["\']?[^{][^"\']+["\']?$',
    r'secret\s*:\s*["\']?[^{][^"\']+["\']?$',
    r'token\s*:\s*["\']?[^{][^"\']+["\']?$',
]


class NoPlaintextSecrets(AnsibleLintRule):
    id = 'COMPANY001'
    shortdesc = 'Jangan menyimpan secret dalam plaintext'
    description = (
        'Variabel yang terlihat seperti secret (password, api_key, token) '
        'tidak boleh berisi nilai plaintext. Gunakan Ansible Vault.'
    )
    severity = 'VERY_HIGH'
    tags = ['security', 'company-policy']

    def matchplay(self, file, data):
        errors = []
        if 'vars' in data:
            for key, value in data.get('vars', {}).items():
                if isinstance(value, str):
                    for pattern in SECRET_PATTERNS:
                        if re.search(pattern, f"{key}: {value}", re.IGNORECASE):
                            errors.append(({}, f"Kemungkinan secret plaintext: {key}"))
        return errors

Pre-commit Hooks #

Cegah commit yang tidak memenuhi standar sebelum masuk ke repository:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/ansible/ansible-lint
    rev: v24.2.0
    hooks:
      - id: ansible-lint
        args: ['--profile=production']
        files: \.(yml|yaml)$
        exclude: ^(molecule|vendor)/

  - repo: https://github.com/adrienverge/yamllint
    rev: v1.35.1
    hooks:
      - id: yamllint
        args: [-c=.yamllint]

  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: check-yaml
      - id: check-merge-conflict
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: detect-private-key      # Cegah private key masuk Git
      - id: check-added-large-files
        args: ['--maxkb=500']

  # Cegah secret masuk ke Git
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks
# Setup pre-commit di environment lokal
pip install pre-commit
pre-commit install

# Jalankan manual terhadap semua file (sekali pertama kali)
pre-commit run --all-files

yamllint: Format YAML yang Konsisten #

# .yamllint
---
extends: default

rules:
  line-length:
    max: 160              # Lebih longgar dari default 80
    level: warning

  truthy:
    allowed-values: ['true', 'false']
    check-keys: false     # Jangan paksa boolean di keys

  comments:
    min-spaces-before-comment: 1
    require-starting-space: true

  braces:
    min-spaces-inside: 0
    max-spaces-inside: 1

  indentation:
    spaces: 2
    indent-sequences: consistent

Code Review Checklist untuk Ansible #

## Checklist Review Playbook/Role

### Kebenaran
- [ ] Task menggunakan module yang tepat (bukan command/shell jika ada module)
- [ ] Semua task idempoten — aman dijalankan berulang kali
- [ ] Handler digunakan untuk restart service, bukan task langsung
- [ ] Error handling ada untuk operasi yang bisa gagal

### Keamanan
- [ ] Tidak ada secret plaintext (password, key, token)
- [ ] `no_log: true` untuk task yang melibatkan credential
- [ ] `become: true` hanya di task yang benar-benar butuh privilege
- [ ] Tidak ada `host_key_checking: false` tanpa justifikasi

### Kualitas
- [ ] Semua nilai melalui variabel, tidak ada hardcode
- [ ] Variabel punya nama yang deskriptif dan namespaced
- [ ] `defaults/main.yml` lengkap dengan nilai default yang masuk akal
- [ ] Dokumen variabel wajib di `meta/argument_specs.yml` atau README role

### Readability
- [ ] Setiap task punya nama yang menjelaskan tujuannya (bukan perintahnya)
- [ ] Komentar ada untuk logika yang tidak obvious
- [ ] Tidak ada kode yang sudah di-comment-out (hapus atau commit alasannya)
- [ ] Nama file dan direktori mengikuti konvensi project

Dokumentasi Inline yang Efektif #

# ANTI-PATTERN: nama task yang mendeskripsikan perintah, bukan tujuan
- name: Run systemctl restart nginx
  systemd:
    name: nginx
    state: restarted

- name: apt-get install nginx
  apt:
    name: nginx
    state: present
# BENAR: nama task yang menjelaskan MENGAPA, bukan APA
- name: Restart nginx untuk menerapkan konfigurasi SSL baru
  systemd:
    name: nginx
    state: restarted

- name: Install nginx sebagai reverse proxy untuk aplikasi
  apt:
    name: nginx
    state: present

# Komentar untuk logika yang tidak obvious:
- name: Tunggu 30 detik sebelum health check
  pause:
    seconds: 30
  # Aplikasi butuh waktu untuk load semua konfigurasi dari environment
  # sebelum health endpoint siap merespons. Nilai ini ditentukan dari
  # observasi bahwa p99 startup time adalah 25 detik.

Ringkasan #

  • Gunakan profile: production di .ansible-lint — profil paling ketat sebagai standar, bukan sebagai tujuan yang akan dicapai nanti.
  • Pre-commit hooks adalah cara paling efektif mencegah masalah masuk ke repository — deteksi saat commit, bukan saat code review atau pipeline.
  • Custom lint rules untuk aturan spesifik organisasi yang tidak ada di ansible-lint bawaan — misalnya, deteksi secret plaintext atau naming convention.
  • Nama task yang menjelaskan tujuan (MENGAPA), bukan perintah (APA) — systemctl restart nginx tidak informatif, Restart nginx setelah update SSL sangat informatif.
  • Code review checklist yang konkret lebih efektif dari guideline umum — reviewer tahu persis apa yang harus dicek, bukan mengandalkan intuisi.
  • yamllint untuk konsistensi format YAML — style yang konsisten mengurangi cognitive load saat membaca kode orang lain.

← Sebelumnya: Project Structure   Berikutnya: Testing Strategy →

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