Claude Code for Rails: Skills, Setup, and Workflow
Set up Claude Code in a Rails project: CLAUDE.md memory, permissions, Skills, subagents, and the RubyMine and VS Code workflows Rails developers actually use.
Claude Code is Anthropic's agentic coding tool: it reads your codebase, edits files, and runs commands from your terminal or IDE. For a Rails developer that means it can run your RSpec suite, generate a migration, trace a bug through your models, refactor a fat controller, and open a pull request, all while asking permission before it writes or runs anything. This post is about the tool and how to fit it into a real Rails workflow, not about building your own agent.
That distinction matters. If you want to embed an agent inside your own app, using the model as a feature your users interact with, that is a different job, and I have written about building AI agents in Ruby with the Anthropic SDK and about the Claude Agent SDK in Ruby separately. Claude Code is the coding assistant you run on your machine. The rest of this is the setup and the day-to-day patterns that make it useful on a Rails codebase.
One caveat up front: this tooling moves fast. Everything here reflects the Claude Code docs as of early July 2026. Command names, defaults, and IDE features change between releases, so treat exact flags as a starting point and check claude --version and the official docs when something does not match.
What Claude Code Is for Rails Developers
Claude Code runs as a command-line tool (claude), a native VS Code extension, a JetBrains plugin, a desktop app, and a web/mobile surface. On a Rails project the terminal CLI and the IDE integrations are what you will use most. It works with any paid Claude subscription (Pro, Max, Team, or Enterprise) or a Console account, so no separate API key is required for interactive use.
What it is good at: the tedious, well-scoped work that eats your afternoon. Writing specs for an untested service object, fixing RuboCop offenses across a directory, drafting a migration and the model changes that go with it, updating a gem and chasing the fallout, writing a first-draft commit message. It is not a replacement for understanding your own domain, and it will confidently do the wrong thing if your instructions or your codebase are ambiguous. You stay the reviewer.
Here is how it compares to the two tools most Rails teams already have open.
| Capability | Claude Code | Cursor | GitHub Copilot |
|---|---|---|---|
| Primary surface | Terminal CLI, plus VS Code and JetBrains | Its own VS Code fork | Editor extension (VS Code, JetBrains) |
| Runs shell commands (rspec, rails, git) | Yes, with a permission prompt | Yes, in agent mode | Limited, via the agent/CLI |
| Multi-file, whole-repo edits | Yes | Yes | Yes, improving |
| Project memory file | CLAUDE.md (plus auto memory) |
.cursorrules / rules |
Repo custom instructions |
| Custom repeatable commands | Skills (SKILL.md) |
Rules and commands | Prompt files |
| Subagents in their own context | Yes | Partial | No |
| Inline completions as you type | No (task-driven) | Yes | Yes (its strength) |
| Runs headless in CI / pipes | Yes (claude -p) |
No | Via Copilot CLI |
The rough division I use: Copilot for inline autocomplete while I type, Claude Code for anything that spans multiple files or needs to run a command and read the result. They are not mutually exclusive, and plenty of people run Copilot completions alongside Claude Code in the same editor.
Setting Up Claude Code in a Rails Project
Install the CLI, drop a CLAUDE.md in the repo root, set a permission allowlist, and connect your editor. That is the whole setup, and it takes about ten minutes.
Install with the native installer or Homebrew, then start it from your project root:
# Native install (macOS, Linux, WSL)
curl -fsSL https://claude.ai/install.sh | bash
# Or with Homebrew
brew install --cask claude-code
# Start it inside your Rails app
cd my_rails_app
claude
Start it from the repo root, not a subdirectory, so it loads the project CLAUDE.md and can see the whole tree.
The CLAUDE.md Project Memory File
CLAUDE.md is a markdown file Claude Code reads at the start of every session. It is the single highest-leverage thing you can set up, because it is where you write down what you would otherwise re-explain every time. On a Rails app the file lives at ./CLAUDE.md or ./.claude/CLAUDE.md and gets committed so the whole team shares it. Personal, uncommitted notes go in ./CLAUDE.local.md (add it to .gitignore), and machine-wide preferences go in ~/.claude/CLAUDE.md.
Run /init to generate a first draft. Claude scans your codebase and writes down the build commands, test setup, and conventions it can find. Then trim it to the things it could not infer on its own. A good Rails CLAUDE.md is short and specific:
# MyApp
## Stack
- Ruby 3.4.7, Rails 8.0, PostgreSQL 16, Hotwire, Solid Queue.
- Tests: Minitest (not RSpec). Run with `bin/rails test`.
- Authorization: Pundit. Every controller action calls `authorize`.
## Conventions
- Business logic lives in `app/services/`, one public method `call`.
- Never write raw SQL in models; use scopes and Arel.
- Prefer `bin/rails g` generators, then edit, over hand-writing files.
## Commands
- Lint: `bundle exec rubocop -A`
- Type/db check before commit: `bin/rails db:migrate:status`
## Do not touch
- `db/schema.rb` by hand; regenerate it via migrations.
- Anything under `vendor/` or `config/credentials/`.
Keep it under about 200 lines. It loads into the context window every session, so a bloated file both costs tokens and, in practice, gets followed less reliably. If a section grows into a multi-step procedure, that is a signal to move it into a Skill (more on that below) or a path-scoped rule under .claude/rules/ instead. For example, a .claude/rules/testing.md with paths: ["test/**/*.rb"] in its frontmatter only loads when Claude touches a test file.
A newer feature worth knowing: alongside the CLAUDE.md you write, Claude Code keeps its own auto memory per repository, saving things it learns (the exact test command, a gotcha in your setup) across sessions without you writing anything. You can browse and edit all of it with the /memory command. The short version: write the facts, let /init bootstrap it, and prune regularly.
Permissions and the Allowlist
Claude Code asks before it edits a file or runs a shell command. That is the safety model, and on a Rails project you tune it so the common, safe things stop prompting while destructive things always ask. Manage rules with /permissions; they are stored in .claude/settings.json and can be committed for the team.
{
"permissions": {
"allow": [
"Bash(bin/rails test *)",
"Bash(bundle exec rspec *)",
"Bash(bundle exec rubocop *)",
"Bash(bin/rails g *)",
"Bash(git commit *)"
],
"deny": [
"Read(config/credentials/**)",
"Read(.env*)",
"Bash(git push *)",
"Bash(bin/rails db:drop *)"
]
}
}
Rules are evaluated deny first, then ask, then allow, so a deny always wins. The deny reads on config/credentials/** and .env* matter: they keep your master key and secrets out of the model's context entirely, and they apply to Claude's file tools and to shell reads like cat and sed. Read-only commands (ls, cat, grep, and read-only git) run without a prompt by default, which is why exploration feels fast and edits feel deliberate.
There are also permission modes you switch between: default (prompt on first use of each tool), plan (read and explore only, no edits, good for "tell me how you would fix this" before it touches anything), and acceptEdits (auto-accept file edits). There is a bypassPermissions mode that skips prompts entirely; I only use it inside a throwaway container, never against a real repo.
Ruby LSP for Semantic Navigation
Claude Code has an official Ruby LSP plugin, and for a Rails codebase it is the upgrade that pays off fastest. By default Claude finds symbols with text search (grep), which is fine until you have three Invoice classes in different namespaces. The plugin wires in Shopify's Ruby LSP so Claude gets real semantic queries instead: list the classes and methods in a file, jump to where a symbol is defined, find every usage of it, pull type info and docs, and search symbols across the whole project. That turns "rename the Invoice module everywhere" from a fragile find-and-replace into a language-server-backed refactor.
Setup is a plugin install plus a Gemfile line. Make sure Ruby 3.0 or newer is available and Ruby LSP is installed, then enable the plugin:
# Prerequisite: the language server gem (or add gem "ruby-lsp" to your Gemfile)
gem install ruby-lsp
# Install the official plugin
claude plugin install ruby-lsp@claude-plugins-official
Then enable it and the LSP tool in ~/.claude/settings.json and restart Claude Code:
{
"env": { "ENABLE_LSP_TOOL": "1" },
"enabledPlugins": { "ruby-lsp@claude-plugins-official": true }
}
It covers .rb, .rake, .gemspec, .ru, and .erb, and because Ruby LSP auto-loads its ruby-lsp-rails add-on when it detects a Rails app, the symbols Claude sees are Rails-aware.
Separately, if you run Claude Code inside VS Code or a JetBrains IDE, the editor integration also shares your language server's diagnostics, the same red underlines you see in the Problems panel, through a built-in ide connection (exposed to the model as mcp__ide__getDiagnostics). So when an edit breaks a method resolution, Claude can notice and correct itself on the next turn. The plugin gives it semantic navigation; the IDE connection gives it your live error list. They stack.
Using Skills for Rails Work
A Skill is a SKILL.md file that packages a repeatable procedure and loads only when you use it. You take a checklist or multi-step task you keep re-typing and turn it into a slash command your whole team can run. Because a Skill's body loads on demand rather than every session, a long reference procedure costs almost nothing until you invoke it, which is exactly why you move procedures out of CLAUDE.md and into Skills.
Skills live in .claude/skills/<name>/SKILL.md for a project or ~/.claude/skills/<name>/SKILL.md for your personal set. The directory name becomes the command. The frontmatter description tells Claude when the Skill is relevant, so it can load it automatically, or you invoke it directly with /name.
Here is a Rails-shaped example: a Skill that scaffolds a Pundit policy the way your team writes them.
---
description: Scaffold a Pundit policy and matching spec for a model. Use when the user asks to add authorization for a new model or resource.
---
## Instructions
When asked to add a policy for a model:
1. Create `app/policies/<model>_policy.rb` subclassing `ApplicationPolicy`.
2. Define `index?`, `show?`, `create?`, `update?`, `destroy?`, each
scoped to `user` and `record`. Default deny; open up explicitly.
3. Add a `Scope` class that filters records to the current user's account.
4. Generate `test/policies/<model>_policy_test.rb` with a case per action,
proving a user from another account is denied.
5. Run `bin/rails test test/policies/` and report the result.
Now /add-policy for Invoice produces the policy, the scoped query, and a test that proves cross-tenant isolation, in your house style. You can also inject live context: a line like !`git diff HEAD` in a Skill runs the command and inlines its output before Claude reads the Skill, which is how a /review-changes Skill grounds itself in your actual working tree.
Claude Code also ships bundled Skills you get for free, including /code-review, /debug, and /loop. And custom slash commands (the older .claude/commands/ files) have been merged into the Skills system, so anything you already had there keeps working.
In Your Editor: RubyMine and VS Code
Both major editor integrations do the same core thing: run Claude Code next to your code, show its edits as IDE diffs, and feed it your selection and diagnostics.
The VS Code extension is a native graphical panel (the recommended way to use Claude Code in VS Code, not just a terminal). It gives you inline diffs you can accept or reject, @-mentions of files with specific line ranges, plan review before Claude starts, conversation history, and checkpoints that let you rewind Claude's file edits. Install it from the Extensions view or the marketplace. It bundles its own copy of the CLI for the panel, but if you also want to type claude in the integrated terminal you need the standalone CLI install too.
The JetBrains plugin is how you run Claude Code in RubyMine. Install the Claude Code plugin from the JetBrains Marketplace, install the CLI separately, and the plugin runs claude inside your IDE's integrated terminal. You get quick launch with Cmd+Esc / Ctrl+Esc, diffs shown in the IDE's diff viewer, automatic selection-context sharing, file-reference shortcuts (Cmd+Option+K to insert a @path#L1-99 reference), and diagnostic sharing from the IDE's language server.
One honest note on RubyMine specifically: the plugin documentation lists "most JetBrains IDEs, including IntelliJ IDEA, PyCharm, WebStorm, GoLand, PhpStorm, and Android Studio." RubyMine is not called out by name in that list as of mid-2026. In practice it is a standard JetBrains IDE and the plugin works the same way, because the integration is just launching the claude CLI in the IDE's terminal and connecting to it. If you would rather not depend on the plugin at all, you can always open RubyMine's built-in terminal and run claude there directly; you lose the in-IDE diff viewer but keep everything else. The plugin is currently marked Beta, so expect rough edges.
Subagents: Isolating the Noisy Work
When a side task would flood your main conversation with search results or file dumps, Claude Code can hand it to a subagent that works in its own context window and returns only a summary. This is a helper agent inside the tool, not an agent in your app. You manage them with /agents, and they are markdown files in .claude/agents/ (project) or ~/.claude/agents/ (personal):
---
name: rails-test-writer
description: Writes and runs Minitest tests for Rails models and services. Use when asked to add test coverage.
tools: Read, Grep, Glob, Edit, Bash
model: sonnet
---
You write focused Minitest tests for this Rails app. Read the target
class, cover the happy path and the important edge cases, prove
authorization boundaries hold, and run `bin/rails test` on the files
you create. Return a short summary of what you added and the result.
Claude delegates to it when a task matches the description, or you invoke it explicitly. The win is context hygiene: the test-writing back-and-forth stays in the subagent's window, and your main session stays focused on the feature.
Real Rails Workflows
The four Rails jobs Claude Code handles best are writing and running tests, generating migrations with backfills, refactoring across files, and reviewing a diff. Each runs the same loop: it edits, runs a command behind your permission prompt, reads the output, and iterates. Here are the patterns I actually use.
Writing and running tests. "Write Minitest coverage for RefundService, then run it." Claude reads the service, writes the test file, runs bin/rails test, reads the failures, and iterates until green. Because the command runs behind a permission prompt you allowlisted, this is fast without being reckless. The subagent above makes it repeatable.
Migrations. "Add a status enum to Invoice with a backfill." Claude generates the migration, updates the model, and can run bin/rails db:migrate after you review the diff. I keep db:drop and db:reset denied so it can never blow away a database, even in development, without me typing yes.
Refactors across files. "Extract the PDF generation out of InvoicesController into a service object following our app/services/ convention." This is where whole-repo awareness pays off: it finds the callers, moves the logic, updates them, and runs the tests. The CLAUDE.md convention line ("one public method call") is what keeps the output in your house style instead of some generic pattern.
Code review. Run the bundled /code-review Skill on your working diff, or pipe a diff in headlessly:
git diff main --name-only | claude -p "review these changed files for N+1 queries, missing authorization, and unsafe migrations"
That claude -p (print/headless) mode is the same thing you would drop into a CI step or a git hook.
Hooks: Enforce, Don't Suggest
CLAUDE.md shapes what Claude tries to do, but it is guidance, not enforcement. When something must happen at a specific moment, use a hook: a shell command Claude Code runs at a lifecycle event, configured in .claude/settings.json. The events include PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop, and more. The classic Rails use is auto-formatting after every edit:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{ "type": "command", "command": "bundle exec rubocop -A --force-exclusion \"$CLAUDE_FILE_PATHS\"" }
]
}
]
}
}
Now every file Claude edits gets RuboCop-corrected automatically, so its output matches your style guide whether or not it remembered to. A PreToolUse hook can go further and block edits to protected paths outright.
Connecting Your Own Tools with MCP
Claude Code supports the Model Context Protocol, so it can talk to external systems: your database, GitHub, Sentry, a Jira board. You add servers with claude mcp add (stdio, SSE, or HTTP transports), and project-level server config lives in a .mcp.json you can commit. A read-only Postgres MCP server, for instance, lets Claude inspect your actual schema and run explain plans while it writes a query, instead of guessing from schema.rb.
Claude Code vs Cursor and Copilot for Rails
For Rails work the division is clear: Copilot wins inline as-you-type completion, Cursor wins as an all-in-one AI editor, and Claude Code wins the terminal-first, command-running loop that runs your specs and reads the output. Copilot is unmatched at inline, as-you-type completion, and it is cheap, so I leave it on. Cursor is a full editor built around AI with strong agentic editing, and if you want everything inside one polished GUI it is a real option. Claude Code's differentiators for Rails work are the terminal-first, command-running loop (it runs your specs and reads the output as part of the task), the CLAUDE.md plus Skills plus subagents customization stack, and the fact that it is composable: claude -p drops into CI, git hooks, and shell pipelines the way Copilot and Cursor do not.
The pragmatic answer is that these overlap and you do not have to pick one. Many Rails developers run Copilot for completions and Claude Code for multi-file, test-running tasks in the same session.
Limitations and Gotchas
Claude Code's main limitations are four: it is not autonomous and needs every diff reviewed, long sessions cost tokens and money, permissions are guardrails not a sandbox, and the tooling changes weekly. Every tool in this category has sharp edges, and pretending otherwise wastes your time.
It is not autonomous, and you should not treat it as if it were. CLAUDE.md is context, not a contract; the docs are explicit that Claude reads it and tries to follow it with no guarantee of strict compliance. Vague instructions produce vague results, and two contradictory lines in your memory files mean it picks one arbitrarily. The review step is not optional. Read every diff, especially on migrations and anything touching authorization or money.
Cost and context are real constraints. A long session accumulates conversation history, and every token is one you pay to process on the next turn. Use /compact when a session gets long, cap the scope of what you ask for, and lean on subagents to keep exploration out of your main context. If you are on a metered Console account rather than a subscription, a sprawling refactor can run up a bill faster than you expect. Route heavy reasoning to a stronger model with /model and keep routine edits on a cheaper tier.
Permissions are guardrails, not a sandbox. Deny rules stop Claude's own tools, but a bare Bash allow rule can still shell out to something that reads a file indirectly. For untrusted code or real enforcement, combine permission rules with the OS-level sandbox rather than relying on prompts alone. And never run bypassPermissions against a repo you care about.
The tooling changes weekly. Features get renamed, defaults shift, the JetBrains plugin is still Beta. Anything specific here can drift. Pin your expectations to claude --version and the current docs, not to a blog post, including this one.
When I do not reach for it. For a one-line fix I already understand, opening Claude Code is slower than just typing it. For work that needs real product judgment, a nuanced pricing change, a schema decision with downstream consequences, I do the thinking myself and use Claude Code to execute the mechanical parts once the decision is made. It is a very good pair of hands, not a substitute for knowing what you want built.
Where to Start
Install the CLI, run /init in your Rails repo, and spend ten minutes trimming the generated CLAUDE.md to the handful of facts Claude cannot guess. Add a permission allowlist for rspec or bin/rails test and deny reads on your credentials. Connect whichever editor you already use so it sees your Ruby LSP diagnostics. Then give it one contained job, a spec for an untested class, and watch how it runs and reads the result. Add a Skill the first time you catch yourself pasting the same instructions twice. That progression, memory then permissions then Skills then subagents, is the whole adoption curve, and each step is small.
If you want to go the other direction and put an agent inside your product rather than on your keyboard, that is a genuinely different discipline with its own trade-offs around tools, authorization, and cost.
Need help getting Claude Code, or AI more broadly, working across a Rails team without turning your codebase into a liability? I help teams with AI-assisted development workflows, agent architecture, and the guardrails that keep them safe to ship. Reach out at hello at nsinenko.com.
Further Reading
- Rails AI Agents - the hub for building agents into Rails apps: tools, background jobs, approval gates, and cost control
- Building AI Agents in Ruby with the Anthropic SDK - the tutorial for embedding an agent in your own application
- Claude Agent SDK in Ruby - building your own agent on Claude Code's engine, versus using the tool as-is
- Claude Code documentation - the official, current reference for every feature above
- Ruby LSP - the Rails-aware language server whose diagnostics Claude Code reads in your editor