Rails Developer Skill
A SKILL.md file for Claude Code with full-stack Rails 8 expertise including Hotwire, ActiveRecord patterns, and Rails conventions
curl -fsSL https://solohub.uklad.vc/i/rails-developer | bash
Auto-detects Claude Code or Cursor. Add ?t=cursor to force Cursor.
Rails Developer Skill
This skill file configures Claude Code with full-stack Rails 8 expertise, including the modern Hotwire stack, ActiveRecord patterns, and Rails conventions.
SKILL.md Content
Place this in .claude/skills/rails-developer/SKILL.md:
# Rails Developer Skill
| name | description | license |
|------|-------------|---------|
| rails-developer | Full-stack Rails 8 expertise with Hotwire, ActiveRecord patterns, and Rails conventions | MIT |
## Capabilities
You are an expert Rails developer with deep knowledge of:
- Rails 8 and the Solid Trifecta (SolidQueue, SolidCache, SolidCable)
- Hotwire stack: Turbo Drive, Turbo Frames, Turbo Streams, Stimulus
- ActiveRecord patterns, query optimization, and N+1 prevention
- RESTful design, concerns, service objects, and Rails conventions
- Propshaft asset pipeline and Importmap (no Node.js bundler)
- Testing with Minitest and system tests
## Rails 8 Stack
### Solid Trifecta
- **SolidQueue**: Background job processing with SQLite/PostgreSQL
- **SolidCache**: Database-backed caching (no Redis required)
- **SolidCable**: Action Cable with database backend
### Asset Pipeline
- **Propshaft**: Modern asset pipeline (replaces Sprockets)
- **Importmap**: JavaScript modules without bundling
## Best Practices
### General Principles
1. **Convention over configuration**: Follow Rails defaults unless there's a compelling reason
2. **Fat models, skinny controllers**: Business logic in models or service objects
3. **Prefer Hotwire over custom JavaScript**: Use Turbo and Stimulus before reaching for React/Vue
4. **Extract complexity**: Use concerns for shared behavior, service objects for complex operations
### Code Organization
- `app/services/` for complex business logic
- `app/models/concerns/` for shared model behavior
- `app/controllers/concerns/` for shared controller behavior
- Keep controllers focused on HTTP concerns only
## Hotwire Patterns
### Turbo Frames
Use for partial page updates without full reloads:
```erb
<%= turbo_frame_tag "post_#{post.id}" do %>
<%= render post %>
<% end %>
Turbo Streams
Use for real-time updates and multi-target responses:
```ruby
Controller
respond_to do |format|
format.turbo_stream
format.html { redirect_to posts_path }
end
```
<%# posts/create.turbo_stream.erb %>
<%= turbo_stream.prepend "posts", @post %>
<%= turbo_stream.update "flash", partial: "shared/flash" %>
Stimulus Controllers
// app/javascript/controllers/toggle_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["content"]
toggle() {
this.contentTarget.classList.toggle("hidden")
}
}
ActiveRecord Patterns
Prevent N+1 Queries
# Bad
Post.all.each { |p| p.author.name }
# Good - eager load associations
Post.includes(:author).each { |p| p.author.name }
# Use strict_loading in development
class Post < ApplicationRecord
self.strict_loading_by_default = true
end
Scopes for Common Queries
class Post < ApplicationRecord
scope :published, -> { where(published: true) }
scope :recent, -> { order(created_at: :desc) }
scope :by_author, ->(author) { where(author: author) }
end
Query Objects for Complex Queries
class PostSearch
def initialize(params)
@params = params
end
def results
scope = Post.published.includes(:author, :tags)
scope = scope.where("title ILIKE ?", "%#{@params[:q]}%") if @params[:q].present?
scope = scope.by_author(@params[:author]) if @params[:author].present?
scope
end
end
Background Jobs
class ProcessUploadJob < ApplicationJob
queue_as :default
def perform(upload)
# Process the upload
upload.process!
end
end
# Enqueue
ProcessUploadJob.perform_later(upload)
Caching Strategies
Fragment Caching
<% cache post do %>
<%= render post %>
<% end %>
Russian Doll Caching
<% cache ["v1", @posts] do %>
<% @posts.each do |post| %>
<% cache post do %>
<%= render post %>
<% end %>
<% end %>
<% end %>
Low-Level Caching
Rails.cache.fetch("user_#{id}_stats", expires_in: 1.hour) do
calculate_expensive_stats
end
Code Style
- Use
&.safe navigation:user&.name - Prefer
%i[]for symbol arrays:%i[create update destroy] - Use keyword arguments for methods with 3+ parameters
- Single quotes unless interpolation needed
- snake_case for methods/variables, CamelCase for classes ```
How to Use
- Create the directory:
.claude/skills/rails-developer/ - Save the content above as
SKILL.md - Claude Code will automatically use this skill for Rails development tasks
Key Principles
The Rails Way
| Principle | Description |
|---|---|
| Convention over Configuration | Follow Rails defaults; deviate only with good reason |
| DRY (Don't Repeat Yourself) | Extract common code into concerns, helpers, partials |
| Fat Models, Skinny Controllers | Controllers handle HTTP; models handle business logic |
| RESTful Design | Model resources with standard CRUD actions |
Hotwire Decision Tree
| Scenario | Solution |
|---|---|
| Navigate without full reload | Turbo Drive (automatic) |
| Update part of a page | Turbo Frame |
| Update multiple parts at once | Turbo Streams |
| Add client-side behavior | Stimulus controller |
| Complex client-side state | Consider React/Vue (last resort) |
Common Patterns
# Service Object
class CreateOrder
def initialize(user:, cart:)
@user = user
@cart = cart
end
def call
ActiveRecord::Base.transaction do
order = @user.orders.create!(total: @cart.total)
@cart.items.each { |item| order.line_items.create!(item: item) }
@cart.clear!
order
end
end
end
# Usage
order = CreateOrder.new(user: current_user, cart: @cart).call
# Concern for shared behavior
module Archivable
extend ActiveSupport::Concern
included do
scope :archived, -> { where.not(archived_at: nil) }
scope :active, -> { where(archived_at: nil) }
end
def archive!
update!(archived_at: Time.current)
end
def archived?
archived_at.present?
end
end