Ruby on Rails, Claude Code and Worktrees
Here’s how I use Claude Code to develop Ruby on Rails applications. Mostly medium sized ones that have been around for a while. No green-field, everything-is-possible, agents-go-wild vibe coding for me. I’ll leave that to others.
I don’t know what I’m doing. Wait, that’s not entirely right. I know what I’m doing when it comes to Rails. I don’t know what I’m doing when it comes to Agentic Coding. But that’s okay, nobody does.
What We’re Working With
The apps I work on are as stock-standard Ruby on Rails as it gets. Some use Bootstrap, some use Tailwind. Some use Postgres, some SQLite. Some have trace amounts of Javascript in them. However, they all follow the default Rails architectural patterns: models, views, and controllers. There are no service objects and no event-based systems.
In this context, Claude works great. It eliminates drudgery. I’ve used it to update a bunch of views after extensive model changes. I’ve used it to make significant updates to models and their validations. I’ve added a lot of translations with Claude. I disable all permission checks, and I’m not alone.
It’s nice. While Claude works on boring stuff, you’re free to do other things. You could grab a coffee. You could play with your kids. Or you could work on the hard stuff that Claude sucks at. You could also start more instances of Claude Code to work on even _more things.
However, that requires some measure of isolation. You certainly don’t want to work on the same code as Claude while it’s doing its thing - that way lies pain.
Isolation with Worktrees
Using git-worktrees you create a new branch that is contained within a specific folder. Then, you start an instance of Claude Code in that folder, and it won’t interfere with the rest of your codebase. Voila, you’ve achieved perfect isolation. I didn’t figure this one out myself, by the way. I just read the manual.
I like to keep the /worktrees
directory within my project folder. I found that I can make my life a lot easier by doing some scripting and by using Tmux. Here’s a simplified version of a script I’m using to start a new Claude Code instance with a new prompt, on a new branch, in it’s own worktree.
WORKTREES_DIR="./worktrees"
create_worktree() {
local feature_description="$1"
local prompt="$2"
local text=${feature_description// /-}
text=$(echo "$text" | tr '[:upper:]' '[:lower:]')
local date
date=$(date +"%y-%m")
local branch_name="$date-$text"
local worktree_path="$WORKTREES_DIR/$branch_name"
tmux split-window -t "worktrees" -c "$worktree_path" "echo '$prompt' | claude --dangerously-skip-permissions"
}
create_worktree "$@"
So here’s what I usually do. I have a Tmux session running for my Rails project. When I want to offload some work to Claude I start a new Tmux Window/Pane using this little helper script and let it go wild. I might check in periodically, while I keep working on a different branch in my main window. Mostly I rely on hooks, though, to notify me when Claude is done.
Now, what to do with the code that our friendly AI spit out?
Review Time
With Rails, I found that automated checks only go so far. I like to test things - especially if there’s view changes involved. I could have Claude open a PR and review that, but that doesn’t do it for me. I want to switch to the branch, run a diff, and start Rails on the branch to verify the results.
Once again, we can leverage Tmux - and it’s good friend Tmuxinator. I create a new local .tmuxinator
file in each of my projects. This gives me the flexibility I need. Every project is different.
name: worktree
root: <%= @args[0] %>
on_project_start: yarn install
windows:
- editor:
panes:
- vim: nvim
- background:
layout: tiled
panes:
- server: PORT=$(nextport 3000) bundle exec foreman start -f Procfile.dev
To avoid port conflicts with already running Rails servers, I use a simple script to get the next open port. Then, we can start reviewing by running tmuxinator start --append worktree <path-to-worktree>
.
Now I can make manual changes if necessary using the new editor instance, or tell Claude to open a PR.
What’s next?
I don’t know. Nobody knows; the landscape changes quickly. For Rails apps, within the constraints I’ve outlined, this works wonderfully for now. I’ve run up to three instances of Claude code in parallel. Now, those are rookie numbers, and I gotta get those up - I’m working on it. There are limits on how much you can parallelize in any given code base.
You can find the full script I use to manage worktrees and Claude Code in this repository. Have fun!