CrackedRuby CrackedRuby

Code Comments Best Practices

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