Kode yang baik tetap membutuhkan dokumentasi—tidak untuk menjelaskan apa yang dilakukan setiap baris (itu tugas komentar), melainkan untuk menjelaskan bagaimana memulai proyek, bagaimana arsitekturnya disusun, dan apa yang berubah di setiap rilis. Tiga file dokumentasi inti yang harus ada di setiap proyek C open-source adalah README.md (pintu masuk pengguna), ARCHITECTURE.md (peta arsitektur untuk kontributor), dan CHANGELOG.md (riwayat versi). Bab ini menunjukkan struktur masing-masing file, lalu mendemonstrasikan bagaimana dokumentasi tersebut mengikat ke perilaku aktual program—mis. README yang menyebut flag --version harus sesuai dengan flag yang benar-benar diimplementasikan di kode. Bab ini menutup section Coding Standards dan Architecture.

README.md: Pintu Masuk Pengguna #

README.md adalah file pertama yang dilihat pengunjung di repository GitHub atau GitLab. File ini harus menjawab empat pertanyaan dalam 30 detik pertama: apa proyek ini, bagaimana cara install, bagaimana cara pakai, dan bagaimana cara kontribusi. Struktur rekomendasi:

markdown
# demo-dok

Aplikasi demonstrasi untuk bab dokumentasi proyek.

## Instalasi

```bash
gcc -std=c11 -Wall -Wextra -o demo-dok main.c

Penggunaan #

bash
./demo-dok             # Sapa "Dunia"
./demo-dok Budi        # Sapa "Budi"
./demo-dok --version   # Tampilkan versi

Kontribusi #

Lihat ARCHITECTURE.md untuk struktur modul. Kirim pull request ke branch develop. Sertakan test untuk fitur baru.

Lisensi #

MIT License - lihat LICENSE.

text

README yang baik **menjalankan perintah konkret** yang dapat di-copy-paste oleh pengguna. Berikut program C yang sesuai dengan README di atas—program ini mendukung tiga flag yang didokumentasikan:

```c
/* File: test_dok1.c - Program yang dibaca dari README/ARCHITECTURE.md */
/* Demo: hello world yang membaca config dari argumen */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* Versi program yang dipakai sebagai contoh di README */
int main(int argc, char **argv)
{
    const char *nama = "Dunia";
    if (argc > 1) {
        nama = argv[1];
    }
    /* Cek argumen --version sesuai dokumentasi */
    if (strcmp(nama, "--version") == 0) {
        printf("demo-dok v1.2.3\n");
        return 0;
    }
    printf("Halo, %s!\n", nama);

    /* Log ke stderr untuk debugging (lihat ARCHITECTURE.md: logging) */
    fprintf(stderr, "[INFO] greeted user: %s\n", nama);
    return 0;
}

Compile dan jalankan dengan tiga skenario yang didokumentasikan README:

bash
gcc -std=c11 -Wall -Wextra -o demo-dok test_dok1.c
./demo-dok
./demo-dok --version
./demo-dok Budi

Output yang Dihasilkan (gabungan tiga skenario):

text
Halo, Dunia!
demo-dok v1.2.3
Halo, Budi!

Output stderr (dipisahkan dari stdout untuk logging):

text
[INFO] greeted user: Dunia
[INFO] greeted user: Budi

README mengatakan “sapa nama pengguna”—program benar-benar melakukannya. README mengatakan “--version menampilkan versi”—program benar-benar mendukungnya. Keselarasan antara README dan implementasi inilah yang membuat dokumentasi dipercaya. Jika README dan kode berbeda, pengguna akan frustrasi: mereka menjalankan perintah sesuai README tetapi mendapat error karena flag tidak ada.

ARCHITECTURE.md: Peta untuk Kontributor #

Sementara README menjawab “bagaimana pakai”, ARCHITECTURE.md menjawab “bagaimana kode disusun”. File ini menjelaskan modul-modul utama, dependensi antar modul, dan alur data. Berikut contoh isi ARCHITECTURE.md untuk proyek kecil:

markdown
# Arsitektur demo-dok

## Struktur Modul

src/ ├── main.c # entry point, parsing CLI args ├── parser.c/.h # parsing input string menjadi user_t ├── logic.c/.h # validasi bisnis dan transformasi └── output.c/.h # format dan cetak hasil

text

## Arah Dependency

main -> parser -> logic -> output

Modul output tidak boleh meng-include parser.h atau logic.h.
Modul parser tidak boleh langsung memanggil output—harus lewat logic.

## Alur Data

1. main menerima argumen CLI
2. parser mengubah string "Budi,32" menjadi struct user_t
3. logic memvalidasi (umur positif, nama tidak kosong)
4. output mencetak hasil ke stdout, log ke stderr

Berikut kode C yang mengimplementasikan arsitektur tiga-lapis di atas:

c
/* File: test_dok3.c - ARCHITECTURE.md: diagram dependensi modul */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* Tiga modul dengan dependensi yang jelas */
/* main -> parser -> logic -> output */

typedef struct { char nama[32]; int umur; } user_t;

/* Modul output (layer 3): tidak dependensi ke siapapun */
static void output_user(const user_t *u)
{
    printf("User: %s, umur %d\n", u->nama, u->umur);
}

/* Modul logic (layer 2): bergantung ke output */
static void logic_proses(const user_t *u)
{
    /* Validasi bisnis: umur harus positif */
    user_t tmp = *u;
    if (tmp.umur < 0) tmp.umur = 0;
    output_user(&tmp);
}

/* Modul parser (layer 1): bergantung ke logic */
static int parser_parse(const char *input, user_t *out)
{
    if (sscanf(input, "%31[^,],%d", out->nama, &out->umur) != 2) {
        return -1;
    }
    logic_proses(out);
    return 0;
}

int main(void)
{
    const char *input = "Budi,32";
    user_t u;
    /* main bergantung ke parser */
    if (parser_parse(input, &u) != 0) {
        fprintf(stderr, "parse gagal\n");
        return 1;
    }
    return 0;
}

Output yang Dihasilkan:

text
User: Budi, umur 32

Mari kita telaah baris-baris kode di atas:

  1. Baris 12-15 (output_user): Modul output. Hanya menerima const user_t * dan mencetak. Tidak meng-include header lain, tidak tahu dari mana data berasal. Modul ini paling independen—dapat diganti dengan output JSON, XML, atau HTTP tanpa memengaruhi modul lain.
  2. Baris 18-24 (logic_proses): Modul logic. Menerima const user_t *, memvalidasi (umur < 0 di-klamp ke 0), lalu memanggil output. Modul ini bergantung ke output, tetapi tidak ke parser—sebaliknya. Arah dependency jelas.
  3. Baris 27-33 (parser_parse): Modul parser. Mengubah string “Budi,32” menjadi struct user_t, lalu memanggil logic. Modul ini bergantung ke logic, bukan langsung ke output. Pemisahan ini memungkinkan parser diuji dengan logic yang dimock (mis. untuk cek error handling tanpa benar-benar mencetak).
  4. Baris 35-43 (main): Entry point. Hanya bergantung ke parser—tidak peduli apa yang dilakukan logic atau output. Jika kita mengganti output_user menjadi kirim ke Kafka, main tidak berubah.

CHANGELOG.md: Riwayat Versi #

CHANGELOG.md mencatat apa yang berubah di setiap rilis. Format yang banyak dipakai adalah Keep a Changelog (keepachangelog.com), yang mengelompokkan perubahan ke empat kategori:

  • Added: fitur baru
  • Changed: perubahan pada fitur yang ada
  • Deprecated: fitur yang akan dihapus
  • Removed: fitur yang sudah dihapus
  • Fixed: perbaikan bug
  • Security: perbaikan kerentanan keamanan

Berikut contoh CHANGELOG.md:

markdown
# Changelog

## [1.2.3] - 2025-01-15
### Added
- Flag `--version` untuk menampilkan versi dan changelog ringkas

## [1.2.2] - 2025-01-10
### Fixed
- Buffer overflow di parser saat input lebih panjang dari 32 karakter

## [1.2.0] - 2025-01-01
### Added
- Modul logging ke stderr dengan level INFO/WARN/ERROR

## [1.0.0] - 2024-12-01
- Rilis awal

Berikut program C yang membaca changelog dari kode (versi kompilasi) dan menampilkannya saat --version dipanggil—sinkron dengan CHANGELOG.md:

c
/* File: test_dok2.c - CHANGELOG-driven version printing */
#include <stdio.h>

#define APP_NAME    "demo-dok"
#define APP_VERSION "1.2.3"
#define APP_DATE    "2025-01-15"

/* Tampilan changelog compact di --version */
static void print_version(void)
{
    printf("%s %s (%s)\n", APP_NAME, APP_VERSION, APP_DATE);
    printf("\n");
    printf("Changelog:\n");
    printf("  1.2.3 (2025-01-15): Tambah --version flag\n");
    printf("  1.2.2 (2025-01-10): Fix buffer overflow di parser\n");
    printf("  1.2.0 (2025-01-01): Tambah modul logging\n");
    printf("  1.0.0 (2024-12-01): Rilis awal\n");
}

int main(int argc, char **argv)
{
    if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'V') {
        print_version();
        return 0;
    }
    printf("%s %s\n", APP_NAME, APP_VERSION);
    return 0;
}

Compile dan jalankan dengan dua skenario:

bash
gcc -std=c11 -Wall -Wextra -o demo-dok2 test_dok2.c
./demo-dok2
./demo-dok2 -V

Output yang Dihasilkan (gabungan dua skenario):

text
demo-dok 1.2.3
demo-dok 1.2.3 (2025-01-15)

Changelog:
  1.2.3 (2025-01-15): Tambah --version flag
  1.2.2 (2025-01-10): Fix buffer overflow di parser
  1.2.0 (2025-01-01): Tambah modul logging
  1.0.0 (2024-12-01): Rilis awal

Versi di kode (APP_VERSION "1.2.3") sinkron dengan versi terbaru di CHANGELOG.md. Saat developer merilis versi baru, mereka memperbarui CHANGELOG.md terlebih dahulu, lalu mengubah APP_VERSION dan APP_DATE di kode. Beberapa proyek otomatisasi (CI/CD) menolak merge jika kedua tempat tidak konsisten.

LICENSE: Wajib di Setiap Proyek Open-Source #

Tanpa file LICENSE, proyek secara default all rights reserved—tidak boleh dipakai, dimodifikasi, atau didistribusi ulang oleh siapapun. Untuk proyek open-source, pilih salah satu lisensi populer:

  • MIT License: permisif, kompatibel dengan mayoritas proyek. Cocok untuk library dan tool kecil.
  • Apache 2.0: permisif dengan perlindungan paten. Cocok untuk proyek enterprise.
  • GPL v3: copyleft—derivatif harus juga GPL. Cocok untuk proyek yang ingin menjamin kebebasan pengguna.
  • BSD 2-Clause / 3-Clause: permisif mirip MIT, dengan klausa tambahan.

Untuk proyek C yang ingin dipakai luas, MIT atau Apache 2.0 adalah pilihan paling umum. cURL, SQLite, dan OpenSSL memakai lisensi yang permisif agar dapat di-include di produk komersial.

File Pendukung Lainnya #

Selain tiga file inti di atas, beberapa file pendukung yang biasa ada di proyek C:

  • CONTRIBUTING.md: panduan untuk kontributor—cara setup dev environment, style guide, proses submit PR.
  • CODE_OF_CONDUCT.md: kode etik interaksi antar kontributor (Adopt Contributor Covenant).
  • .clang-format: konfigurasi formatter otomatis (lihat bab Formatting dan Indentation).
  • .editorconfig: konfigurasi editor lintas IDE (indentasi, line ending).
  • Doxyfile: konfigurasi Doxygen untuk generate dokumentasi HTML dari komentar /** */.