Skip to Content
Course content

185: Scheduling Recurring Jobs with Whenever

Click on the "Edit" button in the top corner of the screen to edit your slide content.

At some point in every professional project, you'll need something to happen automatically on a schedule. Maybe it's clearing out a cache, sending a weekly digest email, or syncing data from a third-party API. When I first started out, my instinct was to just jump straight into the server's crontab. It feels like the "pure" way to do it, but in a team environment, it's a recipe for disaster.

The fragility of manual crontab entries

Imagine we're building a system that sends a "Weekly Activity Summary" to users every Monday at 8:00 AM. The naive approach is to SSH into your production server, run crontab -e, and drop in a line like this:

0 8 * * 1 /bin/bash -l -c 'cd /var/www/my_app/current && RAILS_ENV=production bundle exec rake summary:send_weekly'

On the surface, it works. But here is where the friction starts. First, cron syntax is notoriously cryptic—one misplaced asterisk and your job runs every minute instead of every Monday, potentially spamming your entire user base. Second, and more importantly, this configuration lives exclusively on the server. It isn't in Git. When a new developer joins the team and sets up their local environment, or when you spin up a new staging server, that schedule doesn't exist. You have to remember to manually copy-paste it, which is exactly how "it works in production but not in staging" bugs are born.

Treating your schedule as code

This is why I use the whenever gem. It doesn't actually replace cron; instead, it gives us a Ruby DSL (Domain Specific Language) to describe our schedule in a file that lives right inside our application repository. We stop writing bash strings and start writing Ruby.

Once you add gem 'whenever', require: false to your Gemfile and run the initializer, you get a config/schedule.rb file. That same weekly summary task now looks like this:

every :monday, at: '8am' do
  rake "summary:send_weekly"
end

I love this because it's human-readable. I don't have to double-check a cron cheat sheet to remember if the fifth field is the day of the week or the month. More importantly, your schedule is now version-controlled. If we decide to move the email to Tuesday at 10:00 AM, I make a pull request, the team reviews it, and it gets deployed along with the rest of the code.

The trade-off in deployment

Now, there is a small catch: whenever doesn't magically "run" in the background. It is a translator. It reads your schedule.rb and writes the actual crontab entries for you. This means you have to integrate the update step into your deployment pipeline.

If you're using Capistrano, there's a built-in recipe for this. If you're using a simpler script or a CI/CD pipeline, you just need to run bundle exec whenever --update-crontab during the deploy process. It feels like an extra step, but it's a tiny price to pay for the peace of mind that comes with knowing exactly what is scheduled to run across all your environments without having to SSH into a box and pray you didn't typo a cron expression.




📋 Practical Task

Implementing a Daily Database Archive Task

You have been asked to implement a maintenance task that archives old logs every night at 3:00 AM. You already have a Rake task defined as logs:archive_old.

Your goal is to move the scheduling out of the manual system crontab and into the application code using the whenever gem. Perform the following steps:

  1. Create a config/schedule.rb file.
  2. Define a recurring job that runs the logs:archive_old rake task every day at 3:00 AM.
  3. Add a second job that runs a custom shell command /usr/bin/backup_script.sh every Sunday at midnight.
  4. Verify your work by running bundle exec whenever in your terminal to see the generated cron output without actually writing it to the system crontab.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.