Komentar

Komentar #

Komentar adalah bagian dari kode yang tidak dieksekusi oleh interpreter — tapi bukan berarti tidak penting. Komentar yang baik adalah perbedaan antara kode yang bisa dipahami dalam hitungan detik dan kode yang membuat developer lain (atau dirimu sendiri tiga bulan kemudian) menghabiskan berjam-jam hanya untuk memahami maksudnya. Di Ruby, ada dua sintaks komentar: satu baris dan multi-baris. Tapi jauh lebih penting dari sintaksnya adalah kapan dan bagaimana menulis komentar yang benar-benar bernilai. Artikel ini membahas keduanya, termasuk sistem dokumentasi RDoc dan YARD yang digunakan di ekosistem Ruby profesional.

Komentar Satu Baris #

Komentar satu baris adalah bentuk komentar yang paling sering digunakan di Ruby. Dimulai dengan tanda pagar (#) dan berlaku hingga akhir baris tersebut — interpreter Ruby akan mengabaikan semua teks setelah # di baris yang sama.

# Ini adalah komentar satu baris — seluruh baris diabaikan interpreter

x = 10  # Ini adalah inline comment — kode di sebelah kiri tetap dieksekusi

Komentar satu baris bisa diletakkan di baris tersendiri sebelum kode, atau di akhir baris kode (inline comment). Keduanya valid, tapi punya kegunaan yang berbeda.

# Hitung total harga setelah diskon diterapkan
total = harga_awal * (1 - diskon)

pajak = total * 0.11   # PPN 11% berlaku sejak April 2022

Komentar di baris tersendiri cocok untuk menjelaskan blok kode yang akan datang. Inline comment lebih baik untuk catatan kontekstual yang sangat spesifik untuk satu ekspresi — seperti angka ajaib yang butuh penjelasan.

Menonaktifkan Kode Sementara #

Salah satu kegunaan praktis komentar adalah menonaktifkan baris kode sementara selama debugging, tanpa menghapusnya.

def proses_pembayaran(order)
  validasi_stok(order)
  # kirim_notifikasi_email(order)   # dinonaktifkan sementara — lihat issue #412
  potong_saldo(order)
  buat_invoice(order)
end
Komentar yang menonaktifkan kode harus bersifat sementara. Jika sebuah baris kode sudah dikomentari selama lebih dari beberapa hari tanpa rencana jelas kapan akan diaktifkan kembali, kemungkinan besar baris itu memang tidak lagi dibutuhkan. Hapus saja — version control (Git) menyimpan historinya.

Komentar Multi-baris #

Ketika penjelasan yang dibutuhkan terlalu panjang untuk satu baris, Ruby menyediakan dua cara untuk menulis komentar yang mencakup beberapa baris.

Menggunakan =begin dan =end #

Ruby punya sintaks khusus untuk komentar multi-baris menggunakan pasangan =begin dan =end. Semua teks di antara kedua penanda ini akan diabaikan oleh interpreter.

=begin
Modul ini menangani seluruh alur autentikasi pengguna,
termasuk login, logout, refresh token, dan validasi sesi.

Dependensi:
  - JWT gem untuk enkripsi token
  - BCrypt untuk hashing password
  - Redis untuk penyimpanan sesi aktif

Penulis: Tim Backend
Terakhir diperbarui: 2024-03-15
=end

module AuthHandler
  # implementasi...
end

Ada satu aturan ketat yang harus dipatuhi: =begin dan =end harus berada di awal baris — tidak boleh ada spasi atau karakter apapun sebelumnya. Jika ada indentasi, Ruby tidak akan mengenalinya sebagai penanda komentar.

def contoh
  =begin
  ANTI-PATTERN: ini TIDAK akan berfungsi sebagai komentar!
  =begin dengan indentasi di depannya tidak dikenali Ruby.
  Baris ini justru akan menyebabkan SyntaxError.
  =end
end

Karena keterbatasan ini, =begin...=end hampir tidak pernah digunakan di dalam method atau class body. Penggunaannya paling tepat di level atas file — untuk header file, catatan lisensi, atau deskripsi modul.

Menggunakan Beberapa Komentar Satu Baris #

Dalam praktik nyata, cara yang jauh lebih umum untuk menulis komentar panjang adalah dengan menumpuk beberapa komentar # secara berurutan. Ini lebih fleksibel karena bisa diletakkan di mana saja, termasuk di dalam method atau class.

# Algoritma pencarian ini menggunakan pendekatan binary search
# dengan kompleksitas waktu O(log n). Array yang diberikan
# HARUS sudah terurut secara ascending sebelum metode ini dipanggil.
#
# Jika array tidak terurut, gunakan LinearSearch sebagai alternatif
# meskipun kompleksitasnya O(n).
def binary_search(arr, target)
  low  = 0
  high = arr.length - 1

  while low <= high
    mid = (low + high) / 2

    if arr[mid] == target
      return mid        # indeks ditemukan
    elsif arr[mid] < target
      low = mid + 1     # cari di separuh kanan
    else
      high = mid - 1    # cari di separuh kiri
    end
  end

  -1  # target tidak ditemukan
end

Perhatikan baris kosong # di atas — ini adalah konvensi yang banyak digunakan untuk memisahkan paragraf dalam komentar panjang, membuat komentar lebih mudah dipindai secara visual.

flowchart TD
    A[Butuh menulis komentar panjang?] --> B{Di dalam method\natau class body?}
    B -- Ya --> C["Gunakan beberapa # berturut-turut"]
    B -- Tidak --> D{Apakah ini header\nfile atau lisensi?}
    D -- Ya --> E["Boleh pakai =begin...=end\natau beberapa #"]
    D -- Tidak --> C
    C --> F["Pisahkan paragraf\ndengan baris # kosong"]
    E --> G[Letakkan di\nawal file]

Filosofi Komentar yang Baik #

Ini adalah bagian yang paling sering diabaikan tapi paling penting: bukan bagaimana menulis komentar secara sintaksis, tapi apa yang layak dikomentari.

Jelaskan Mengapa, Bukan Apa #

Kode yang baik sudah menjelaskan apa yang sedang dilakukan. Komentar yang hanya mengulang apa yang sudah jelas dari kode adalah noise — ia menambah panjang file tanpa menambah pemahaman.

# ANTI-PATTERN: komentar yang mengulang kode — tidak ada nilai tambah
# Menambahkan 1 ke counter
counter += 1

# Mengecek apakah user aktif
if user.aktif?
  # ...
end

# BENAR: komentar yang menjelaskan MENGAPA — memberikan konteks
counter += 1  # kompensasi offset 0-based dari API pihak ketiga

if user.aktif?
  # Hanya user aktif yang bisa mengakses endpoint ini.
  # User yang di-suspend masih ada di DB tapi tidak boleh login.
end

Komentari Keputusan yang Tidak Intuitif #

Komentar paling berharga adalah yang menjelaskan keputusan desain yang tidak obvious — kenapa kamu memilih pendekatan A dan bukan B, kenapa ada angka ajaib tertentu, atau kenapa ada workaround yang terlihat aneh.

# Gunakan sleep 0.5 di sini karena API payment gateway membutuhkan
# jeda minimal 500ms antara dua request berturut-turut dari IP yang sama.
# Tanpa jeda ini, request kedua akan di-rate-limit dan gagal dengan 429.
# Referensi: https://docs.paymentgw.com/rate-limiting
sleep 0.5
response = gateway.charge(amount)

# Formula bobot ini diambil dari paper Nielsen (1994) tentang
# usability heuristics. Nilai 0.1 untuk minor, 0.5 untuk major,
# 1.0 untuk catastrophic — sudah divalidasi dengan user testing internal.
skor_akhir = (minor * 0.1) + (major * 0.5) + (catastrophic * 1.0)

Komentar TODO dan FIXME #

Ruby (dan banyak editor/IDE) mengenali komentar dengan tag khusus yang berguna untuk menandai pekerjaan yang belum selesai.

# TODO: tambahkan validasi format email sebelum release v2.0
# FIXME: method ini crash jika input berupa string kosong — lihat issue #88
# HACK: workaround sementara untuk bug di library net-http versi 0.3.x
# NOTE: perilaku ini berbeda di Windows karena perbedaan line ending CRLF vs LF
# OPTIMIZE: query ini lambat untuk dataset >100k baris, pertimbangkan indexing

def simpan_user(email, nama)
  # FIXME: belum ada pengecekan duplikat email
  User.create(email: email, nama: nama)
end
Kapan gunakan tag komentar khusus:
  ✓ TODO  → pekerjaan yang direncanakan, tapi belum dikerjakan
  ✓ FIXME → bug yang diketahui dan harus diperbaiki
  ✓ HACK  → solusi sementara yang tidak ideal tapi sengaja dibiarkan
  ✓ NOTE  → informasi penting yang perlu diperhatikan developer berikutnya
  ✓ OPTIMIZE → kode yang berfungsi tapi performa-nya perlu ditingkatkan

  ✗ Jangan gunakan TODO sebagai pengganti tiket di issue tracker
  ✗ Jangan biarkan FIXME menumpuk tanpa ditindaklanjuti

Anti-Pattern Komentar yang Harus Dihindari #

# ANTI-PATTERN 1: komentar yang sudah tidak sinkron dengan kode
# Menghitung diskon 10%
diskon = harga * 0.15   # ✗ kodenya 15% tapi komentarnya bilang 10%

# ANTI-PATTERN 2: komentar yang menceritakan sejarah — gunakan Git
# Dulu menggunakan bubble sort tapi diganti quicksort pada 12 Jan 2023
# karena performa lebih baik. Sebelumnya dicoba merge sort tapi memory-nya
# terlalu besar untuk dataset besar.
def urutkan(arr)
  arr.sort   # gunakan built-in saja dan commit message yang deskriptif di Git
end

# ANTI-PATTERN 3: komentar penutup block yang tidak perlu
class UserService
  def initialize
    # ...
  end # def initialize   ← tidak perlu, Ruby sudah jelas
end # class UserService   ← tidak perlu untuk class pendek

# BENAR: komentar penutup hanya berguna untuk block yang SANGAT panjang
# dan layar tidak cukup untuk melihat pembuka dan penutupnya sekaligus
# ANTI-PATTERN 4: kode yang dikomentari tanpa penjelasan
def proses_data(input)
  # hasil = input.map { |x| x * 2 }
  # hasil = hasil.select { |x| x > 10 }
  hasil = input.filter_map { |x| x * 2 if x * 2 > 10 }
  hasil
end
# ✗ Kode yang dikomentari tanpa keterangan membingungkan —
#   apakah ini sengaja dipertahankan? Kenapa tidak dihapus saja?

# BENAR: jika memang perlu dipertahankan, beri konteks
def proses_data(input)
  # Versi lama yang lebih eksplisit — dipertahankan sebagai referensi
  # jika filter_map bermasalah di Ruby < 2.7:
  # hasil = input.map { |x| x * 2 }
  # hasil = hasil.select { |x| x > 10 }
  hasil = input.filter_map { |x| x * 2 if x * 2 > 10 }
  hasil
end

RDoc — Dokumentasi Standar Ruby #

RDoc adalah sistem dokumentasi bawaan Ruby. Ketika kamu menjalankan ri Array atau membaca dokumentasi di ruby-doc.org, output yang kamu lihat dihasilkan dari komentar RDoc yang ada di source code Ruby itu sendiri. Dengan kata lain, komentar RDoc adalah cara kamu menulis dokumentasi yang bisa dibaca mesin sekaligus manusia.

RDoc membaca komentar # yang diletakkan tepat di atas deklarasi kelas, modul, atau method — tanpa baris kosong di antaranya.

Dokumentasi Kelas dan Modul #

# Mengelola operasi CRUD untuk entitas Produk dalam sistem inventori.
#
# Kelas ini bertanggung jawab atas validasi data produk, penyimpanan
# ke database, dan sinkronisasi dengan sistem warehouse eksternal.
#
# == Contoh Penggunaan
#
#   produk = Produk.new(nama: "Laptop", harga: 15_000_000)
#   produk.simpan
#   puts produk.id  # => 1
#
# == Catatan
#
# Kelas ini tidak thread-safe. Gunakan Mutex jika diakses dari
# beberapa thread secara bersamaan.
class Produk
  # implementasi...
end

Dokumentasi Method #

RDoc untuk method paling berguna ketika method tersebut punya parameter, nilai kembalian, atau kemungkinan exception yang tidak obvious dari nama method-nya saja.

# Menghitung harga akhir produk setelah diskon dan pajak.
#
# Urutan kalkulasi: harga_dasar → kurangi diskon → tambah pajak.
# Diskon diterapkan terlebih dahulu sebelum pajak dihitung.
#
# @param harga_dasar [Float, Integer] Harga sebelum diskon dan pajak.
#   Harus bernilai positif.
# @param diskon [Float] Persentase diskon dalam bentuk desimal (0.0 - 1.0).
#   Contoh: 0.1 untuk diskon 10%.
# @param tarif_pajak [Float] Tarif pajak dalam bentuk desimal.
#   Default menggunakan PPN 11% (0.11).
#
# @return [Float] Harga akhir setelah diskon dan pajak dibulatkan
#   ke dua desimal.
#
# @raise [ArgumentError] Jika harga_dasar negatif atau diskon di luar
#   rentang 0.0 - 1.0.
#
# @example Harga normal dengan PPN standar
#   hitung_harga(100_000, 0.1)         # => 99_000.0
#
# @example Harga dengan tarif pajak kustom
#   hitung_harga(100_000, 0.2, 0.05)   # => 84_000.0
def hitung_harga(harga_dasar, diskon, tarif_pajak = 0.11)
  raise ArgumentError, "Harga tidak boleh negatif" if harga_dasar < 0
  raise ArgumentError, "Diskon harus antara 0 dan 1" unless (0.0..1.0).include?(diskon)

  setelah_diskon = harga_dasar * (1 - diskon)
  setelah_pajak  = setelah_diskon * (1 + tarif_pajak)
  setelah_pajak.round(2)
end

Dokumentasi Konstanta dan Atribut #

class KonfigurasiServer
  # Batas maksimal koneksi database yang diperbolehkan per instance.
  # Nilai ini ditentukan berdasarkan kapasitas connection pool PostgreSQL
  # dan jumlah worker Puma yang berjalan secara bersamaan.
  MAX_KONEKSI = 25

  # Timeout request dalam detik sebelum koneksi dianggap gagal.
  # Nilai lebih dari 30 detik tidak direkomendasikan karena akan
  # mempengaruhi UX pengguna secara signifikan.
  TIMEOUT_DETIK = 30

  # @return [String] Hostname atau IP server database yang aktif.
  attr_reader :host_db

  # @return [Integer] Port yang digunakan untuk koneksi.
  #   Default: 5432 untuk PostgreSQL.
  attr_accessor :port
end

Menghasilkan Dokumentasi RDoc #

Setelah komentar RDoc ditulis, kamu bisa menghasilkan dokumentasi HTML dengan perintah berikut:

# Generate dokumentasi untuk satu file:
rdoc lib/produk.rb

# Generate dokumentasi untuk seluruh proyek:
rdoc lib/

# Generate dengan format yang lebih kaya (template darkfish):
rdoc --format darkfish lib/

# Hasilkan dokumentasi dan buka langsung di browser:
rdoc --format darkfish lib/ && open doc/index.html

Output-nya adalah folder doc/ berisi file HTML yang bisa dibuka di browser atau di-hosting sebagai situs dokumentasi.


YARD — Standar Dokumentasi Modern Ruby #

Meskipun RDoc adalah bawaan Ruby, ekosistem modern Ruby lebih banyak menggunakan YARD (Yet Another Ruby Documentation). YARD kompatibel dengan RDoc tapi menawarkan sintaks yang lebih kaya, dukungan tipe data yang lebih ekspresif, dan output yang lebih baik.

# Instal YARD:
gem install yard

# Generate dokumentasi:
yard doc lib/

# Jalankan server dokumentasi lokal:
yard server

Perbandingan Sintaks RDoc vs YARD #

KebutuhanRDocYARD
Parameter# @param [Tipe] nama Deskripsi# @param nama [Tipe] Deskripsi
Return value# @return [Tipe] Deskripsi# @return [Tipe] Deskripsi
Exception# @raise [Tipe] Deskripsi# @raise [Tipe] Deskripsi
Contoh# == Contoh# @example Judul
Deprecatedmanual# @deprecated Pesan
Authormanual# @author Nama
Versionmanual# @since versi

Contoh Dokumentasi Lengkap dengan YARD #

# Layanan untuk mengirim notifikasi ke berbagai channel (email, SMS, push).
#
# Setiap notifikasi dikirim secara asinkron menggunakan background job.
# Gunakan {NotifikasiService#kirim_sinkron} jika butuh konfirmasi langsung.
#
# @example Kirim notifikasi email
#   svc = NotifikasiService.new
#   svc.kirim(user, :email, "Verifikasi akun kamu")
#
# @example Kirim ke beberapa channel sekaligus
#   svc.kirim_multi(user, [:email, :sms], "Reset password berhasil")
#
# @since 1.2.0
# @author Tim Platform
class NotifikasiService

  # Mengirim notifikasi ke satu channel secara asinkron.
  #
  # @param penerima [User] Objek user penerima notifikasi.
  #   User harus punya email atau nomor telepon yang terverifikasi.
  # @param channel [Symbol] Channel pengiriman. Nilai yang valid:
  #   `:email`, `:sms`, `:push`, `:in_app`.
  # @param pesan [String] Isi notifikasi. Maksimal 500 karakter untuk SMS.
  # @param prioritas [Symbol] Tingkat prioritas pengiriman.
  #   `:normal` (default) diproses dalam 5 menit, `:tinggi` dalam 30 detik.
  #
  # @return [String] Job ID dari background job yang dibuat.
  #   Gunakan ID ini untuk melacak status pengiriman.
  #
  # @raise [ArgumentError] Jika channel tidak valid.
  # @raise [User::TidakAktifError] Jika akun penerima di-suspend.
  #
  # @example
  #   job_id = svc.kirim(user, :email, "Selamat datang!")
  #   puts job_id  # => "job_a1b2c3d4"
  def kirim(penerima, channel, pesan, prioritas: :normal)
    # implementasi...
  end
end

Magic Comments di Ruby #

Selain komentar biasa dan dokumentasi, Ruby juga mengenal magic comments — komentar khusus di baris pertama atau kedua file yang mengubah perilaku interpreter.

# frozen_string_literal: true

# Magic comment di atas membuat semua string literal di file ini
# menjadi frozen (immutable). Ini meningkatkan performa karena Ruby
# tidak perlu mengalokasikan objek String baru setiap kali literal
# yang sama ditemukan.

nama = "Ruby"
nama << " on Rails"  # => FrozenError: can't modify frozen String
# encoding: utf-8

# Menentukan encoding file secara eksplisit.
# Sejak Ruby 2.0, UTF-8 adalah default, tapi ini berguna
# ketika bekerja dengan file yang punya encoding berbeda.
# warn_indent: true

# Mengaktifkan peringatan jika indentasi kode tidak konsisten.
# Berguna saat refactoring kode lama yang mungkin punya
# inkonsistensi indentasi.
flowchart TD
    A[Jenis Komentar di Ruby] --> B[Komentar Biasa]
    A --> C[Magic Comments]
    A --> D[Dokumentasi]
    B --> B1["# satu baris"]
    B --> B2["=begin...=end\nmulti-baris"]
    B --> B3["Tag khusus\nTODO/FIXME/HACK"]
    C --> C1["frozen_string_literal: true"]
    C --> C2["encoding: utf-8"]
    C --> C3["warn_indent: true"]
    D --> D1["RDoc\nbawaan Ruby"]
    D --> D2["YARD\nstandar modern"]

Komentar dalam Konteks Tim #

Komentar punya dimensi sosial yang sering diabaikan — ia adalah komunikasi tertulis yang kamu tinggalkan untuk developer lain (atau dirimu sendiri di masa depan). Beberapa prinsip yang berlaku di tim:

Komentar harus tetap sinkron dengan kode. Komentar yang sudah tidak akurat lebih berbahaya dari tidak ada komentar sama sekali — ia menyesatkan. Setiap kali mengubah kode, periksa apakah komentar di sekitarnya masih relevan.

Kode yang butuh banyak komentar untuk dipahami adalah sinyal refactoring. Jika kamu perlu menulis paragraf panjang untuk menjelaskan sebuah method, pertimbangkan apakah method itu bisa dipecah menjadi beberapa method yang lebih kecil dengan nama yang lebih deskriptif.

# ANTI-PATTERN: method kompleks yang butuh banyak komentar
def proses(u, o, p)
  # u adalah user, o adalah order, p adalah payment
  # Cek dulu apakah user aktif dan order belum dibayar
  # kemudian validasi payment method yang dipilih
  # lalu potong saldo atau charge kartu
  # terakhir update status order dan kirim email konfirmasi
  return false unless u.aktif? && o.belum_dibayar?
  return false unless p.valid?
  p.proses
  o.tandai_lunas
  UserMailer.konfirmasi_pembayaran(u, o).deliver_later
  true
end

# BENAR: method yang nama dan strukturnya sudah self-documenting
def selesaikan_pembayaran(user, order, payment)
  return false unless user_dan_order_valid?(user, order)
  return false unless payment.valid?

  proses_transaksi(payment, order)
  kirim_konfirmasi(user, order)
  true
end

private

def user_dan_order_valid?(user, order)
  user.aktif? && order.belum_dibayar?
end

def proses_transaksi(payment, order)
  payment.proses
  order.tandai_lunas
end

def kirim_konfirmasi(user, order)
  UserMailer.konfirmasi_pembayaran(user, order).deliver_later
end

Ringkasan #

  • **Dua sintaks komentar ** — # untuk satu baris (paling umum), =begin...=end untuk multi-baris (terbatas di level atas file).
  • Komentar tumpuk # lebih fleksibel dari =begin...=end — bisa diletakkan di mana saja termasuk di dalam method dan class body.
  • Jelaskan mengapa, bukan apa — kode sudah menjelaskan apa yang dilakukan; komentar terbaik menjelaskan alasan di balik keputusan desain yang tidak intuitif.
  • Tag TODO/FIXME/HACK berguna untuk menandai pekerjaan yang belum selesai, tapi jangan biarkan menumpuk tanpa ditindaklanjuti.
  • Magic comments mengubah perilaku interpreter# frozen_string_literal: true adalah yang paling umum dan direkomendasikan untuk performa.
  • RDoc adalah sistem dokumentasi bawaan Ruby — komentar di atas kelas dan method bisa diubah menjadi dokumentasi HTML dengan perintah rdoc.
  • YARD adalah standar dokumentasi modern — lebih ekspresif dari RDoc dengan sintaks @param, @return, @raise, @since, dan lainnya.
  • Komentar yang tidak sinkron lebih berbahaya dari tidak ada komentar — selalu perbarui komentar ketika mengubah kode di sekitarnya.
  • Kode yang butuh banyak komentar adalah sinyal refactoring — nama method dan variabel yang deskriptif mengurangi kebutuhan komentar secara signifikan.

← Sebelumnya: Sintaks Utama   Berikutnya: Variabel →

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