Rails applications can handle massive scale with proper optimization. Performance tuning involves multiple layers:
/plugin marketplace add sjnims/rails-expert/plugin install rails-expert@rails-expert-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
examples/optimization-patterns.rbreferences/caching-guide.mdreferences/profiling-tools.mdRails applications can handle massive scale with proper optimization. Performance tuning involves multiple layers:
Rails 8 provides excellent performance out of the box with Solid Cache, Thruster, and modern defaults.
The #1 performance problem in Rails apps:
Problem:
# 1 query for products + N queries for categories
products = Product.limit(10)
products.each { |p| puts p.category.name } # N additional queries!
Solution:
# 2 queries total
products = Product.includes(:category).limit(10)
products.each { |p| puts p.category.name } # No additional queries
Use the Bullet gem in development to detect N+1:
# Gemfile
gem 'bullet', group: :development
# config/environments/development.rb
config.after_initialize do
Bullet.enable = true
Bullet.alert = true
Bullet.console = true
end
Enforce eager loading by raising errors when associations are lazily loaded:
# On a relation - raises ActiveRecord::StrictLoadingViolationError
user = User.strict_loading.first
user.address.city # raises error - not eager loaded
# On a record
user = User.first
user.strict_loading!
user.comments.to_a # raises error
# N+1 only mode - allows singular associations, catches collection lazy loads
user.strict_loading!(mode: :n_plus_one_only)
user.address.city # allowed (singular)
user.comments.first.likes.to_a # raises error (N+1 risk)
# On an association
class Author < ApplicationRecord
has_many :books, strict_loading: true
end
App-wide configuration:
# config/application.rb
config.active_record.strict_loading_by_default = true
# Log instead of raise
config.active_record.action_on_strict_loading_violation = :log
Use strict_loading in development/staging to catch N+1 queries before production.
Add indexes for frequently queried columns:
# Migration
add_index :products, :sku
add_index :products, [:category_id, :available]
add_index :products, :name, unique: true
When to index:
Check with EXPLAIN:
Product.where(category_id: 5).explain
# Look for "Index Scan" (good) vs "Seq Scan" (bad)
# Select only needed columns
Product.select(:id, :name, :price)
# Use pluck for single values
Product.pluck(:name) # Returns array of names
# Count efficiently
Product.count # COUNT(*) query
Product.size # Smart: uses count or length based on context
# Check existence
Product.exists?(name: "Widget") # Fast
# Batch processing
Product.find_each { |p| process(p) } # Loads in batches
Rails 8 uses Solid Cache by default (database-backed).
Cache rendered view fragments:
<% cache @product do %>
<%= render @product %>
<% end %>
Cache key includes:
updated_at timestampCache multiple items efficiently:
<%= render partial: 'products/product', collection: @products, cached: true %>
Reads all caches in one query, much faster than individual caching.
Nest caches that invalidate properly:
class Product < ApplicationRecord
belongs_to :category, touch: true # Update category when product changes
end
<% cache @category do %>
<h2><%= @category.name %></h2>
<% @category.products.each do |product| %>
<% cache product do %>
<%= render product %>
<% end %>
<% end %>
<% end %>
When product updates:
# Cache expensive calculations
def complex_stats
Rails.cache.fetch("product_#{id}/stats", expires_in: 1.hour) do
calculate_expensive_statistics
end
end
# Cache with dependencies
Rails.cache.fetch(["product", id, :reviews, last_review_at]) do
reviews.includes(:user).order(created_at: :desc).limit(10)
end
Rails automatically caches identical queries within a request:
Product.find(1) # Fires query
Product.find(1) # Uses cache (within same request)
Rails 8's asset pipeline:
Propshaft handles:
Thruster handles:
# config/environments/production.rb
config.asset_host = 'https://cdn.example.com'
Serves assets from CDN for faster global delivery.
# Use Active Storage variants
<%= image_tag @product.image.variant(resize_to_limit: [800, 600]) %>
# Or ImageProcessing gem
<%= image_tag @product.image.variant(resize_and_pad: [800, 600, background: "white"]) %>
# config/puma.rb
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count
workers ENV.fetch("WEB_CONCURRENCY") { 2 }
preload_app!
Threads: Handle concurrent requests (5 is good default) Workers: Separate processes for parallelism (1 per CPU core)
Rules of thumb:
Rails 8 enables YJIT by default (Ruby 3.3+):
# config/application.rb
config.yjit = true # Enabled by default in Rails 8
YJIT benefits:
Use jemalloc for better memory management:
# Dockerfile
RUN apt-get install -y libjemalloc2
ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2
Reduces memory fragmentation with threaded servers.
# Gemfile
gem 'rack-mini-profiler'
Shows in-page:
Appears in top-left corner of every page in development.
# Gemfile
gem 'bullet', group: :development
# Detects:
# - N+1 queries (missing includes)
# - Unused eager loading
# - Unnecessary counter cache
Production performance monitoring:
includesfind_eachRails.cache.fetchrequire 'benchmark'
# Compare implementations
Benchmark.bm do |x|
x.report("approach 1:") { 1000.times { slow_method } }
x.report("approach 2:") { 1000.times { fast_method } }
end
# Apache Bench
ab -n 1000 -c 10 https://myapp.com/products
# wrk
wrk -t12 -c400 -d30s https://myapp.com/products
Production APM provides:
Causes:
Solutions:
includes)Causes:
Solutions:
memory_profiler gemderailed_benchmarksCauses:
Solutions:
includes for associationsFor deeper exploration:
references/caching-guide.md: Complete caching strategies guidereferences/profiling-tools.md: How to profile and debug performanceFor code examples:
examples/optimization-patterns.rb: Common optimization patternsRails performance involves:
Master these techniques and your Rails app will scale to millions of users.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.