Documentation as Code — Clean Code

Documentation as Code adalah pendekatan dimana dokumentasi diperlakukan seperti kode: di-version control, di-review, dan diupdate bersama kode yang berubah…

Documentation as code artinya dokumentasi diperlakukan seperti kode: ditulis sebagai teks biasa (Markdown) di repo yang sama, diubah di commit dan pull request yang sama dengan kodenya, di-review bersama, dan kalau bisa dicek oleh mesin. Bayangin petunjuk cara pakai mesin kasir Toko Kaos Raka. Yang ditempel di mesinnya ikut diganti waktu mesinnya diganti. Yang disimpan di laci kantor tetap menjelaskan mesin lama, dan gak ada yang sadar sampai karyawan baru mengikutinya.

🧪 Tebakan cepat: Raka menaikkan batas gratis ongkir jadi Rp 250.000. Kode dan test diubah di commit yang sama, semua cek OK. README masih bilang calculateTotal(cartA, 'HEMAT10') menghasilkan Total: Rp 220.500. Apa yang memberitahumu README-nya basi: error, test yang gagal, atau gak ada? Tebak dulu, jawabannya ada di bagian "Dokumentasi basi gak bikin error".

README kasir

README adalah pintu depan repo: apa ini, cara menjalankannya, aturan pentingnya, dan contoh. Punya kasir Raka, README.md di folder yang sama dengan kasir.js:

# Kasir Toko Kaos Raka

Menghitung total belanja: subtotal, potongan voucher, lalu ongkir.

## Aturan

- Voucher HEMAT10 memotong 10%.
- Ongkir Rp 15.000, gratis kalau total setelah potongan minimal Rp 200.000.

## Contoh

    calculateTotal(cartA, 'HEMAT10') // Total: Rp 220.500
    calculateTotal(cartC, '')        // Total: Rp 90.000

Karena ia file Markdown di repo, perubahan README kelihatan di diff pull request, persis seperti kode. Dokumen di Word, Google Drive atau wiki terpisah gak ikut diff itu, jadi gak ada reviewer yang menagihnya.

Dokumentasi basi gak bikin error

Raka minta gratis ongkir mulai Rp 250.000. Kamu mengubah FREE_SHIPPING_MIN = 250000 dan harapan di test. Kodenya sekarang:

> calculateTotal(cartA, 'HEMAT10')
Total: Rp 235.500

220.500 sekarang di bawah 250.000, jadi kena ongkir. Test lulus, karena test sudah diubah. README masih bilang Total: Rp 220.500 dan minimal Rp 200.000. Gak ada yang memberitahumu. Itu jawaban tebakannya: README cuma teks, gak ada yang menjalankannya. Dokumentasi yang salah lebih buruk daripada gak ada, karena orang percaya dan mengikutinya.

Contoh yang dijalankan

Contoh di README ditulis dengan pola yang bisa dibaca mesin: panggilan, lalu // dan hasil yang diharapkan. Script kecil ini menjalankan tiap contoh persis seperti tertulis:

const carts = { cartA, cartC, cartD };

function checkExamples(doc) {
  const pattern = /calculateTotal\((\w+), '(\w*)'\);?\s*\/\/ (.+)/g;
  for (const [, cartName, voucherCode, expected] of doc.matchAll(pattern)) {
    check(calculateTotal(carts[cartName], voucherCode), expected);
  }
}
checkExamples(README); // README: isi README.md sebagai string
BEDA Total: Rp 235.500, harusnya Total: Rp 220.500
OK   Total: Rp 90.000

Ide yang sama ada di alat sungguhan: cargo test di Rust menjalankan contoh di komentar dokumentasi, dan Python punya modul doctest. Tapi mesin cuma mengecek contohnya. Kalimat "minimal Rp 200.000" tetap diubah manusia, dan reviewer menagihnya di pull request yang sama.

JSDoc untuk calculateTotal

JSDoc adalah komentar /** ... */ tepat di atas fungsi, dengan tag yang diawali @. Editor seperti VS Code menampilkannya saat kursor diarahkan ke nama fungsi:

/**
 * Menghitung total belanja: subtotal, potongan voucher, lalu ongkir.
 * @param {Array} cart Barang di keranjang, tiap barang punya price dan qty.
 * @param {string} voucherCode Kode voucher, '' kalau tanpa voucher.
 * @returns {string} Teks total untuk struk.
 * @example
 * calculateTotal(cartA, 'HEMAT10'); // Total: Rp 220.500
 */
function calculateTotal(cart, voucherCode) { /* ... */ }

Tulis JSDoc untuk fungsi yang dipanggil orang lain. Fungsi kecil di dalam modul yang namanya sudah jelas gak perlu.

ADR: mencatat kenapa

ADR (Architecture Decision Record) adalah satu file Markdown pendek per keputusan penting, di docs/adr/. Formatnya dari Michael Nygard ("Documenting Architecture Decisions", 2011): judul, status, konteks, keputusan, konsekuensi. Keputusan tabel voucher dari lesson Refactoring Techniques:

# ADR-0002: Voucher disimpan di tabel VOUCHERS

Status: accepted

## Context
Tiap voucher baru menambah if di applyVoucher, dan PR #14 (HEMAT20) mengubah fungsi yang sudah dites.

## Decision
Tarif voucher disimpan di tabel VOUCHERS; applyVoucher membaca VOUCHERS[voucherCode] ?? 0.

## Consequences
Voucher persen baru cukup satu baris. Voucher yang bukan persen, misalnya potongan Rp 25.000, gak muat di tabel tarif dan butuh keputusan baru.

Status-nya salah satu dari proposed, accepted, deprecated atau superseded (diganti ADR yang lebih baru). ADR gak diedit setelah diterima; keputusan baru jadi ADR baru yang menggantikannya. Setahun lagi, yang bertanya "kenapa tabel, bukan class?" menemukan jawabannya di repo.

Tiap jenis dokumentasi punya tugas

Latihan: parseJsDoc dan validateAdr

parseJsDoc(comment) mengubah teks JSDoc jadi data. Pola /@param\s*\{(\w+)\}\s*(\w+)([^@]*)/g dipakai berulang dengan exec dalam while: grup 1 tipe, grup 2 nama, grup 3 keterangan sampai @ berikutnya. /@returns?\s*\{(\w+)\}/ mengambil tipe kembalian; kalau gak ada @returns, returns tetap null, jangan dikarang. Untuk '/** @param {string} name User name @param {number} age @returns {boolean} */': 2 param, yang pertama name bertipe string, return boolean.

validateAdr(adr) mendorong satu error per field wajib yang kosong (title, status, context, decision, consequences) dan satu lagi kalau status bukan salah satu dari empat status di atas. valid adalah errors.length === 0. ADR lengkap: true dan 0. { title: 'Test', status: 'unknown' } dapat 4 error (tiga field kosong, satu status), jadi false dan errors.length >= 2 bernilai true. Output-nya:

2
name
string
boolean
true
0
false
true

⚠️ Jebakan yang sering ketemu

TL;DR: Documentation as code: dokumentasi ditulis sebagai Markdown di repo yang sama, diubah di pull request yang sama, di-review bersama kodenya, dan contohnya dijalankan mesin. Dokumentasi basi gak bikin error: setelah batas gratis ongkir jadi Rp 250.000, README yang masih bilang Total: Rp 220.500 cuma ketahuan kalau contohnya dijalankan (checkExamples mencetak BEDA Total: Rp 235.500). README berisi apa, cara menjalankan, aturan dan contoh; JSDoc (@param {tipe} nama, @returns, @example) menjelaskan cara memanggil fungsi publik; ADR mencatat konteks, keputusan dan konsekuensi sebuah keputusan arsitektur, dengan status proposed, accepted, deprecated atau superseded; komentar menjelaskan kenapa. Di latihan: parseJsDoc mengembalikan params { name, type, description } dan returns null kalau gak ada @returns; validateAdr mendorong error untuk tiap field kosong dan status tidak valid, dan output-nya 2, name, string, boolean, true, 0, false, true.

Yang akan kamu pelajari