Overview
Code comments are annotations within source code that explain logic, intent, constraints, and context. Comments exist outside the execution path and serve as documentation for developers who read the code. In Ruby, comments begin with a hash symbol and extend to the end of the line, or span multiple lines within block comment delimiters.
The primary purpose of comments is to communicate information that the code itself cannot express. While code shows what happens and how it happens, comments explain why decisions were made, what assumptions exist, and what trade-offs were considered. Comments document complex algorithms, clarify non-obvious behavior, and preserve historical context about design decisions.
Ruby's dynamic nature and emphasis on readability creates specific commenting needs. The language allows metaprogramming, duck typing, and runtime modification of classes, which can make code behavior less obvious from static analysis. Comments help document these dynamic features and their implications.
# Documents a method that modifies class behavior at runtime
def self.define_accessor(name)
# Creates getter and setter dynamically
# Avoids repetitive method definitions
define_method(name) { instance_variable_get("@#{name}") }
define_method("#{name}=") { |val| instance_variable_set("@#{name}", val) }
end
The relationship between comments and code quality is counterintuitive: more comments do not indicate better documentation. Excessive comments often signal unclear code that requires explanation. The goal is to write self-documenting code that needs minimal commentary, then add comments only where they provide value that code alone cannot convey.
Comments fall into two categories: documentation comments and inline comments. Documentation comments describe the external interface of classes, modules, and methods. These comments follow structured formats like RDoc or YARD and generate API documentation. Inline comments appear within method bodies and explain implementation details.
Key Principles
Comments explain why, not what. Code already shows what operations occur and how they execute. Comments that merely restate code logic create noise and maintenance burden. The value of comments comes from explaining reasoning, context, and decisions that the code cannot express.
# Bad: Restates what the code does
# Set the user name to the parameter value
user.name = params[:name]
# Good: Explains why this approach was chosen
# Assign name directly instead of using mass assignment
# to avoid security issues with strong parameters
user.name = params[:name]
Self-documenting code reduces comment needs. Clear variable names, descriptive method names, and logical structure communicate intent better than comments. When code requires extensive explanation, refactoring often provides more value than adding comments.
# Bad: Code requires comment to explain
# Check if user can view
if u.r == 'admin' || (u.r == 'editor' && u.d == d.id)
# Good: Code explains itself
if user.admin? || user.owns?(document)
Comments must stay synchronized with code. Outdated comments mislead developers and create bugs. Comments that contradict code are worse than no comments. Every code change requires evaluating whether associated comments need updates. This maintenance cost means comments should exist only where they provide significant value.
API documentation differs from inline comments. Public methods, classes, and modules require documentation comments that describe their purpose, parameters, return values, and usage. These structured comments generate reference documentation. Internal implementation details use inline comments sparingly, only when the code cannot be made self-explanatory.
Comments should be grammatically correct. Comments are prose written for human readers. They should use complete sentences with proper punctuation and capitalization. This professionalism improves readability and shows respect for future maintainers. However, brief annotations can use fragments when the meaning is clear.
Comments should not compensate for bad code. If code requires extensive explanation, the code should be improved. Extract complex logic into well-named methods, introduce explaining variables for complex conditions, and restructure confusing control flow. Add comments only after code clarity is maximized.
Comments express assumptions and constraints. Code cannot document what must be true for it to work correctly, what inputs are valid, or what preconditions must hold. Comments capture these requirements explicitly.
def process_payment(amount)
# Assumes amount is in cents (integer)
# Caller must validate amount > 0
# Will raise PaymentError if gateway unavailable
gateway.charge(amount)
end
Comments document non-obvious decisions. When multiple valid approaches exist, comments explain why one was chosen. This preserves the reasoning for future developers who might question the decision.
def calculate_score
# Use integer division to match legacy system expectations
# Floating point version available but breaks compatibility
(total / count).to_i
end
TODO comments track incomplete work. Comments can mark areas that need future attention, but these must be actionable and ideally linked to issue tracking systems. Vague TODOs without context create technical debt.
# TODO(username): Extract payment logic to service object
# Issue #1234 - Blocked until gateway refactor completes
def process_order
# Current inline implementation
end
Ruby Implementation
Ruby uses the hash symbol for single-line comments and supports block comments with the =begin and =end delimiters. Single-line comments are idiomatic and preferred in practice.
# Single-line comment
x = 5 # Inline comment after code
=begin
Block comment
Spans multiple lines
Rarely used in practice
=end
Documentation comments use structured formats that documentation generators parse. RDoc is built into Ruby's standard library, while YARD provides enhanced features. Both use specially formatted comments preceding method definitions.
RDoc format uses simple markup within regular comments:
# Calculates the total cost including tax and shipping.
#
# amount:: The base price in cents (Integer)
# tax_rate:: Tax percentage as decimal (Float)
#
# Returns the total cost in cents (Integer)
# Raises ArgumentError if amount is negative
def calculate_total(amount, tax_rate)
raise ArgumentError, "amount must be positive" if amount < 0
(amount * (1 + tax_rate)).to_i
end
YARD format uses tags with @ symbols for structured metadata:
# Processes a payment through the payment gateway.
#
# @param amount [Integer] the amount in cents to charge
# @param currency [String] the three-letter currency code
# @param options [Hash] additional options
# @option options [String] :description transaction description
# @option options [Boolean] :capture whether to capture immediately
#
# @return [Payment] the created payment object
# @raise [PaymentError] if the gateway rejects the charge
# @raise [ArgumentError] if amount is not positive
#
# @example Process a basic payment
# payment = process_payment(1000, 'USD')
#
# @example Process with options
# payment = process_payment(
# 1000,
# 'USD',
# description: 'Monthly subscription',
# capture: false
# )
def process_payment(amount, currency, options = {})
# Implementation
end
Ruby's attr_accessor, attr_reader, and attr_writer dynamically define methods, so documentation must appear before these declarations:
class User
# The user's email address, must be unique and valid format
attr_accessor :email
# The user's role, one of: admin, editor, viewer
attr_reader :role
# The last login timestamp, nil if never logged in
attr_accessor :last_login_at
end
Module and class documentation appears immediately after the definition line:
# Handles user authentication and session management.
#
# This module provides methods for logging in users,
# validating credentials, and managing session tokens.
# Sessions expire after 24 hours of inactivity.
module Authentication
# Module implementation
end
Magic comments control file encoding and behavior. These special comments must appear on the first line or second line if a shebang is present:
# frozen_string_literal: true
# encoding: UTF-8
class Example
# Class implementation
end
The frozen_string_literal magic comment makes all string literals in the file immutable, improving performance and preventing accidental modifications. This comment documents an important constraint on string usage throughout the file.
Ruby's commenting conventions differ from languages with block comment styles. Inline comments typically appear on their own line above the code they describe, rather than at the end of lines:
# Preferred: Comment above code
# Calculates adjusted score based on difficulty
score = base_score * difficulty_multiplier
# Acceptable for brief annotations
score = base_score * difficulty_multiplier # Adjusted for difficulty
# Avoid: Long inline comments
score = base_score * difficulty_multiplier # Score is calculated by multiplying the base by difficulty which ranges from 0.5 to 2.0
Comments within method bodies document complex algorithms, non-obvious logic, or important constraints:
def complex_calculation(data)
# Phase 1: Normalize input data
# Remove outliers beyond 2 standard deviations
# This prevents skewing results in small datasets
normalized = remove_outliers(data)
# Phase 2: Apply weighted average
# Weights come from historical performance data
weighted = apply_weights(normalized, @weights)
# Phase 3: Apply calibration curve
# Curve coefficients determined through regression analysis
# See research/calibration_2024.pdf for derivation
calibrate(weighted)
end
Temporary debugging comments should be removed before committing code. If debugging information has ongoing value, convert it to proper logging statements:
# Bad: Leftover debugging
# puts "DEBUG: user = #{user.inspect}"
def process_user(user)
# Implementation
end
# Good: Proper logging
def process_user(user)
logger.debug("Processing user: #{user.id}")
# Implementation
end
Common Patterns
Intent comments explain the purpose or goal of a code section when the implementation is complex or non-obvious. These comments state what the code accomplishes at a higher level of abstraction.
# Find the first available delivery date
# that doesn't fall on a weekend or holiday
available_date = dates.find do |date|
!date.weekend? && !holidays.include?(date)
end
Constraint comments document requirements, limitations, or assumptions that must hold for the code to work correctly. These comments prevent misuse and document preconditions.
# Input must be sorted ascending for binary search
# Performance degrades to O(n) if unsorted
def binary_search(array, target)
# Implementation assumes sorted input
end
# Maximum batch size is 1000 records
# Larger batches cause database timeout
def process_batch(records)
raise ArgumentError, "batch too large" if records.size > 1000
# Processing logic
end
Historical context comments explain why code exists in its current form, especially when the reasoning is non-obvious. These comments prevent well-intentioned refactoring that reintroduces previous problems.
# Cannot use concurrent processing here
# Previous implementation caused race conditions
# that corrupted user data (incident #2847)
records.each do |record|
process_record(record)
end
# Using deprecated API because new API doesn't support
# batch operations needed for performance
# Planned migration to new API in Q3 2025
LegacyAPI.batch_update(records)
Algorithm description comments document complex algorithms, especially when implementing standard algorithms or mathematical formulas. These comments reference sources and explain the approach.
# Implements Dijkstra's shortest path algorithm
# Time complexity: O((V + E) log V) with binary heap
# Space complexity: O(V) for distance and predecessor tracking
def shortest_path(graph, start, finish)
distances = {}
predecessors = {}
queue = PriorityQueue.new
# Algorithm implementation
end
# Haversine formula for great-circle distance between two points
# Distance in kilometers on Earth's surface
# Formula: a = sin²(Δφ/2) + cos(φ1) * cos(φ2) * sin²(Δλ/2)
# c = 2 * atan2(√a, √(1−a))
# d = R * c
def calculate_distance(lat1, lon1, lat2, lon2)
# Implementation
end
Workaround comments document code that works around bugs, limitations, or quirks in external systems. These comments explain why unusual code exists and when it might be removed.
# Workaround for bug in third_party_gem v2.3.1
# Bug filed: https://github.com/project/issue/456
# Remove this conditional when fixed version released
if ThirdPartyGem::VERSION == "2.3.1"
# Alternative implementation
else
# Normal implementation
end
# SQLite doesn't support ALTER COLUMN
# Use create/copy/drop pattern instead
# Can remove when PostgreSQL-only (migration to PG planned)
def migrate_column_type
# Workaround implementation
end
Performance optimization comments explain code that sacrifices readability for performance. These comments include benchmarks or measurements justifying the optimization.
# Use string concatenation instead of interpolation
# Benchmarked 35% faster for large datasets (n > 10000)
# See benchmarks/string_building.rb for details
result = ""
data.each { |item| result << item.to_s }
# Cache reflection results to avoid repeated lookups
# Reduces method call overhead from O(n) to O(1)
@attribute_names ||= self.class.attribute_names
Security consideration comments highlight security-sensitive code and explain the security measures in place. These comments help security audits and prevent accidental removal of protections.
# Sanitize user input to prevent SQL injection
# Do not use string interpolation for queries
# Use parameterized queries only
query = "SELECT * FROM users WHERE email = ?"
db.execute(query, [user_input])
# Constant-time comparison prevents timing attacks
# Do not use == for password or token comparison
if ActiveSupport::SecurityUtils.secure_compare(token, expected_token)
# Authenticated
end
Configuration comments document configuration values, especially magic numbers or thresholds chosen through experimentation or business requirements.
# Connection timeout in seconds
# Set to 30 based on 95th percentile response time analysis
# Shorter timeout causes false failures during peak load
CONNECTION_TIMEOUT = 30
# Batch size tuned for optimal memory usage
# Smaller batches increase overhead
# Larger batches risk out-of-memory errors
BATCH_SIZE = 500
State machine comments document complex state transitions or business logic with multiple conditions. These comments clarify the rules and edge cases.
def update_order_status(order, event)
case order.status
when :pending
# Can only ship if payment confirmed and inventory available
# Cancellation allowed at any time in pending state
if event == :ship && order.payment_confirmed? && order.inventory_available?
order.status = :shipped
elsif event == :cancel
order.status = :cancelled
end
when :shipped
# Shipped orders can only move to delivered or returned
# Cannot cancel after shipping starts
order.status = :delivered if event == :deliver
order.status = :returned if event == :return
end
end
Practical Examples
Example 1: Poorly Commented Code
This example shows common commenting mistakes:
# User class
class User
# Gets the name
def name
@name
end
# Sets the name
def name=(value)
@name = value
end
# Processes the user
def process
# Check if active
if @active
# Do processing
perform_action
end
end
# Saves the user to database
def save
# Call the database
Database.save(self)
end
end
These comments fail to add value. They restate what the code already shows clearly. The comments become maintenance burden without improving understanding.
Example 2: Well-Commented Code
Improved version with meaningful comments:
# Represents a system user with authentication and authorization.
#
# Users can be active or inactive. Inactive users cannot authenticate
# or perform actions. Activation status persists across sessions.
class User
attr_accessor :name, :email
# Processes pending actions for the user.
#
# Only active users can have actions processed. This prevents
# accidentally processing actions for deactivated accounts.
# Background jobs call this method, so status check is critical.
def process
return unless active?
perform_action
end
# Persists user changes to the database.
#
# Validates email uniqueness before saving to prevent constraint
# violations. Validation happens here instead of database level
# to provide better error messages to API consumers.
def save
validate_email_uniqueness
Database.save(self)
end
private
def active?
@active == true
end
end
These comments explain business logic, constraints, and design decisions rather than restating obvious code behavior.
Example 3: Complex Algorithm Documentation
Documenting a non-trivial algorithm with context:
# Calculates optimal route for delivery vehicles using nearest neighbor heuristic.
#
# This is not the optimal solution (which requires solving TSP), but provides
# good-enough results in reasonable time for typical route sizes (< 100 stops).
#
# Algorithm:
# 1. Start at depot
# 2. Find nearest unvisited stop
# 3. Move to that stop and mark visited
# 4. Repeat until all stops visited
# 5. Return to depot
#
# Time complexity: O(n²) where n is number of stops
# Space complexity: O(n) for visited tracking
#
# @param stops [Array<Stop>] delivery stops to visit
# @param depot [Location] starting and ending location
# @return [Array<Stop>] ordered list of stops
def calculate_route(stops, depot)
route = []
unvisited = stops.dup
current = depot
# Continue until all stops visited
# Greedy selection of nearest neighbor at each step
until unvisited.empty?
# Find closest unvisited stop from current position
# Distance calculation uses Haversine formula (see distance_between method)
nearest = unvisited.min_by { |stop| distance_between(current, stop) }
route << nearest
unvisited.delete(nearest)
current = nearest
end
route
end
The comment provides context about algorithm choice, explains trade-offs, documents complexity, and guides the reader through the implementation.
Example 4: Metaprogramming Documentation
Ruby's metaprogramming requires careful documentation:
# Dynamically defines predicate methods for each status value.
#
# For a status :pending, creates methods:
# - pending? # Returns true if status == :pending
# - pending! # Sets status to :pending
# - not_pending? # Returns true if status != :pending
#
# This approach avoids repetitive method definitions while maintaining
# clear method names for each status type. The trade-off is reduced
# discoverability (methods don't appear in static analysis).
#
# @param statuses [Array<Symbol>] valid status values
def define_status_methods(*statuses)
statuses.each do |status|
# Predicate method: status?
define_method("#{status}?") do
self.status == status
end
# Setter method: status!
define_method("#{status}!") do
self.status = status
end
# Negated predicate: not_status?
define_method("not_#{status}?") do
self.status != status
end
end
end
# Defines all order status methods
# Status flow: pending -> processing -> shipped -> delivered
# Cancelled can occur from any state
define_status_methods(:pending, :processing, :shipped, :delivered, :cancelled)
The comments explain what gets generated, why this approach was chosen, and document the trade-offs involved.
Example 5: Integration Documentation
Documenting external system integration:
# Synchronizes order data with the warehouse management system.
#
# Integration details:
# - Uses REST API v2 (v1 deprecated January 2024)
# - Authentication via Bearer token (expires every 24 hours)
# - Rate limit: 100 requests per minute
# - Retries automatically on 5xx errors (max 3 attempts)
# - Idempotent operations using order_id as key
#
# Error handling:
# - 4xx errors: Log and skip (bad data, won't succeed on retry)
# - 5xx errors: Retry with exponential backoff
# - Network errors: Retry up to 3 times
# - Timeout after 30 seconds (warehouse SLA is 10 seconds)
#
# @param order [Order] the order to synchronize
# @raise [SyncError] if synchronization fails after all retries
def sync_to_warehouse(order)
# Convert to warehouse format
# Their API expects snake_case but uses inconsistent naming
# See docs/warehouse_api_mapping.md for field mappings
payload = {
order_number: order.id, # They call it order_number
customer_ref: order.customer_id, # customer_ref not customer_id
line_items: format_items(order.items)
}
# Retry logic with exponential backoff
# Initial delay: 1s, doubles each retry: 1s, 2s, 4s
retries = 0
begin
response = warehouse_client.post('/orders', payload)
log_sync_success(order, response)
rescue ServerError => e
retries += 1
if retries <= 3
sleep(2 ** (retries - 1))
retry
else
raise SyncError, "Failed after #{retries} retries: #{e.message}"
end
end
end
This detailed comment documents integration requirements, error handling strategy, and quirks in the external system.
Common Pitfalls
Commented-out code creates confusion and maintenance burden. Version control preserves code history, making commented-out code unnecessary. Developers cannot determine if commented code is outdated or represents future work.
# Bad: Unclear why code is commented
def calculate_total(items)
# total = items.sum(&:price)
# if discount_active?
# total *= 0.9
# end
items.sum(&:price)
end
# Good: Remove dead code, explain if needed
def calculate_total(items)
# Discount logic removed per requirement #1234
# Discount functionality moved to separate promotion system
items.sum(&:price)
end
Obvious comments add noise without value. If the code is self-explanatory, omit the comment. Comments should address gaps in code expressiveness, not repeat what the code states plainly.
# Bad: Comments state the obvious
# Increment counter
counter += 1
# Set user name
user.name = "John"
# Return true
return true
# Good: Only comment non-obvious aspects
# Increment before check to avoid off-by-one error
counter += 1
# Name comes from legacy system (cannot change format)
user.name = "John"
# Early return prevents unnecessary database query
return true
Outdated comments mislead developers and cause bugs. When code changes, associated comments must be updated. Outdated comments are worse than no comments because they actively deceive.
# Bad: Comment describes old behavior
# Returns user if email and password match
# Raises NotFoundError if user doesn't exist
def authenticate(email, password)
user = User.find_by_email(email)
return nil unless user # Actually returns nil now, not raising
user if user.password_matches?(password)
end
# Good: Comment matches current behavior
# Returns user if email and password match
# Returns nil if user not found or password incorrect
def authenticate(email, password)
user = User.find_by_email(email)
return nil unless user
user if user.password_matches?(password)
end
Vague TODO comments lack context and become technical debt. TODO comments should specify who is responsible, why the work is needed, and link to tracking issues.
# Bad: No context or ownership
# TODO: Fix this
# TODO: Refactor
# TODO: This could be better
# Good: Actionable with context
# TODO(jsmith): Extract to service object for better testability
# TODO(team-api): Migrate to v2 endpoint when available (Q2 2025)
# TODO(security): Add rate limiting - See issue #5678
Comment clutter occurs when every line has a comment. This makes code harder to read by adding visual noise. Comments should be strategic, not ubiquitous.
# Bad: Excessive commenting
def process_order(order)
# Check if order is valid
return unless order.valid?
# Get the items
items = order.items
# Calculate the total
total = items.sum(&:price)
# Apply tax
total *= 1.08
# Save the total
order.total = total
# Save the order
order.save
end
# Good: Comment only where needed
def process_order(order)
return unless order.valid?
total = order.items.sum(&:price)
# Tax rate is 8% (state rate, updated annually)
total *= 1.08
order.update(total: total)
end
Apologetic comments indicate code quality issues that should be fixed rather than documented. Comments like "ugly hack" or "this is messy" acknowledge problems without solving them.
# Bad: Apologizes instead of fixing
# This is a hack but it works
# Ugly code ahead, sorry
# This is terrible but I'm in a hurry
# Better: Explain necessity and plan
# Uses reflection to avoid boilerplate for 50+ similar methods
# Trade-off: Reduced readability for maintainability
# TODO(team): Consider code generation if pattern grows
Misleading abstraction levels occur when comments describe implementation details at the wrong level. High-level comments should describe intent and purpose, not line-by-line operations.
# Bad: Low-level details in high-level comment
# This method loops through each item
# Then it multiplies the price by quantity
# Then it adds them all up
def calculate_total(items)
items.sum { |item| item.price * item.quantity }
end
# Good: Describes purpose and important details
# Calculates order total including quantity discounts
# Does not include tax or shipping (added separately)
def calculate_total(items)
items.sum { |item| item.price * item.quantity }
end
Comments as crutch for bad names indicate the code needs refactoring. If a variable or method needs a comment to explain what it represents, rename it instead.
# Bad: Comment compensates for bad name
# Number of days until expiration
x = 30
# Days since last login
y = user.last_login_days
# Good: Self-documenting names
days_until_expiration = 30
days_since_last_login = user.days_since_last_login
Divider comments create visual sections but indicate the method is too long. Long methods should be broken into smaller methods with descriptive names rather than using comment dividers.
# Bad: Dividers in long method
def process_order(order)
# ========== Validation ==========
return unless order.valid?
# ========== Payment ==========
charge_customer(order)
# ========== Inventory ==========
reduce_inventory(order)
# ========== Notification ==========
send_confirmation(order)
end
# Good: Separate methods
def process_order(order)
return unless valid_order?(order)
charge_customer(order)
update_inventory(order)
notify_customer(order)
end
Tools & Ecosystem
RDoc generates HTML documentation from Ruby source code. RDoc is part of Ruby's standard library and requires no additional installation. RDoc parses structured comments and creates formatted documentation.
# Run RDoc from command line
# rdoc lib/**/*.rb
# Generate documentation for specific files
# rdoc lib/user.rb lib/order.rb
# Output to specific directory
# rdoc --op docs lib/**/*.rb
RDoc supports simple markup in comments:
# = Main Heading
# == Subheading
#
# Regular paragraph text.
#
# * Bullet point
# * Another bullet
#
# 1. Numbered item
# 2. Second item
#
# Code block with indentation
# Second line of code
#
# Links: https://example.com
# See also: OtherClass#method_name
def example_method
# Implementation
end
YARD provides enhanced documentation features beyond RDoc. YARD uses structured tags and supports more complex documentation patterns. Install via gem:
# Gemfile
gem 'yard'
# Generate documentation
# yard doc
# Start local documentation server
# yard server
YARD tags provide structured metadata:
# @param specifies parameter details
# @return documents return value
# @raise documents exceptions
# @example shows usage examples
# @see references related methods or documentation
# @deprecated marks deprecated code
# @note adds implementation notes
# @since documents when feature was added
# @version specifies version information
# @author attributes code to developer
Common YARD patterns:
# @overload documents multiple method signatures
# @overload process(data)
# @param data [Array] array of items
# @return [Array] processed items
# @overload process(data, options)
# @param data [Array] array of items
# @param options [Hash] processing options
# @return [Array] processed items with options applied
def process(*args)
# Implementation handles both signatures
end
# @api marks API stability
# @api public - Stable public API
# @api private - Internal implementation
# @api deprecated - Scheduled for removal
class ExampleClass
# @api public
def stable_method
end
# @api private
def internal_method
end
end
RuboCop enforces comment style guidelines. RuboCop's Style/Documentation cop requires public classes and modules to have documentation comments. Configure in .rubocop.yml:
Style/Documentation:
Enabled: true
Exclude:
- 'spec/**/*'
- 'test/**/*'
Style/CommentedKeyword:
Enabled: true
Layout/CommentIndentation:
Enabled: true
Layout/LeadingCommentSpace:
Enabled: true
Style/CommentAnnotation:
Enabled: true
Keywords:
- TODO
- FIXME
- OPTIMIZE
- HACK
- REVIEW
RuboCop detects common comment issues:
# Violation: Missing space after hash
#This comment lacks space
# Violation: Keyword in comment without colon
# TODO fix this should be TODO: fix this
# Violation: Misaligned comment
def method
# Comment not aligned with method body
code
end
# Correct: Properly formatted comments
# This comment has proper spacing
# TODO: Fix this implements the standard format
def method
# Comment aligns with method body
code
end
Inch measures documentation coverage. Inch evaluates whether classes and methods have adequate documentation and generates coverage reports:
# Install inch
gem install inch
# Run documentation coverage report
inch
# Show undocumented methods
inch list --undocumented
# Show documentation quality scores
inch stats --format=badge
Documentation testing with doctest validates example code in documentation comments:
# Gemfile
gem 'yard-doctest'
# Documentation with testable examples
# @example Basic calculation
# Calculator.add(2, 3) #=> 5
# Calculator.add(0, 0) #=> 0
class Calculator
def self.add(a, b)
a + b
end
end
# Run doctests
# yard doctest
Git hooks enforce documentation standards. Pre-commit hooks can check for missing documentation on public methods:
# .git/hooks/pre-commit
#!/usr/bin/env ruby
# Check for undocumented public methods
files = `git diff --cached --name-only --diff-filter=ACM | grep .rb$`.split("\n")
files.each do |file|
content = File.read(file)
# Simple check for methods without preceding comments
content.scan(/^\s*def (self\.)?\w+/) do |match|
# Check if previous line is comment
# This is simplified - production version would be more sophisticated
puts "Warning: Potentially undocumented method in #{file}"
end
end
IDE integration provides inline documentation viewing. Most Ruby IDEs display YARD documentation when hovering over methods or displaying autocomplete suggestions. This makes good documentation immediately useful during development.
Configuration for documentation generation in build tools:
# Rakefile
require 'yard'
YARD::Rake::YardocTask.new do |t|
t.files = ['lib/**/*.rb']
t.options = ['--output-dir', 'docs', '--readme', 'README.md']
end
# Run with: rake yard
Reference
Comment Syntax
| Syntax | Usage | Example |
|---|---|---|
| # | Single-line comment | # This is a comment |
| =begin =end | Block comment (rare) | =begin Multi-line =end |
| #! | Shebang for executable | #!/usr/bin/env ruby |
| # encoding: | Encoding declaration | # encoding: UTF-8 |
| # frozen_string_literal: | String behavior | # frozen_string_literal: true |
RDoc Tags
| Tag | Purpose | Example |
|---|---|---|
| :call-seq: | Method signature | :call-seq: method(arg1, arg2) |
| :category: | Group methods | :category: Authentication |
| :section: | Define section | :section: Public API |
| :yields: | Block parameters | :yields: item, index |
| :include: | Include external file | :include: examples.rb |
YARD Tags
| Tag | Purpose | Example |
|---|---|---|
| @param | Parameter description | @param name [String] user name |
| @return | Return value | @return [Boolean] true if valid |
| @raise | Exception thrown | @raise [ArgumentError] if invalid |
| @example | Usage example | @example Basic usage |
| @see | Related reference | @see OtherClass#method |
| @deprecated | Deprecation notice | @deprecated Use new_method instead |
| @since | Added in version | @since 2.0.0 |
| @note | Important note | @note This modifies state |
| @option | Hash option | @option opts [String] :key |
| @overload | Method signature | @overload process(data) |
| @private | Internal method | @private |
| @api | API stability | @api public |
| @author | Code attribution | @author John Smith |
| @version | Version info | @version 1.0.0 |
Annotation Keywords
| Keyword | Purpose | Usage |
|---|---|---|
| TODO | Planned work | TODO: Refactor this method |
| FIXME | Known bug | FIXME: Handles edge case incorrectly |
| OPTIMIZE | Performance improvement | OPTIMIZE: Use better algorithm |
| HACK | Workaround | HACK: Works around library bug |
| REVIEW | Needs review | REVIEW: Verify this approach |
| NOTE | Important information | NOTE: Must be called before save |
| DEPRECATED | Scheduled for removal | DEPRECATED: Use new_method instead |
Comment Style Guidelines
| Guideline | Description |
|---|---|
| Capitalization | Start sentences with capital letters |
| Punctuation | End complete sentences with periods |
| Spacing | One space after # symbol |
| Indentation | Align with code indentation |
| Line length | Keep under 80-100 characters |
| Blank lines | Separate comment blocks with blank line |
| Position | Place comments above code, not after |
| Grammar | Use complete sentences for documentation |
Documentation Comment Structure
# Brief one-line description of the method or class.
#
# More detailed explanation of behavior, including any important
# details about usage, assumptions, or constraints.
#
# @param name [Type] description of parameter
# @param other [Type] description of another parameter
# @option options [Type] :key description of hash option
#
# @return [Type] description of return value
#
# @raise [ExceptionType] when this exception occurs
#
# @example Basic usage
# code_example
# # => expected_output
#
# @example Advanced usage
# complex_example
#
# @see RelatedClass
# @see #related_method
# @since 1.0.0
def method_name(name, other, options = {})
# Implementation
end
RuboCop Comment Cops
| Cop | Purpose |
|---|---|
| Style/Documentation | Requires documentation for classes/modules |
| Style/CommentedKeyword | Flags commented keywords |
| Layout/CommentIndentation | Enforces comment alignment |
| Layout/LeadingCommentSpace | Requires space after # |
| Style/CommentAnnotation | Validates annotation format |
| Layout/EmptyLineAfterMagicComment | Spacing after magic comments |
When to Comment Checklist
| Scenario | Comment Required | Why |
|---|---|---|
| Public API method | Yes | External interface documentation |
| Private implementation | Rarely | Should be self-documenting |
| Complex algorithm | Yes | Clarifies non-obvious logic |
| Workaround or hack | Yes | Explains why unusual approach needed |
| Magic number | Yes | Documents why this specific value |
| Security-sensitive code | Yes | Highlights security implications |
| Performance optimization | Yes | Justifies readability trade-off |
| External API integration | Yes | Documents integration details |
| Business logic decision | Yes | Preserves reasoning |
| Simple getter/setter | No | Code is self-explanatory |
| Obvious conditional | No | Code expresses intent clearly |
| Standard pattern | No | Well-known patterns need no explanation |