.claude/skills/rails-architecture/SKILL.md
Guides modern Rails 8 code architecture decisions and patterns. Use when deciding where to put code, choosing between patterns (service objects vs concerns vs query objects), designing feature architecture, refactoring for better organization, or when user mentions architecture, code organization, design patterns, or layered design. WHEN NOT: Implementing specific patterns (use specialist agents like service-agent or query-agent), writing tests, or debugging runtime errors.
npx skillsauth add ThibautBaissac/rails_ai_agents rails-architectureInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Where should this code go?
|
+- View/display formatting? -> Presenter (@presenter-agent)
+- Complex business logic? -> Service Object (@service-agent)
+- Complex database query? -> Query Object (@query-agent)
+- Shared behavior across models? -> Concern (/rails-concern skill)
+- Authorization logic? -> Policy (@policy-agent)
+- Reusable UI with logic? -> ViewComponent (@viewcomponent-agent)
+- Async/background work? -> Job (@job-agent, /solid-queue-setup skill)
+- Complex form (multi-model)? -> Form Object (@form-agent)
+- Transactional email? -> Mailer (@mailer-agent)
+- Real-time/WebSocket? -> Channel (/action-cable-patterns skill)
+- Data validation only? -> Model (@model-agent)
+- HTTP request/response only? -> Controller (@controller-agent)
| Layer | Responsibility | Should NOT contain | |-------|---------------|-------------------| | Controller | HTTP, params, response | Business logic, queries | | Model | Data, validations, relations | Display logic, HTTP | | Service | Business logic, orchestration | HTTP, display logic | | Query | Complex database queries | Business logic | | Presenter | View formatting, badges | Business logic, queries | | Policy | Authorization rules | Business logic | | Component | Reusable UI encapsulation | Business logic | | Job | Async processing | HTTP, display logic | | Form | Complex form handling | Persistence logic | | Mailer | Email composition | Business logic | | Channel | WebSocket communication | Business logic |
| Situation | Keep It Simple | Don't Create |
|-----------|----------------|--------------|
| Simple CRUD (< 10 lines) | Keep in controller | Service object |
| Used only once | Inline the code | Abstraction |
| Simple query with 1-2 conditions | Model scope | Query object |
| Basic text formatting | Helper method | Presenter |
| Single model form | form_with model: | Form object |
| Simple partial without logic | Partial | ViewComponent |
| Signal | Action | |--------|--------| | Same code in 3+ places | Extract to concern/service | | Controller action > 15 lines | Extract to service | | Model > 300 lines | Extract concerns | | Complex conditionals | Extract to policy/service | | Query joins 3+ tables | Extract to query object | | Form spans multiple models | Extract to form object |
See /extraction-timing skill for detailed extraction guidance.
# GOOD: Thin controller delegates to service
class OrdersController < ApplicationController
def create
result = Orders::CreateService.call(user: current_user, params: order_params)
if result.success?
redirect_to result.data, notice: t(".success")
else
flash.now[:alert] = result.error
render :new, status: :unprocessable_entity
end
end
end
All services return a consistent Result object:
Result = Data.define(:success, :data, :error) do
def success? = success
def failure? = !success
end
# GOOD: Scoped through account
def index
@events = current_account.events.recent
end
| Feature | Purpose | Skill/Agent |
|---------|---------|-------------|
| Authentication | has_secure_password generator | /authentication-flow |
| Background Jobs | Solid Queue (database-backed) | /solid-queue-setup, @job-agent |
| Real-time | Action Cable + Solid Cable | /action-cable-patterns |
| Caching | Solid Cache (database-backed) | /caching-strategies |
| Assets | Propshaft + Import Maps | (built-in) |
| Deployment | Kamal 2 + Thruster | (built-in) |
| Layer | Test Type | Focus | |-------|-----------|-------| | Model | Unit | Validations, scopes, methods | | Service | Unit | Business logic, edge cases | | Query | Unit | Query results, tenant isolation | | Presenter | Unit | Formatting, HTML output | | Controller | Request | Integration, HTTP flow | | Component | Component | Rendering, variants | | Policy | Unit | Authorization rules | | System | E2E | Critical user paths |
development
Creates Turbo Streams, Turbo Frames, and morphing patterns for real-time UI updates. Use when adding real-time updates, partial page rendering, form submissions, or broadcasting. WHEN NOT: For Stimulus JavaScript controllers (see stimulus-patterns skill). For general view conventions (see rules/views.md).
testing
Writes Minitest tests with fixtures following 37signals conventions. Uses Minitest (not RSpec) and fixtures (not factories). Use when writing tests, adding test coverage, or creating fixtures. WHEN NOT: For RSpec or FactoryBot patterns (this project uses Minitest + fixtures exclusively). For test configuration/CI setup (see project docs).
tools
Builds focused, single-purpose Stimulus controllers for progressive enhancement. Use when adding JavaScript behavior, UI interactions, form enhancements, or building reusable client-side components. WHEN NOT: For Turbo Stream/Frame patterns (see turbo-patterns skill). For server-side view logic (see rules/views.md).
testing
Implements the state-as-records-not-booleans pattern for rich state tracking. Use when modeling state changes, replacing boolean flags with record-based state, or when user mentions state records, closures, publications, or toggling state. WHEN NOT: Technical flags like cached/processed (use booleans), concern extraction (use concern-patterns), general model work (use model-patterns).