From rails-agent-skills
Applies checklist for clean Rails code: design principles (DRY, YAGNI, PORO, CoC, KISS), per-path rules (models, services, workers, controllers), structured logging, comment discipline. Defers style to project linters. Use for writing, reviewing, refactoring.
npx claudepluginhub igmarin/rails-agent-skills --plugin rails-agent-skillsThis skill uses the workspace's default tool permissions.
**Style source of truth:** Style and formatting defer to the project's configured linter(s). This skill adds **non-style behavior** and **architecture guidance** only. For Hotwire + Tailwind specifics, see **rails-stack-conventions**.
Provides Ruby on Rails conventions, patterns, and best practices for ActiveRecord models, controllers, migrations, configuration, background jobs, logging, and request specs.
Writes and reviews Rails code following DHH's 37signals style guide; advises on architecture while respecting existing conventions like Tailwind or RSpec.
Orchestrates three-phase Rails code quality workflow: conventions review, safe refactoring with tests, and YARD documentation generation. Use for pre-PR sweeps, quality audits, or production-readiness reviews.
Share bugs, ideas, or general feedback.
Style source of truth: Style and formatting defer to the project's configured linter(s). This skill adds non-style behavior and architecture guidance only. For Hotwire + Tailwind specifics, see rails-stack-conventions.
Detect → run → defer. Do not invent style rules.
.rubocop.yml / standard gem → bundle exec rubocop or bundle exec standardrbeslint.config.*, .eslintrc*, biome.json, or package.json lint script → run accordingly| Topic | Rule |
|---|---|
| Style/format | Project linter(s) — detect and run as above; do not invent style rules here |
| Principles | DRY, YAGNI, PORO where it helps, CoC, KISS |
| Comments / tags | Explain why; tagged notes need actionable context |
| Logging | First arg: static string; second arg: hash with event: key; no interpolation; backtrace on errors |
| Deep stacks | Chain rails-stack-conventions → domain skills (services, jobs, RSpec) |
When reviewing or refactoring Rails code, follow this sequence:
Comment why, not what. Tagged notes — TODO: / FIXME: / HACK: / NOTE: / OPTIMIZE: — are MANDATORY in these triggers; every tag carries actionable context (owner, ticket id, deadline, or next step). Naked tags (# TODO: fix this) fail review.
| Trigger | Required tag |
|---|---|
| Business-rule constant (rates, caps, thresholds) | NOTE: with the rule's source/owner |
| Deferred work / known shortcut | TODO: with ticket or next step |
| Workaround for a bug or external limitation | HACK: or FIXME: with the upstream issue |
| Performance tradeoff or hot path | OPTIMIZE: with the measured concern |
# BAD — naked tag, no context
# TODO: fix this
rate = TIER_RATES.fetch(tier, 0.0)
# GOOD
# NOTE: 50% cap set by Pricing policy v3 (PRI-118, owner: pricing-team).
MAX_DISCOUNT = 0.50
# GOOD — TODO with next step + dependency
# TODO: replace TIER_RATES with DB-backed lookup (PRI-482; blocked on legal).
rate = TIER_RATES.fetch(tier, 0.0)
MANDATORY SHAPE — every Rails.logger.* call uses exactly two positional arguments.
Rails.logger.<level>(static_string_message, { event: "dot.namespaced", ...domain_fields })
# └── 1st arg: STRING ──┘ └─────────── 2nd arg: HASH ───────────┘
event: with a dot-namespaced value (do NOT use :type, :action, or :name). All dynamic data goes here.rescue logs both e.message and e.backtrace.first(5).join("\n") as hash fields — backtrace is non-optional.# BAD — interpolation destroys log aggregator grouping; single-arg call loses structured fields
Rails.logger.info("Processing order #{order.id}")
Rails.logger.info(event: "order.processing_started", order_id: order.id)
# GOOD
Rails.logger.info("order.processing_started", {
event: "order.processing_started",
order_id: order.id,
user_id: user.id
})
# GOOD — error path with backtrace
rescue StandardError => e
Rails.logger.error("order.processing_failed", {
event: "order.processing_failed",
order_id: order.id,
error: e.message,
backtrace: e.backtrace.first(5).join("\n")
})
raise
end
Rules below apply when those paths exist in the project. If a path is absent, skip that row.
| Area | Path pattern | Guidance |
|---|---|---|
| ActiveRecord performance | app/models/**/*.rb | Eager load in loops; prefer pluck / exists? / find_each. N+1: run bullet → fix eager loads → re-run clean |
| Background jobs | app/workers/**/*.rb, app/jobs/**/*.rb | Depth: rails-background-jobs |
| Error handling | app/services/**/*.rb, app/lib/**/*.rb, app/exceptions/**/*.rb | Domain exceptions + layer rescue_from; specs must cover rescue paths |
| Logging / tracing | app/services/**/*.rb, app/workers/**/*.rb, app/jobs/**/*.rb, app/controllers/**/*.rb, app/repositories/**/*.rb | Structured logs (see above); APM spans/tags on hot paths when stack has APM |
| Controllers | app/controllers/**/*_controller.rb | Strong params; thin actions → services; IDOR / PII → rails-security-review |
| Repositories | app/repositories/**/*.rb | New repos only for SQL, caching, clear boundary, or external I/O — document why |
| RSpec | spec/**/*_spec.rb | FactoryBot; request over controller specs; env: (or project pattern) for ENV; let > let! unless eager setup required; avoid heavy before when let is clearer |
| Serializers | app/serializers/**/*.rb | Explicit keys; no N+1; preload associations passed in |
| Service objects | app/services/**/*.rb | Single responsibility; .call / injected deps per ruby-service-objects; after extract, specs + caller still green |
| SQL security | Raw SQL anywhere | Bind params / sanitize_sql_array; whitelist dynamic ORDER BY; document why raw SQL |
let_it_be (test-prof)Only recommend let_it_be if test-prof is already in Gemfile.lock. Otherwise default to let; reach for let! only when lazy evaluation would break the example. Don't introduce test-prof unless asked.
When this skill guides new behavior, the tests gate still applies:
PRD → TASKS → TEST (write, run, fail) → IMPLEMENTATION → …
No implementation code before a failing test. See rspec-best-practices and rails-agent-skills.
Every Rails-code task lands these:
TODO: / FIXME: / HACK: / NOTE: / OPTIMIZE:) on every assumption, deferred work, or business-rule constant; every tag carries actionable context (owner, ticket id, deadline).event:, and a backtrace line on every error rescue.| Skill | When to chain |
|---|---|
| rails-stack-conventions | Stack-specific: PostgreSQL, Hotwire, Tailwind |
| ddd-rails-modeling | When domain concepts and invariants need clearer Rails-first modeling choices |
| ruby-service-objects | Implementing or refining service objects |
| rails-background-jobs | Workers, queues, retries, idempotency |
| rspec-best-practices | Spec style, tests gate (red/green/refactor), request vs controller specs |
| rails-security-review | Controllers, params, IDOR, PII |
| rails-code-review | Full PR pass before merge |