网页
综合
更新时间 2026-05-05 12:00:00
Your Rails App Is Already AI-Ready. You Just Don't Know It. - DEV Community
自动蜘蛛
dev.to
正文约 6000 字
Posted on May 5 • Originally published at blog.expedora.com
I've spent the last several months watching a Claude-based agent read a production Rails codebase — opening files, tracing scopes, following background jobs, identifying the right place to investigate a bug I'd written years ago. The strange thing isn't that it works. It's how little I had to do to make it work.
I went in expecting to write a lot of glue. Entity maps. Canonical module summaries. Hand-built manifests telling the model where things live. I wrote none of it. The agent reads config/routes.rb, follows the conventions Rails apps have agreed on for fifteen years, and finds what it needs.
That disconnect is what this post is about. Not "AI for Rails." Rails, seen through an LLM's eyes.
The mental model that helped me most is this: the agent is a new hire on day one. It has never seen your code. It has no institutional memory. But it has general knowledge of Ruby and Rails, the way a new hire does. It reads quickly and forgets nothing — and it believes what it reads.
Under that frame, every decision you've made about naming, layout, and structure is either cheap for the agent to navigate or expensive. And "cheap to navigate" almost always means "looks like every other codebase of this kind." That's where Rails wins, and wins in a way that surprised me even after a decade and a half of writing it.
Here are five things I kept noticing. None of them are features. All of them are conventions.
We work in regulated clinical software. Tickets often look like: "Customer reports the nightly audit export for study X came back empty." An ordinary support engineer would know, without thinking, to start at app/services/exports/. They don't know because someone documented it. They know because that's where Rails apps put services.
The agent knows the same thing, for the same reason. It was trained on millions of Rails apps following the convention. Ask it where the logic for X lives; it opens app/services/ and is often right on the first read.
Imagine the alternative. Seventeen repositories with seventeen philosophies — hexagonal architecture here, DDD bounded contexts there, lib/ somewhere else. All defensible. Taken together, each new ticket forces the agent to spend its first five minutes learning your idiosyncrasies before it can do any work. Those minutes are tokens. Tokens are budget. Budget is what decides whether a per-ticket analysis costs $0.30 or $5.
Shared conventions compound. Clever per-repo conventions don't.
Here's a scope close in shape to one that lives in our codebase, with domain nouns swapped:
One line. It tells you: this model is tenant-scoped to a sponsor, through a study association, and archived studies don't count. The agent reads that scope and has absorbed three things it would otherwise need to discover by tracing across files: tenancy rule, association path, lifecycle rule.
Compare to the equivalent in a codebase using a repository-pattern abstraction over an ORM — a StudyRepository.find_visible_to(sponsor) method that delegates to a query builder that composes predicates defined somewhere else. Same behavior; three files to read; each file requires inference to connect to the next.
Agents reason the way tired humans do: by pattern-matching against the nearest legible evidence. Active Record puts the legible evidence at the definition site. The agent reads once and knows. No tracing, no inference, no token budget spent on reconstruction.
This is why, when I watch the agent answer questions about multi-tenant access, it almost never gets it wrong. The tenancy rules are inside the model, at the scope. It can't miss them.
AuditTrailReplayJob.perform_later(study_id, since: 24.hours.ago)
Tell me what that does. You told me without reading the class.
Now picture the same operation as a publish to a generic queue with a JSON payload and a routing consumer on the other end that dispatches based on an event type string. Same behavior. The agent has to find the producer, follow the payload, find the consumer, match the event type, read the handler. Four files, three inferences, and one place where a typo in the event type silently breaks everything.
Sidekiq, GoodJob, Solid Queue — doesn't matter which — gave us a convention where background work has a name and a signature that describes itself. For an agent trying to trace "what happens after a study closes?", that's an enormous gift. It finds the controller that receives the close action, sees three Job.perform_later calls with self-describing names, and is done. It doesn't spelunk through an event bus.
I've watched this specifically make the difference on root-cause analysis. The agent's reasoning chains are shorter. Shorter chains mean fewer hallucination opportunities. Fewer hallucinations mean I trust the output.
Our platform ships on a quarterly cadence. Customers run a pinned version — let's call it 26.1.0 or 26.2.0. Our agent's first concrete task on any new ticket isn't to analyze the error. It's to resolve which branch the customer is actually running, and to read that branch.
If it reads main, every downstream conclusion is drifting. "This function handles X" — yes, on main, not on the release the customer is on.
This sounds like an ops detail, not a Rails thing. It matters because Rails-shop culture expects release branches. Most of the Rails teams I've worked in or read about branch from main for releases and maintain those branches for the release's lifetime. When you build tooling that assumes this, you're building on ground that already holds the weight.
Not every ecosystem works this way. I've watched teams attempt the same pattern against a polyglot stack where some services cut tags, others branched, others shipped trunk-based with feature flags. Every service had a different answer to "what code is the customer running right now." Each variation was a bug waiting to happen. Rails shops tend to vary less on this axis, and that homogeneity is wort
本页为82搜索生成的网页快照,内容来自站内索引;演示数据的原文链接仅作示例。