Komentar yang baik bukan terjemahan baris kode ke bahasa natural—itu pekerjaan yang bisa dilakukan compiler atau AI. Komentar yang bernilai menjelaskan mengapa sebuah keputusan diambil, batasan apa yang dipaksakan oleh domain, dan trade-off apa yang ditolak. Bab ini membedakan komentar buruk (yang menjelaskan what) dari komentar baik (yang menjelaskan why), memperkenalkan gaya Doxygen untuk dokumentasi API publik, dan menunjukkan kapan marker TODO, FIXME, NOTE bermanfaat untuk pelacakan utang teknis. Setelah membaca bab ini, kita akan tahu kapan sebuah komentar menambah nilai dan kapan sebuah komentar justru menjadi sumber kebingungan.
Aturan Inti: WHY, Bukan WHAT #
Komentar yang menjelaskan what mengulang apa yang sudah jelas dari kode. Komentar seperti i++; /* tambah i dengan 1 */ tidak menambah informasi—justru menambah noise visual. Sebaliknya, komentar yang menjelaskan why memberi konteks yang tidak tersedia di kode: regulasi yang mendasari rumus, bug historis yang sudah diperbaiki, atau trade-off performa yang sengaja dipilih.
/* File: komentar1.c - Komentar buruk vs komentar yang menjelaskan "why" */
#include <stdio.h>
/* Versi BURUK: komentar hanya mengulang apa yang sudah jelas dari kode */
static int hitung_pajak_bad(int gaji)
{
int pajak = gaji * 5 / 100; /* hitung pajak 5% */
return pajak;
}
/* Versi BAIK: komentar menjelaskan kenapa rumus ini dipakai */
static int hitung_pajak_good(int gaji)
{
/* Tarif 5% ditetapkan oleh UU PPh Orang Pribadi final untuk UMKM
dengan omzet < 4.8 miliar setahun (PP 55/2022). Bulatkan ke bawah
agar tidak menagih lebih dari yang seharusnya. */
return (gaji * 5) / 100;
}
int main(void)
{
int gaji = 5000000;
printf("Pajak bad : %d\n", hitung_pajak_bad(gaji));
printf("Pajak good: %d\n", hitung_pajak_good(gaji));
return 0;
}Output yang Dihasilkan:
Pajak bad : 250000
Pajak good: 250000Pada contoh di atas, komentar /* hitung pajak 5% */ adalah duplikasi total dari statement gaji * 5 / 100. Pembaca kode dapat melihat sendiri operasi tersebut—komentar tidak menambah informasi. Sebaliknya, komentar di fungsi hitung_pajak_good menjelaskan dasar hukum tarif 5%, mengapa pembulatan ke bawah dipilih, dan acuan regulasi yang menjadi sumbernya. Jika kelak ada orang yang ingin mengubah tarif ke 3%, mereka tahu harus mengecek PP 55/2022 terlebih dahulu.
Doxygen-style untuk API Publik #
Ketika sebuah fungsi adalah bagian dari API publik modul, dokumentasi terstruktur membantu pengguna memahami kontrak tanpa membaca implementasi. Doxygen adalah generator dokumentasi standar de facto untuk C/C++; ia membaca komentar dengan tag khusus (@param, @return, @brief) lalu menghasilkan HTML atau PDF.
Tiga bentuk komentar yang dikenali Doxygen:
/** ... */— blok dokumentasi (JavaDoc style)/// ...— blok dokumentasi pendek satu baris/*! ... */— blok dokumentasi alternatif (Qt style)
/* File: komentar2.c - Doxygen-style comments untuk API documentation */
#include <stdio.h>
#include <string.h>
/**
* @brief Tambahkan dua bilangan integer dengan overflow check.
*
* Fungsi ini melakukan penjumlahan dan memeriksa apakah hasil
* overflow terjadi. Jika overflow, nilai balik di-set ke INT_MAX
* dan status di-set ke -1.
*
* @param a Operand pertama.
* @param b Operand kedua.
* @param status Pointer ke int. 0 jika sukses, -1 jika overflow.
* @return Hasil penjumlahan, atau INT_MAX jika overflow.
*/
static int tambah_aman(int a, int b, int *status)
{
int hasil = a + b;
/* Bit ke-31 (sign) overflow jika tanda a dan b sama tapi tanda
hasil berbeda. Lihat CERT C INT32-C. */
if (((a < 0) == (b < 0)) && ((hasil < 0) != (a < 0))) {
*status = -1;
return 0; /* nilai balik tidak bermakna saat overflow */
}
*status = 0;
return hasil;
}
/// Tampilkan hasil penjumlahan ke stdout.
/// @param label String deskriptif yang dicetak sebelum hasil.
/// @param nilai Angka yang akan dicetak.
static void cetak(const char *label, int nilai)
{
printf("%-12s: %d\n", label, nilai);
}
int main(void)
{
int s;
int r1 = tambah_aman(100, 200, &s);
cetak("100 + 200", r1);
printf("status : %d\n", s);
int r2 = tambah_aman(2147483647, 1, &s);
cetak("INT_MAX + 1", r2);
printf("status : %d\n", s);
return 0;
}Output yang Dihasilkan:
100 + 200 : 300
status : 0
INT_MAX + 1 : 0
status : -1Mari kita telaah baris-baris kode di atas:
- Baris 7-17 (
/** @brief ... */): Blok komentar Doxygen untuk fungsitambah_aman. Tag@briefmemberi ringkasan satu baris, lalu paragraf detail menjelaskan perilaku.@parammendokumentasikan setiap parameter secara eksplisit—tipenya, maknanya, dan batasannya. - Baris 22 (
/* Bit ke-31 (sign) overflow ... */): Komentar in-line yang menjelaskan why di balik tes(a < 0) == (b < 0). Tanpa komentar ini, pembaca harus menebak apakah tes tanda ini adalah optimasi atau koreksi bug. Acuan ke CERT C INT32-C memberi pintu masuk ke literatur resmi. - Baris 33-35 (
/// Tampilkan ...): Bentuk pendek///untuk fungsi helper yang perilakunya sederhana. Doxygen memperlakukan///sama dengan/** */untuk blok dokumentasi. - Baris 47 (
tambah_aman(2147483647, 1, &s)): Tes edge case untuk overflow.INT_MAX + 1di int 32-bit menghasilkan undefined behavior tanpa pengecekan; di sini pengecekan menangkap overflow dan mengembalikan status-1.
Marker TODO, FIXME, dan NOTE #
Ketika kita menulis kode cepat dan tahu ada bagian yang belum tuntas, marker seperti TODO, FIXME, dan NOTE memberi sinyal ke reviewer dan ke diri kita sendiri di masa depan. Berikut konvensi yang banyak dipakai:
TODO:pekerjaan yang belum selesai tetapi kode sudah berfungsi.FIXME:bug yang diketahui ada tetapi belum diperbaiki—kode mungkin tidak berfungsi benar.NOTE:informasi penting yang harus disadari reviewer (mis. trade-off, gotcha).HACK:solusi sementara yang tidak ideal dan perlu diganti.
/* File: komentar3.c - TODO, FIXME, NOTE markers */
#include <stdio.h>
#include <stdlib.h>
static const char *status_str(int rc)
{
/* NOTE: rc -1 dan -2 sengaja dipisah agar caller dapat membedakan
"input NULL" dari "out of memory" di log produksi. */
switch (rc) {
case 0: return "OK";
case -1: return "INVALID_INPUT";
case -2: return "OUT_OF_MEMORY";
default: return "UNKNOWN";
}
}
static int parse_konfigurasi(const char *path)
{
if (path == NULL) {
return -1;
}
/* TODO: ganti ke parser INI yang proper (mis. inih) ketika
proyek pindah dari format key=value ke section-based. */
FILE *f = fopen(path, "r");
if (f == NULL) {
return -2; /* dianggap OOM untuk sementara */
}
fclose(f);
return 0;
}
int main(void)
{
const char *paths[] = {NULL, "/tidak/ada/file"};
for (int i = 0; i < 2; i++) {
int rc = parse_konfigurasi(paths[i]);
printf("path[%d] -> rc=%d (%s)\n", i, rc, status_str(rc));
}
return 0;
}Output yang Dihasilkan:
path[0] -> rc=-1 (INVALID_INPUT)
path[1] -> rc=-2 (OUT_OF_MEMORY)Marker TODO pada baris 23 mengatakan bahwa parser saat ini hanya mengecek apakah file bisa dibuka—belum benar-benar memparse isi. Reviewer yang membaca TODO langsung paham bahwa pekerjaan ini belum tuntas. Marker NOTE pada baris 9 menjelaskan keputusan desain (memisahkan error code) yang tidak akan terlihat dari kode saja.
Prinsip Praktis #
Berikut ringkasan aturan yang biasa kami pakai di proyek C:
- Komentar publik = kontrak. Apa yang ditulis di
/** */di header harus tetap valid selama API tidak berubah. Update komentar saat API berubah—bukan saat implementasi berubah. - Komentar privat = why. Di dalam fungsi
static, komentar menjelaskan keputusan desain, bukan langkah kode. - Jangan duplikasi nama.
count++; /* increment count */adalah sampah visual. - Hindari komentar yang menjawab “apa” sebelum ditanya. Komentar
/* inisialisasi */di atasx = 0;tidak menambah apa-apa. - Komentar block-out (
#if 0) hanya untuk debugging sementara. Jangan commit kode yang di-comment; gunakangit revertatau delete saja.