Curated library of AI agent skills for Ruby on Rails development. Covers code review, architecture, security, testing (RSpec), engines, service objects, DDD patterns, and workflow automation.
98
99%
Does it follow best practices?
Impact
98%
1.38xAverage score across 26 eval scenarios
Passed
No known issues
Use this skill when the task is to add, configure, or review background jobs in a Rails application.
Core principle: Design jobs for idempotency and safe retries. Prefer Active Job's unified API; choose backend based on Rails version and scale.
EVERY job MUST have its test written and validated BEFORE implementation.
1. Write the job spec (idempotency, retry, error handling)
2. Run the spec — verify it fails because the job does not exist yet
3. ONLY THEN write the job class
EVERY job that performs a side effect (charge, email, API call) MUST have
an idempotency check BEFORE the side effect.
EVERY perform method should do only three things:
1. Load the record from the passed ID
2. Guard for idempotency / permanent no-op conditions
3. Delegate the side effect or orchestration to a service object
If perform needs more than that, extract a service.
After implementation: run full suite, confirm job appears in queue dashboard,
verify idempotency by enqueueing twice and checking the second run is a no-op.| Aspect | Rule |
|---|---|
| Arguments | Pass IDs, not objects. Load in perform. |
| Idempotency | Check "already done?" before doing work |
| Retries | retry_on for transient, discard_on for permanent errors |
| Job size | Load, guard, delegate. No multi-step orchestration in perform. |
| Backend (Rails 8) | Solid Queue (database-backed, no Redis) |
| Backend (Rails 7) | Sidekiq + Redis for high throughput |
| Recurring | config/recurring.yml (Solid Queue) or cron/sidekiq-cron |
| Aspect | Rails 7 and earlier | Rails 8 |
|---|---|---|
| Default | No default; set queue_adapter (often Sidekiq) | Solid Queue (database-backed) |
| Dev/test | :async or :inline | Same |
| Recurring | External (cron, sidekiq-cron) | config/recurring.yml |
| Dashboard | Third-party (Sidekiq Web) | Mission Control Jobs |
See BACKENDS.md for install steps, configuration, and dashboard setup for both Solid Queue and Sidekiq.
Pass IDs, not objects:
# Bad — object may be stale or deleted by perform time
SomeJob.perform_later(@order)
# Good — reload fresh inside perform
SomeJob.perform_later(@order.id)Thin job with idempotency and retry:
class SendInvoiceReminderJob < ApplicationJob
queue_as :default
retry_on Net::OpenTimeout, wait: :polynomially_longer, attempts: 5
discard_on ActiveRecord::RecordNotFound
def perform(invoice_id)
invoice = Invoice.find(invoice_id)
return if invoice.reminder_sent_at?
InvoiceReminders::Send.call(invoice:)
end
endService owns the side effect and state update:
module InvoiceReminders
class Send
def self.call(invoice:)
InvoiceMailer.overdue(invoice).deliver_now
invoice.update!(reminder_sent_at: Time.current)
end
end
endRecurring job (Solid Queue):
# config/recurring.yml
production:
nightly_cleanup:
class: "NightlyCleanupJob"
schedule: "0 2 * * *"
hourly_sync:
class: "HourlySyncJob"
schedule: "every 1 hour"
queue: low| Problem | Correct approach |
|---|---|
| Passing ActiveRecord objects as arguments | Pass IDs — objects may be deleted or stale by perform time |
| No idempotency check before side effects | Jobs run at-least-once; double-charging and double-emailing result |
retry_on without attempts limit | Infinite retries on persistent errors |
Missing discard_on for permanent errors | Job retries forever on RecordNotFound |
Complex business logic in perform | Keep perform thin — delegate to service objects |
Using :inline or :async in production | No persistence, no retry, no monitoring |
| Recurring job defined only in code | Use recurring.yml or equivalent for visibility and recoverability |
Before calling the job done:
retry_on has an explicit attempts: limit and discard_on covers at least one permanent error.config/recurring.yml (Rails 8) or the chosen scheduler config.perform only loads, guards, and delegates.process_log.md.| Skill | When to chain |
|---|---|
| rails-migration-safety | Solid Queue uses DB tables; add migrations safely |
| rails-security-review | Jobs receive serialized input; validate like any entry point |
| rspec-best-practices | TDD gate: write job spec before implementation; use perform_enqueued_jobs |
| ruby-service-objects | Keep perform thin; call service objects for business logic |
api-rest-collection
create-prd
ddd-boundaries-review
ddd-rails-modeling
ddd-ubiquitous-language
docs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10
scenario-11
scenario-12
scenario-13
scenario-14
scenario-15
scenario-16
scenario-17
scenario-18
scenario-19
scenario-20
scenario-21
scenario-22
scenario-23
scenario-24
scenario-25
scenario-26
generate-tasks
mcp_server
rails-architecture-review
rails-background-jobs
rails-bug-triage
rails-code-conventions
rails-code-review
rails-engine-compatibility
rails-engine-docs
rails-engine-extraction
rails-engine-installers
rails-engine-release
rails-engine-reviewer
rails-engine-testing
rails-graphql-best-practices
rails-migration-safety
rails-review-response
rails-security-review
rails-skills-orchestrator
rails-stack-conventions
rails-tdd-slices
refactor-safely
rspec-best-practices
rspec-service-testing
ruby-service-objects
strategy-factory-null-calculator
ticket-planning
yard-documentation