job-patterns
Skill211 starsChanged 5 months agoMITView on GitHub
Implements shallow background jobs with _later/_now conventions using Solid Queue. Use when adding background processing, async operations, scheduled tasks, or when user mentions jobs, queues, workers, or background processing. WHEN NOT: Business logic implementation (use model-patterns), controller work (use crud-patterns), or mailer delivery (use mailer-patterns).
1 other project uses a version of this.
Adopted by
Most recently taken up by
- AkbarDevop/ai-job-agenttheir own version, first seen Apr 26, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- AkbarDevop/ai-job-agent★ 61same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository17 other files it gives its agents.
- AGENTS.mdAGENTS.md
- .claude/CLAUDE.mdCLAUDE.md
- .opencode/CLAUDE.mdCLAUDE.md
- api-patternsSkill
- auth-setupSkill
- caching-patternsSkill
- concern-patternsSkill
- crud-patternsSkill
- event-trackingSkill
- mailer-patternsSkill
- migration-patternsSkill
- model-patternsSkill
- multi-tenant-setupSkill
- state-recordsSkill
- stimulus-patternsSkill
- testing-patternsSkill
- turbo-patternsSkill
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 2 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 41
Default method can be sync (explicit async)
- Very specific: line 180
Always set and reset Current context:
- Very specific: line 276
Always: Keep jobs thin (call model methods), follow the _later/_now naming convention (though the _now suffix is not strictly enforced), …
- Distinctive: line 277
Ask first: Before putting business logic in jobs, before using Redis/Sidekiq, before running jobs synchronously in production
- Very specific: line 278
Never: Put business logic in jobs, use Sidekiq/Resque (use Solid Queue), enqueue jobs in transactions (may not commit), forget …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Job Patterns (37signals)
Jobs orchestrate. Models do the work. Background jobs are thin wrappers around model methods.
Project knowledge
Tech Stack: Rails 8.2 (edge), Solid Queue, ActiveJob
Pattern: Thin jobs call model methods; models have _later/_now pairs
Commands:
bin/rails generate job NotifyRecipients # Generate job
bundle exec rake solid_queue:start # Run worker
bin/rails runner "puts SolidQueue::Job.count" # Check queue
bin/rails runner "SolidQueue::Job.destroy_all" # Clear jobsWhy shallow jobs
- Business logic stays in models (testable, reusable)
- Jobs are simple orchestrators
- Easy to run sync or async
- Can call methods directly in tests
- Clearer separation of concerns
Why _later/_now convention
- Clear which version is async
- Default method can be sync (explicit async)
- Easy to switch between sync/async
- Testable (call
_nowin tests)
Core pattern: shallow job
The job receives a model and calls its _now method. The _now suffix is conventional but not required -- some jobs call the plain method name (e.g., notifiable.notify_recipients).
# app/jobs/notify_recipients_job.rb
class NotifyRecipientsJob < ApplicationJob
queue_as :default
def perform(notifiable)
notifiable.notify_recipients_now
end
endThe model defines both _later and _now methods:
# In model or concern
def notify_recipients_later
NotifyRecipientsJob.perform_later(self)
end
def notify_recipients_now
recipients.each do |recipient|
next if recipient == creator
Notification.create!(recipient: recipient, notifiable: self, action: notification_action)
end
end
# Default to sync
def notify_recipients
notify_recipients_now
end
# Trigger async from callbacks
after_create_commit :notify_recipients_laterJob patterns
Notification job
class NotifyRecipientsJob < ApplicationJob
queue_as :default
def perform(notifiable)
notifiable.notify_recipients_now
end
endBatch processing job
class DeliverBundledNotificationsJob < ApplicationJob
queue_as :default
def perform
Notification::Bundle.deliver_all_now
end
endCleanup job
class SessionCleanupJob < ApplicationJob
queue_as :low_priority
def perform
Session.cleanup_old_sessions_now
end
endEvent tracking job
class TrackEventJob < ApplicationJob
queue_as :default
def perform(eventable, action, options = {})
eventable.track_event_now(action, options)
end
endBroadcasting job
class BroadcastUpdateJob < ApplicationJob
queue_as :default
def perform(broadcastable)
broadcastable.broadcast_update_now
end
endExternal API job
class DispatchWebhookJob < ApplicationJob
queue_as :webhooks
retry_on StandardError, wait: :exponentially_longer, attempts: 5
def perform(webhook, event)
webhook.dispatch_now(event)
end
endRetry and error handling
class DispatchWebhookJob < ApplicationJob
discard_on Webhook::InvalidUrl # Don't retry
retry_on StandardError, wait: :exponentially_longer, attempts: 5 # Backoff
retry_on CustomError, wait: 5.minutes, attempts: 3 # Fixed interval
rescue_from Webhook::Timeout do |exception|
webhook.mark_as_slow!
raise exception # Re-raise to trigger retry
end
def perform(webhook, event)
webhook.dispatch_now(event)
end
endCurrent attributes in jobs
Always set and reset Current context:
class NotifyRecipientsJob < ApplicationJob
before_perform do |job|
notifiable = job.arguments.first
Current.account = notifiable.account
Current.user = notifiable.creator if notifiable.respond_to?(:creator)
end
after_perform { Current.reset }
def perform(notifiable)
notifiable.notify_recipients_now
end
endPerformance patterns
Batch processing
# Enqueue in batches
Card.active.pluck(:id).each_slice(100) do |batch|
ProcessCardsJob.perform_later(batch)
end
class ProcessCardsJob < ApplicationJob
def perform(card_ids)
Card.where(id: card_ids).find_each(&:process_now)
end
endDebouncing (avoid duplicate jobs)
def reindex_later
return if reindex_job_queued?
ReindexBoardJob.perform_later(id)
end
def reindex_job_queued?
SolidQueue::Job.exists?(
job_class: "ReindexBoardJob",
arguments: [id].to_json,
finished_at: nil
)
endTesting jobs
Test model methods directly (preferred)
class CommentTest < ActiveSupport::TestCase
test "notify_recipients_now creates notifications" do
comment = comments(:logo_comment)
assert_difference -> { Notification.count }, 2 do
comment.notify_recipients_now
end
end
endVerify job is enqueued
class NotifyRecipientsJobTest < ActiveJob::TestCase
test "enqueues job" do
comment = comments(:logo_comment)
assert_enqueued_with job: NotifyRecipientsJob, args: [comment] do
NotifyRecipientsJob.perform_later(comment)
end
end
endVerify callbacks enqueue jobs
test "creating comment enqueues notification job" do
card = cards(:logo)
assert_enqueued_with job: NotifyRecipientsJob do
card.comments.create!(body: "Great work!", creator: users(:david))
end
endSee references/solid-queue.md for Solid Queue configuration and
references/recurring-jobs.md for recurring job setup.
Boundaries
- Always: Keep jobs thin (call model methods), follow the
_later/_nownaming convention (though the_nowsuffix is not strictly enforced), put business logic in models, set queue priorities, implement retry strategies, test model methods directly - Ask first: Before putting business logic in jobs, before using Redis/Sidekiq, before running jobs synchronously in production
- Never: Put business logic in jobs, use Sidekiq/Resque (use Solid Queue), enqueue jobs in transactions (may not commit), forget
Current.resetin jobs, skip retry strategies for unreliable operations
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

