Two small skills, `add-mailer` and `add-job`, cover the two most common kinds of work that happen
outside the request cycle: sending an email, and doing anything else in the background. Both build
on plumbing that's already fully configured, so each skill is short. The concept pages are
[Sending Email](/docs/building-features/sending-email) and
[Background Jobs](/docs/building-features/background-jobs); this page is the two skills'
step-by-step.

## add-mailer

Email is already wired end to end: Resend in production, `letter_opener` in development, `:test` in
specs. Copy `app/mailers/sign_in_code_mailer.rb` and its views as the template.

1. Add a method to an existing mailer, or create `app/mailers/<name>_mailer.rb` subclassing
   `ApplicationMailer`. Keep the default `from` address unless you have a specific reason to
   override it per message.
2. Create both `app/views/<name>_mailer/<method>.html.erb` and the matching `.text.erb`, for
   deliverability.
3. Send it with `<Name>Mailer.<method>(...).deliver_later`, which delivers asynchronously through
   Solid Queue. For a delayed send, `deliver_later(wait: 5.minutes)`. See
   `User#send_welcome_email_later` for the dev/test inline-fallback pattern this kit uses.
4. Write `spec/mailers/<name>_mailer_spec.rb`, asserting recipient, subject, and body, plus a
   `have_enqueued_mail` assertion at the trigger site.
5. Optionally add a `spec/mailers/previews/` preview for viewing the rendered email in
   `letter_opener`.
6. Run `bin/check`.

## add-job

Jobs run on Solid Queue, already configured, with no Redis: in-Puma in production, async or inline
in development and test.

1. `bin/rails g job <Name>` generates `app/jobs/<name>_job.rb`. Keep `perform` small and idempotent,
   since a job may be retried, and operate through account-scoped associations, never a global
   scope.
2. Enqueue with `<Name>Job.perform_later(record_id)`, passing ids rather than objects. For future
   work, `set(wait: ...).perform_later`.
3. For periodic work, add an entry to `config/recurring.yml`, cron-style.
4. Write `spec/jobs/<name>_job_spec.rb`, asserting behavior with the job performed inline, and use
   `have_enqueued_job` wherever it's triggered.
5. Run `bin/check`.

## Next

See how to publish content, using the same file-based, no-database pattern the docs site itself is
built on: [The write-post Skill](/docs/claude-code/skill-write-post).
