Use when adding or reviewing background jobs in Rails. Covers Active Job, Solid Queue (Rails 8+), Sidekiq, recurring jobs, idempotency, retry/discard strategies, and queue selection.
82
77%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Optimize this skill with Tessl
npx tessl skill review --optimize ./rails-background-jobs/SKILL.mdUse 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
See rspec-best-practices for the full gate cycle.| 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 | One responsibility. Call services for complex logic. |
| 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 |
EVERY job that performs side effects (charge, email, API call) MUST have
an idempotency check before the side effect.| 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 |
perform.retry_on for transient errors, discard_on for permanent failures.Bad: Passing ActiveRecord object (can be stale/missing):
# In controller:
SomeJob.perform_later(@order) # @order is an ActiveRecord object
# In job:
class SomeJob < ApplicationJob
def perform(order) # If @order is stale or deleted, this will fail
order.process!
end
endGood: Passing only the ID (always reliable):
# In controller:
SomeJob.perform_later(@order.id) # Only pass the ID
# In job:
class SomeJob < ApplicationJob
def perform(order_id)
order = Order.find(order_id) # Always re-load fresh data inside the job
order.process!
end
endJob with idempotency and retry:
class NotifyOrderShippedJob < ApplicationJob
queue_as :default
retry_on Net::OpenTimeout, wait: :polynomially_longer, attempts: 5
discard_on ActiveRecord::RecordNotFound
def perform(order_id)
order = Order.find(order_id)
return if order.shipped_notification_sent?
OrderMailer.shipped(order).deliver_now
order.update!(shipped_notification_sent_at: Time.current)
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| Mistake | Reality |
|---|---|
| Passing ActiveRecord objects as arguments | Object may be deleted or stale by perform time. Pass IDs. |
| No idempotency check before side effects | Jobs run at-least-once. Double-charging, double-emailing. |
retry_on without attempts limit | Infinite retries on persistent errors |
Using :inline or :async in production | No persistence, no retry, no monitoring |
Complex business logic in perform | Keep perform thin. Delegate to service objects. |
Missing discard_on for permanent errors | Job retries forever on RecordNotFound |
:inline or :async in productionrecurring.yml or equivalentperform — exceptions silently discarded or retried endlessly| 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 and validate job spec before implementation |
| ruby-service-objects | Keep perform thin; call service objects for business logic |
| rspec-best-practices | Use perform_enqueued_jobs to test; test idempotency |
ae8ea63
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.