CtrlK
BlogDocsLog inGet started
Tessl Logo

igmarin/rails-agent-skills

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.

73

Quality

91%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

SKILL.mdrails-background-jobs/

name:
rails-background-jobs
description:
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.

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

rails-background-jobs

README.md

tile.json