Best Practices

Code Style

Ruby Style Guide

Follow the community Ruby style guide:

Naming Conventions

# Good
class UserAccount
  MAX_LOGIN_ATTEMPTS = 3

  attr_accessor :first_name, :last_name

  def full_name
    "#{first_name} #{last_name}"
  end

  def self.create_user(attributes)
    # ...
  end
end

# Bad
class useraccount
  maxloginattempts = 3

  def FullName
    # ...
  end
end

Method Design

Single Responsibility Principle

# Good
class User
  def save
    validate
    persist_to_database
    send_welcome_email
  end

  private

  def validate
    # validation logic
  end

  def persist_to_database
    # database logic
  end

  def send_welcome_email
    # email logic
  end
end

# Bad - method does too many things
def save_user(user_data)
  # validate
  # save to database
  # send email
  # log activity
end

Method Length

Keep methods short and focused:

# Good
def calculate_total(items)
  subtotal = items.sum(&:price)
  tax = subtotal * TAX_RATE
  shipping = calculate_shipping(items)
  subtotal + tax + shipping
end

# Bad
def calculate_total(items)
  # 50+ lines of complex logic
end

Parameter Handling

# Use keyword arguments for clarity
def create_user(name:, email:, age: nil)
  # ...
end

# Use splat for variable arguments
def log(*messages)
  messages.each { |msg| puts "[LOG] #{msg}" }
end

Class Design

SOLID Principles

Composition over Inheritance

# Prefer composition
class Car
  def initialize(engine, wheels)
    @engine = engine
    @wheels = wheels
  end

  def start
    @engine.start
  end
end

# Avoid deep inheritance hierarchies

Error Handling

Use Appropriate Exception Types

class ValidationError < StandardError; end
class PaymentError < StandardError; end

def process_payment(amount)
  raise PaymentError, "Invalid amount" if amount <= 0
  # process payment
end

Handle Exceptions Properly

begin
  risky_operation
rescue SpecificError => e
  handle_specific_error(e)
rescue StandardError => e
  log_error(e)
  raise  # Re-raise if needed
ensure
  cleanup_resources
end

Testing

Test-Driven Development (TDD)

# Write test first
def test_addition
  calculator = Calculator.new
  assert_equal 5, calculator.add(2, 3)
end

# Then implement
class Calculator
  def add(a, b)
    a + b
  end
end

Test Coverage

Aim for high test coverage, especially for business logic.

Test Types

Performance

Avoid Premature Optimization

# Don't do this unless profiling shows it's a bottleneck
optimized_code = complex_optimization(data)

Use Appropriate Data Structures

# Use Set for membership testing
require 'set'
valid_items = Set.new(['item1', 'item2', 'item3'])
valid_items.include?('item1')  # O(1) lookup

Memory Management

# Process large files in chunks
File.open('large_file.txt') do |file|
  file.each_line do |line|
    process_line(line)
  end
end

Security

Input Validation

def create_user(params)
  name = params[:name].to_s.strip
  email = params[:email].to_s.downcase.strip

  # Validate input
  raise ArgumentError, "Invalid name" unless name.match?(/\A\w+\z/)
  raise ArgumentError, "Invalid email" unless email.match?(URI::MailTo::EMAIL_REGEXP)

  User.new(name: name, email: email)
end

SQL Injection Prevention

# Use parameterized queries
User.where("name = ? AND age > ?", name, min_age)

# Avoid string interpolation in queries
# Bad: User.where("name = '#{name}'")

Documentation

Use Comments Wisely

# Good - explains why, not what
def complex_calculation
  # Using Newton's method for better convergence
  # than simple iteration
  # ...
end

# Bad - just repeats the code
def add(a, b)
  # Add a and b
  a + b
end

RDoc Documentation

# Calculates the total price including tax
#
# @param items [Array<Item>] List of items to calculate
# @param tax_rate [Float] Tax rate as decimal (e.g., 0.08 for 8%)
# @return [Float] Total price including tax
def calculate_total(items, tax_rate = 0.08)
  subtotal = items.sum(&:price)
  subtotal * (1 + tax_rate)
end

Code Organization

File Structure

lib/
├── my_app.rb
├── my_app/
│   ├── models/
│   │   ├── user.rb
│   │   └── product.rb
│   ├── services/
│   │   ├── payment_service.rb
│   │   └── email_service.rb
│   └── utils/
│       └── string_utils.rb
└── my_app.rb

Require Structure

# Use require_relative for local files
require_relative 'models/user'
require_relative 'services/payment_service'

# Use require for external libraries
require 'json'
require 'date'

Tools and Gems

Code Quality

Development Tools

Deployment and Production

Environment Configuration

# Use environment variables
database_url = ENV.fetch('DATABASE_URL')
api_key = ENV['API_KEY']

# Avoid hardcoding values
# Bad: DATABASE_URL = "postgresql://localhost/myapp"

Logging

require 'logger'

logger = Logger.new(STDOUT)
logger.level = ENV['RACK_ENV'] == 'production' ? Logger::INFO : Logger::DEBUG

# Use appropriate log levels
logger.debug "Processing user #{user.id}"
logger.info "User #{user.id} logged in"
logger.error "Failed to process payment: #{e.message}"

Continuous Learning

Following these best practices will help you write maintainable, efficient, and professional Ruby code. Remember that best practices evolve, so stay engaged with the Ruby community.

Loading