mcpField
R
rails-background-jobs

rails-background-jobs

github.com/midudevDevelopment7.0k58 installsUpdated 1w ago
Repository

About rails-background-jobs

A guide for designing, configuring, and reviewing Rails background jobs, requiring tests first and idempotency checks before side effects.

Key features

  1. 01Test-first implementation gate
  2. 02Idempotency and safe retries
  3. 03Active Job argument and responsibility guidance
  4. 04Rails 7 and Rails 8 backend reference
  5. 05Recurring jobs and verification checklist

Use cases

  1. 01Design retries and error handling for jobs
  2. 02Implement jobs with side effects such as email
  3. 03Choose a queue backend for a Rails version

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/midudev/autoskills --skill rails-background-jobs

After installing, type/rails-background-jobsin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

BACKENDS.md
assets/
job_patterns.md
retry_examples.md

Details

From the project README

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

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.

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 sizeLoad, 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
Recurringconfig/recurring.yml (Solid Queue) or cron/sidekiq-cron

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

See BACKENDS.md for install steps, configuration, and dashboard setup for both Solid Queue and Sidekiq.

Examples

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
end

Service 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
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

Pitfalls

ProblemCorrect approach
Passing ActiveRecord objects as argumentsPass IDs — objects may be deleted or stale by perform time
No idempotency check before side effectsJobs run at-least-once; double-charging and double-emailing result
retry_on without attempts limitInfinite retries on persistent errors
Missing discard_on for permanent errorsJob retries forever on RecordNotFound
Complex business logic in performKeep perform thin — delegate to service objects
Using :inline or :async in productionNo persistence, no retry, no monitoring
Recurring job defined only in codeUse recurring.yml or equivalent for visibility and recoverability

Verification

Before calling the job done:

  1. Enqueue or perform the job twice and confirm the second run is a no-op.
  2. Confirm retry_on has an explicit attempts: limit and discard_on covers at least one permanent error.
  3. Confirm recurring jobs live in config/recurring.yml (Rails 8) or the chosen scheduler config.
  4. Confirm perform only loads, guards, and delegates.
  5. If the task asks for an ops artifact, record backend, retry, and idempotency decisions in process_log.md.

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 job spec before implementation; use perform_enqueued_jobs
ruby-service-objectsKeep perform thin; call service objects for business logic

Assets

Comments

Markdown supported