Engineering Checklists for Ruby and Rails
Engineering Checklists for Ruby and Rails
I keep checklists for the same reason pilots do: not because the steps are hard, but because skipping one under pressure is easy. Every checklist here comes from real work — deploys that broke, upgrades that stalled, debugging sessions that went in circles because I forgot something obvious.
Print these. Pin them next to your monitor. Work through them in order. The order matters.
Pre-Deployment Checklist
Run through this before every production deploy. No exceptions, no shortcuts. The five minutes this takes will save you hours of incident response. For the full deployment picture, see the Rails Deployment topic page and Deploy Ruby on Rails on a VPS.
- All tests pass on the branch being deployed. Run the full suite, not just the files you changed. CI should be green. If you're deploying without CI, that's a separate problem to solve.
- Gemfile.lock is committed and up to date. Run
bundle installlocally and confirm the lock file hasn't drifted. If it has, commit the updated lock file and push before deploying. - Database migrations are backward-compatible. If the migration drops a column, renames a table or changes a type, the currently-running code must still work against the new schema. Deploy the migration separately from the code that depends on it.
- Environment variables are set on the target server. Check that any new ENV vars your code references are present in production. A missing
DATABASE_URLorREDIS_URLwill crash the app on boot. - Asset compilation succeeds in the production environment. Run
RAILS_ENV=production bundle exec rails assets:precompilelocally or in your build step. CSS/JS build failures at deploy time are preventable. - Health check endpoint returns 200. After deploy, hit
/upor/healthand confirm you get a success response. Automate this — don't rely on remembering to check manually. - Background job workers are restarted. Sidekiq, GoodJob or Solid Queue workers run your application code. If you deploy new code but don't restart workers, they're still running the old version. This causes subtle, maddening bugs.
- Cron jobs and scheduled tasks reference the correct code path. If you use whenever, solid_queue recurring jobs or system crontabs, verify they point to the right release directory or container.
- Log output is flowing. SSH into the server (or check your log aggregator) and confirm that requests are being logged. Silent logs after a deploy usually mean the app didn't boot correctly.
- Rollback plan is ready. Know exactly what command you'll run if this deploy goes wrong. With Kamal, that's
kamal rollback. With Capistrano, it'scap production deploy:rollback. Practice it before you need it.
Rails Upgrade Checklist
For major and minor Rails version bumps. This is deliberately conservative — I'd rather take an extra hour on the upgrade than spend a weekend debugging production. For a detailed walkthrough, see the Rails 8 Upgrade Checklist guide.
- Read the official release notes and upgrade guide for the target version. Every Rails release includes a detailed upgrade guide. Read the whole thing, not just the summary.
- Create a dedicated branch for the upgrade. Don't upgrade Rails on main/master. You want a clean diff and the ability to abandon the branch if things go sideways.
- Update the Rails gem version in your Gemfile and run
bundle update rails. Let Bundler resolve the dependency tree. If it can't resolve, you have gem version conflicts to address first. - Run
rails app:updateand review every file change. This command generates new config files and shows diffs. Don't blindly accept changes — review each one against your existing configuration. - Run the full test suite and fix all failures. Deprecation warnings from the previous version often become errors in the new version. Work through them methodically.
- Check for deprecated gem dependencies. Some gems drop support for older Rails versions, others haven't added support for the new version yet. Check each gem's compatibility.
- Update framework defaults incrementally. Rails includes
config.load_defaultsto opt into new defaults. Bump this version and test thoroughly. New defaults can change behavior in subtle ways — session serialization, cookie rotation, form handling. - Test asset compilation under the new version. The asset pipeline changes between Rails versions are a frequent source of upgrade pain. Verify CSS, JS and image assets compile and render correctly.
- Run the upgrade in a staging environment before production. Deploy the upgraded branch to a staging server and run through your critical user flows manually. Automated tests don't catch everything.
- Deploy to production during a low-traffic window. Even with a staging test, production is different. Give yourself room to observe, react and roll back if needed.
Production Debugging Checklist
When something is broken in production and you're under pressure, it's easy to flail. This checklist imposes order on the chaos. For a deeper framework, see Debugging Production Rails Issues and the Debugging and Maintenance topic page.
- Check the error tracker first. If you're running an exception monitoring service, look at the most recent exceptions. The stack trace tells you where; the frequency tells you how bad.
- Read the application logs.
tail -f log/production.logor check your log aggregator. Look for error messages, stack traces and unusual patterns. Filter by timestamp around when the problem was first reported. - Check server resource utilization. Run
htop,free -manddf -h. High CPU, exhausted memory or a full disk are common causes of degraded behavior that won't show up in application logs. - Verify database connectivity and performance. Connect to the database directly and run a simple query. Check for long-running queries with
SELECT * FROM pg_stat_activity WHERE state = 'active'. A locked or overloaded database causes cascading failures. - Verify background job queues. Check your job processor's dashboard or CLI. Are jobs piling up? Are workers running? A dead Sidekiq process means emails don't send, webhooks don't fire and async work silently stops.
- Check recent deployments. Correlate the problem's start time with your deploy history. If the timing matches, the deploy is your prime suspect. Roll back first, investigate second.
- Test the health check endpoint. Hit
/upor/healthfrom outside your network. If it fails, the issue is at the application or infrastructure level. If it passes, the issue may be isolated to specific functionality. - Reproduce the issue locally if possible. Pull production logs, replicate the request or data state and try to trigger the same error in development. Once you can reproduce it, you can fix it.
New Server Setup Checklist
For provisioning a fresh VPS or dedicated server for a Rails application. This covers the foundation — the application deployment itself is a separate process. See Deploy Ruby on Rails on a VPS for the full deployment walkthrough.
- Create a non-root user with sudo access. Never run application processes as root. Create a deploy user, add it to the sudo group and disable root SSH login.
- Configure SSH key authentication and disable password login. Copy your public key to the server, confirm key-based login works, then set
PasswordAuthentication noin sshd_config and restart sshd. - Set up a firewall (ufw or iptables). Allow SSH (22), HTTP (80), HTTPS (443) and nothing else by default. Open additional ports only as needed and only from specific IP ranges where possible.
- Install and configure Nginx. Nginx serves as your reverse proxy and SSL termination point. Configure it to proxy requests to Puma on a Unix socket or localhost port. See Nginx for Rails Apps for configuration details.
- Obtain and configure SSL certificates. Use Certbot with Let's Encrypt. Set up automatic renewal with a cron job or systemd timer. Verify renewal works with
certbot renew --dry-run. - Install Ruby using a version manager. Use rbenv, asdf or chruby on the server. System Ruby is usually outdated and modifying it can break OS-level tools. Install the exact Ruby version your application requires.
- Install and configure PostgreSQL. Create a database user for your application with limited privileges. Configure
pg_hba.conffor local socket authentication. Set appropriateshared_buffers,work_memandeffective_cache_sizevalues. See PostgreSQL Indexing for Rails for ongoing database optimization. - Set up automated backups. Configure
pg_dumpon a cron schedule, writing backups to a separate volume or remote storage. Test restoration from a backup before you consider the setup complete. Untested backups aren't backups.
Performance Audit Checklist
For a structured review of a Rails application's performance characteristics. This isn't about premature optimization — it's about identifying the bottlenecks that actually affect users. For the broader context, see Web Performance for Rails Developers and Ruby Performance.
- Enable and review rack-mini-profiler in development. This badge in the corner of every page shows render time, SQL query count and query time. If a page runs 30+ queries, you have an N+1 problem.
- Run the Bullet gem in development to detect N+1 queries. Bullet alerts you to missing eager loading and unused eager loading. Fix the alerts it raises before they become production performance problems.
- Analyze slow queries using
EXPLAIN ANALYZE. Identify the 5-10 slowest queries in your application (your APM tool or pg_stat_statements can tell you which ones). Run each throughEXPLAIN ANALYZEand check for sequential scans on large tables, missing indexes and inefficient joins. - Check Puma thread and worker configuration. Verify your Puma config matches your server's resources. A common mistake is running too few workers on a multi-core machine or too many threads with a small database connection pool.
- Audit memory usage across requests. Monitor RSS memory of your Puma workers over time. Steadily growing memory indicates a leak. The
derailed_benchmarksgem can help isolate which gems or code paths are responsible. - Review asset delivery. Check that static assets are served with far-future cache headers, gzip/brotli compression is enabled and your CDN (if you have one) is actually caching. Use browser developer tools to audit response headers.
- Check Ruby and YJIT versions. If you're on Ruby 3.1+, confirm YJIT is enabled in production (
RubyVM::YJIT.enabled?). YJIT alone can improve throughput by 15–25% on typical Rails workloads. If you're on an older Ruby, upgrading may be the single highest-leverage performance improvement available. - Profile a representative request end-to-end. Pick your most common request (usually a page load or API call) and trace it from Nginx through Puma, through Rails routing, controller, view rendering and database queries. Know where the time goes before deciding where to optimize.
These checklists reflect how I work. They'll evolve as tools and practices change. If you spot a missing step or a better ordering, the contact page is open.