CtrlK
BlogDocsLog inGet started
Tessl Logo

rails-background-jobs

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

Quality

77%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Passed

No known issues

Optimize this skill with Tessl

npx tessl skill review --optimize ./rails-background-jobs/SKILL.md
SKILL.md
Quality
Evals
Security

Rails Background Jobs

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.

HARD-GATE: Tests Gate Implementation

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.

Quick Reference

AspectRule
ArgumentsPass IDs, not objects. Load in perform.
IdempotencyCheck "already done?" before doing work
Retriesretry_on for transient, discard_on for permanent errors
Job sizeOne responsibility. Call services for complex logic.
Backend (Rails 8)Solid Queue (database-backed, no Redis)
Backend (Rails 7)Sidekiq + Redis for high throughput
Recurringconfig/recurring.yml (Solid Queue) or cron/sidekiq-cron

HARD-GATE

EVERY job that performs side effects (charge, email, API call) MUST have
an idempotency check before the side effect.

Rails 8 vs Rails 7

AspectRails 7 and earlierRails 8
DefaultNo default; set queue_adapter (often Sidekiq)Solid Queue (database-backed)
Dev/test:async or :inlineSame
RecurringExternal (cron, sidekiq-cron)config/recurring.yml
DashboardThird-party (Sidekiq Web)Mission Control Jobs

Core Rules

  1. Pass serializable arguments: Pass IDs, strings, numbers. Load records in perform.
  2. Design for idempotency: Check state before doing work; use unique constraints or "already processed" checks.
  3. Use retries wisely: retry_on for transient errors, discard_on for permanent failures.
  4. Keep jobs small: One responsibility. Call services or POROs for complex logic.

Examples

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
end

Good: 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
end

Job 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
end

Recurring job (Solid Queue):

# config/recurring.yml
production:
  nightly_cleanup:
    class: "NightlyCleanupJob"
    schedule: "0 2 * * *"
  hourly_sync:
    class: "HourlySyncJob"
    schedule: "every 1 hour"
    queue: low

Common Mistakes

MistakeReality
Passing ActiveRecord objects as argumentsObject may be deleted or stale by perform time. Pass IDs.
No idempotency check before side effectsJobs run at-least-once. Double-charging, double-emailing.
retry_on without attempts limitInfinite retries on persistent errors
Using :inline or :async in productionNo persistence, no retry, no monitoring
Complex business logic in performKeep perform thin. Delegate to service objects.
Missing discard_on for permanent errorsJob retries forever on RecordNotFound

Red Flags

  • Job performs non-idempotent side effects without "already done?" check
  • Passing large or non-serializable objects as arguments
  • Relying on "run once" semantics (at-least-once delivery can run a job twice)
  • Using :inline or :async in production
  • Recurring job defined only in code without recurring.yml or equivalent
  • No error handling in perform — exceptions silently discarded or retried endlessly

Integration

SkillWhen to chain
rails-migration-safetySolid Queue uses DB tables; add migrations safely
rails-security-reviewJobs receive serialized input; validate like any entry point
rspec-best-practicesTDD gate: write and validate job spec before implementation
ruby-service-objectsKeep perform thin; call service objects for business logic
rspec-best-practicesUse perform_enqueued_jobs to test; test idempotency
Repository
igmarin/rails-agent-skills
Last updated
Created

Is this your skill?

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.