Introducing ruby-upgrade-toolkit
Make Ruby and Rails upgrades a Tuesday-afternoon job, not a multi-week saga.
If you’ve ever upgraded a Rails 5 app to Rails 7, jumped Ruby 2.7 to 3.3 across keyword-argument breaking changes, or untangled a Gemfile that hadn’t moved in three years — you know exactly what I mean.
There’s no shortage of guidance: official Rails upgrade guides,
gem-by-gem changelogs, scattered blog posts about Zeitwerk migrations,
framework-defaults toggles, and bin/rails app:update diffs. The
problem isn’t a lack of knowledge.
It’s that executing the work — sequencing it correctly, handling intermediate version hops, keeping the test suite honest at every step, and not blowing up a Friday production deploy — is mechanical, repetitive, and unforgiving. It’s exactly the kind of work that ought to be automated.
That’s what ruby-upgrade-toolkit does. It’s a Claude Code plugin that gives Claude a structured, repeatable methodology for Ruby and Rails upgrades — separately or together, any version pair. You install it, point it at your project, and it audits, plans, fixes, verifies, and commits — phase by phase, with you in the loop at every checkpoint.
Three modes — pick the one that matches your appetite for risk #
The toolkit ships six commands. You don’t have to memorize them; the modes compose:
- Fully automated (
/upgrade ruby:3.3.1 rails:7.2) — one command, everything. Detects current versions, computes intermediate hops, runs each phase, prompts you for the per-phase commit, auto-advances when you approve. Best when you trust the methodology and want to move fast. - Review first, then automate (
/audit → /plan → /upgrade) — read-only audit surfaces the breaking changes; plan shows you the exact phase sequence with effort/risk estimates; upgrade then executes the same plan. Best for major version jumps and large codebases where you want to inspect the scope before committing. - Fully manual (
/audit → /plan → /fix nextrepeatedly) — same per-phase machinery, but you drive each step. Best when you want maximum control or you’re applying fixes to a specific scope (scope:app/models/user.rb).
The three modes share the same per-phase work — /upgrade is just
a loop over /fix next calls with prerequisite checks bolted on. So
if you start automated, hit a snag, and want to drop into manual, your
in-progress task list comes with you.
Why phased commits matter #
The unit of work in this toolkit isn’t “the upgrade.” It’s the
phase. Going Rails 6.1 → 8.0 isn’t one operation; it’s three (6.1
→ 7.0 → 7.1 → 8.0). Each phase ends with verification — RSpec green,
RuboCop clean, deprecation count, Zeitwerk happy — and a git
commit with a detailed message itemizing what changed. Failures
pause the pipeline before any commit happens; the working tree holds
the problematic state and you decide whether to investigate, retry, or
abort. There’s no opaque “rollback” to debug — you just have ordinary
git commits at every successful checkpoint, and a dirty tree when
something breaks. Bisecting after a regression is a git bisect away.
Estimates aren’t guesses, either. The plan command derives effort,
risk, and blast radius from concrete grep counts and bundle outdated
output — keyword-argument call sites, deprecated API matches,
native-extension gem flags — and ships every total with the formula so
you can audit or override it.
Custom rules for project-specific policies #
Released in v0.9: a /rules command for the constraints the toolkit
can’t infer on its own. Things like:
- Pin a gem (
devise >= 4.9for compliance reasons) - Swap a gem mid-upgrade (
phantomjs → selenium-webdriverplus the Capybara config rewrite) - Substitute the upgrade target itself (mainline Rails → Rails LTS, paid backports — credentials read from an env var, never stored in the rules file)
- Add verification gates (every phase must pass
bundle exec brakeman --exit-on-warnbefore the commit) - Tweak whitelisted policy knobs (skip RuboCop, require zero deprecations, etc.)
Rules live in .ruby-upgrade-toolkit/rules.yml at the project root —
committed to your repo, reviewable in PRs like any other config. Every
command (audit, plan, fix, upgrade, status) picks the file
up automatically. Rule-driven steps appear inline in plan checklists
tagged [rule: <id>]; rule outcomes show up in fix’s commit messages
so the whole upgrade is auditable in git history. With no rules.yml,
every command behaves byte-identically to before — the feature is
strictly additive.
Try it #
/plugin marketplace add dhruvasagar/ruby-upgrade-toolkit
/plugin install ruby-upgrade-toolkit@dhruvasagar
Then in any Ruby project:
/ruby-upgrade-toolkit:audit ruby:3.3.1 rails:7.2
And here’s an exerpt of what it produces:
# Ruby Upgrade Audit Report
Current: Ruby 2.7.8
Target: Ruby 3.3.1
Upgrade Path: 2.7 → 3.0 → 3.1 → 3.2 → 3.3 (4 phases required)
## Test Suite Baseline
- Status: PASSING
- 312 examples, 0 failures
## Critical Issues
### Keyword Argument Mismatches (Ruby 3.0)
- 18 methods with **kwargs or opts={} patterns
- 34 call sites with potential hash/keyword mismatch
- Preview warnings from Ruby 2.7: 22 unique warning sites
### Unsafe YAML.load calls
- 3 occurrences: lib/config_loader.rb, lib/serializer.rb, config/initializers/legacy.rb
## Effort Estimate
Overall: Medium
- Keyword arg fixes: ~34 sites (automated with review)
- YAML fixes: 3 files (automated)
- RuboCop TargetRubyVersion gap: 2.7 → 3.3 (expect new cops)
The audit is read-only — it won’t touch a single file. Read the
report, decide whether to keep going, and either run /plan to see
the roadmap or /upgrade to execute it.
The full README, all six command references, and four worked walkthrough scenarios (Ruby-only, coordinated Ruby+Rails, Rails-only, and a custom-rules walkthrough using Rails LTS + Brakeman + a gem swap) are at github.com/dhruvasagar/ruby-upgrade-toolkit.
If you’ve been putting off an upgrade, this is the week to start.