Backend conventions for Rails codebases — models, validations, controllers, forms, services, queries, jobs, policies, mailers, and migrations.
Automatically activates when working in a Rails project, especially on .rb files.
Also activates when the user asks to build, review, or refactor backend code in a Rails codebase.
This skill applies to the Rails monolith stack. For Rails-specific conventions used by $dev-plan, see dev-plan.md and skills/dev-plan/references/stack-rails-monolith.md.
- Models — validations, associations, named scopes, callbacks (when appropriate)
- Controllers — thin controllers: parse params, authorize, call service, render response
- Services — single-responsibility service objects with one public method (
.call) - Policies — Pundit authorization: who can perform which actions on which resources
- Jobs — Sidekiq background jobs: idempotent, always retryable, always logged
- Mailers — ActionMailer conventions and preview setup
- Queries — query objects for complex database queries outside of scopes
- Migrations — naming, index conventions, zero-downtime rules
- RSpec — request specs, factory conventions, shared examples
# app/controllers/api/v1/orders_controller.rb
def create
authorize Order
result = Orders::CreateService.call(permitted_params, current_user)
render json: OrderSerializer.new(result), status: :created
end# app/services/orders/create_service.rb
class Orders::CreateService
def self.call(params, user)
new(params, user).call
end
def call
# business logic here
end
end# app/models/order.rb
class Order < ApplicationRecord
belongs_to :user
has_many :line_items
validates :status, presence: true, inclusion: { in: %w[pending confirmed shipped] }
scope :pending, -> { where(status: "pending") }
end# app/policies/order_policy.rb
class OrderPolicy < ApplicationPolicy
def show?
record.user == user || user.admin?
end
def create?
user.present?
end
end# db/migrate/20240101120000_add_status_to_orders.rb
def change
add_column :orders, :status, :string, null: false, default: "pending"
add_index :orders, :status
endSafe (no table lock):
- Add a nullable column
add_index ... algorithm: :concurrently- Add a new table
- Drop an index
Requires a maintenance window or special handling:
- Add a
NOT NULLcolumn without a default value - Rename a column
- Change a column type
- Add a constraint that validates existing rows
- Large data backfill — run as a background job instead
- Use request specs for API endpoints (
spec/requests/) — not controller specs - Use FactoryBot for test data — not fixtures
- Use shared examples for repeated auth and permission patterns
- Coverage minimum: 80%; target 100% for service objects