<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Raj Kumar</title>
    <description>The latest articles on DEV Community by Raj Kumar (@techie_raj).</description>
    <link>https://dev.to/techie_raj</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3907966%2F21f1d29f-e1be-43b5-bb37-cd07430f828c.png</url>
      <title>DEV Community: Raj Kumar</title>
      <link>https://dev.to/techie_raj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/techie_raj"/>
    <language>en</language>
    <item>
      <title>Your Rails App Is Already AI-Ready. You Just Don't Know It.</title>
      <dc:creator>Raj Kumar</dc:creator>
      <pubDate>Tue, 05 May 2026 12:00:00 +0000</pubDate>
      <link>https://dev.to/techie_raj/your-rails-app-is-already-ai-ready-you-just-dont-know-it-1jc4</link>
      <guid>https://dev.to/techie_raj/your-rails-app-is-already-ai-ready-you-just-dont-know-it-1jc4</guid>
      <description>&lt;p&gt;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. &lt;strong&gt;It's how little I had to do to make it work.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;config/routes.rb&lt;/code&gt;, follows the conventions Rails apps have agreed on for fifteen years, and finds what it needs.&lt;/p&gt;

&lt;p&gt;That disconnect is what this post is about. Not "AI for Rails." &lt;strong&gt;Rails, seen through an LLM's eyes.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The agent is a new hire who reads fast
&lt;/h2&gt;

&lt;p&gt;The mental model that helped me most is this: &lt;em&gt;the agent is a new hire on day one.&lt;/em&gt; 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.&lt;/p&gt;

&lt;p&gt;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 &lt;strong&gt;"looks like every other codebase of this kind."&lt;/strong&gt; That's where Rails wins, and wins in a way that surprised me even after a decade and a half of writing it.&lt;/p&gt;

&lt;p&gt;Here are five things I kept noticing. None of them are features. All of them are conventions.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. &lt;code&gt;app/services/&lt;/code&gt; is a keyword, even to the model
&lt;/h2&gt;

&lt;p&gt;We work in regulated clinical software. Tickets often look like: &lt;em&gt;"Customer reports the nightly audit export for study X came back empty."&lt;/em&gt; An ordinary support engineer would know, without thinking, to start at &lt;code&gt;app/services/exports/&lt;/code&gt;. They don't know because someone documented it. They know because that's where Rails apps put services.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;app/services/&lt;/code&gt; and is often right on the first read.&lt;/p&gt;

&lt;p&gt;Imagine the alternative. Seventeen repositories with seventeen philosophies — hexagonal architecture here, DDD bounded contexts there, &lt;code&gt;lib/&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;Shared conventions compound. Clever per-repo conventions don't.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Active Record scopes are self-documenting in a way no repository pattern is
&lt;/h2&gt;

&lt;p&gt;Here's a scope close in shape to one that lives in our codebase, with domain nouns swapped:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;:visible_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sponsor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;joins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:study&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;studies: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;sponsor: &lt;/span&gt;&lt;span class="n"&gt;sponsor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;archived_at: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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: &lt;em&gt;tenancy rule, association path, lifecycle rule.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Compare to the equivalent in a codebase using a repository-pattern abstraction over an ORM — a &lt;code&gt;StudyRepository.find_visible_to(sponsor)&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;Agents reason the way tired humans do: by pattern-matching against the nearest legible evidence. &lt;strong&gt;Active Record puts the legible evidence at the definition site.&lt;/strong&gt; The agent reads once and knows. No tracing, no inference, no token budget spent on reconstruction.&lt;/p&gt;

&lt;p&gt;This is why, when I watch the agent answer questions about multi-tenant access, it almost never gets it wrong. The tenancy rules are &lt;em&gt;inside the model, at the scope.&lt;/em&gt; It can't miss them.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Background jobs that name themselves
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;AuditTrailReplayJob.perform_later(study_id, since: 24.hours.ago)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Tell me what that does. You told me without reading the class.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Sidekiq, GoodJob, Solid Queue — doesn't matter which — gave us a convention where background work has &lt;strong&gt;a name and a signature that describes itself.&lt;/strong&gt; 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 &lt;code&gt;Job.perform_later&lt;/code&gt; calls with self-describing names, and is done. It doesn't spelunk through an event bus.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Release branches are how Rails ships, and that matters more than you think
&lt;/h2&gt;

&lt;p&gt;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. &lt;strong&gt;It's to resolve which branch the customer is actually running, and to read that branch.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If it reads &lt;code&gt;main&lt;/code&gt;, every downstream conclusion is drifting. "This function handles X" — yes, on &lt;code&gt;main&lt;/code&gt;, not on the release the customer is on.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;main&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;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 worth real money when you're trying to pin an agent to the truth.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. &lt;code&gt;config/routes.rb&lt;/code&gt; is the densest map of application structure I've ever handed to an LLM
&lt;/h2&gt;

&lt;p&gt;When a ticket says &lt;em&gt;"the customer is seeing a 500 on the study-closeout page,"&lt;/em&gt; the agent does this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reads &lt;code&gt;config/routes.rb&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Identifies the controller action.&lt;/li&gt;
&lt;li&gt;Opens the controller.&lt;/li&gt;
&lt;li&gt;Follows to the service.&lt;/li&gt;
&lt;li&gt;Lands on the specific method where the exception is raised.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I've watched this exact chain complete in under a minute on a real ticket, zero guidance from a human. No custom index, no pre-built call graph, no LSP server in the loop. &lt;strong&gt;One routes file, read top to bottom.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That file tells the truth. It's one place. It reads like a table of contents. No custom dispatcher, no decorators rewriting paths at runtime, no middleware hijacking the first matching route. Just &lt;code&gt;resources :studies&lt;/code&gt;, with nested routes for the actions that matter.&lt;/p&gt;

&lt;p&gt;This is the single highest-leverage file in a Rails app for an agent to read. If you've been putting off cleaning up your routes file, now you have a new reason.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I was wrong about
&lt;/h2&gt;

&lt;p&gt;I expected to spend the first month of this project writing &lt;em&gt;helpers&lt;/em&gt; for the agent — summaries, structured manifests, entity graphs, canonicalized module docs. The classic "AI-friendly codebase requires extra scaffolding" thesis you see a lot of right now.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I wrote none of it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The agent reads &lt;code&gt;config/routes.rb&lt;/code&gt;, navigates &lt;code&gt;app/services/&lt;/code&gt;, reads Active Record scopes and follows associations, reads named jobs and understands them. The scaffolding I'd pre-written to help it turned out to be noise the model had to weigh against the code itself. The most useful thing I did was &lt;strong&gt;delete&lt;/strong&gt; the "helpful" entity summaries I'd generated up front and trust the agent to read the code instead.&lt;/p&gt;

&lt;p&gt;If you take one concrete thing from this post, let it be that. &lt;strong&gt;Don't pre-chew the codebase for the model. Let it read what your new-hire Rails engineer reads.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The broader point
&lt;/h2&gt;

&lt;p&gt;There's a common framing that AI-native development will require "AI-friendly" codebases written in some new way. My experience points the other direction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The AI-friendly codebase is the one that was already legible to the tenth engineer who joined the team on day one.&lt;/strong&gt; Rails has been optimizing for that engineer for twenty years. Shared conventions. Declarative syntax. Predictable layout. Boring, obvious names.&lt;/p&gt;

&lt;p&gt;Rails didn't set out to be AI-friendly. It set out to make humans productive by reducing the number of arbitrary decisions they had to make. LLMs turn out to want the exact same thing, for the exact same reason: &lt;strong&gt;both are pattern-matching engines whose cost of understanding drops when the code looks like every other codebase of its kind.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rails optimized for the human version. It gets the LLM version for free.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where this is going
&lt;/h2&gt;

&lt;p&gt;What I've described here is the substrate — the reason any of this was tractable to begin with. The actual system on top of it — what the agent does with this legibility, the invariants it defends, the specific places it still fails, what we had to build around it — is a longer story.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I'm submitting the full writeup as a talk at Rails World 2026.&lt;/strong&gt; There's no public version of the system yet, and there won't be before the talk. If it gets accepted, come find me in Austin. If it doesn't, I'll write it up afterwards.&lt;/p&gt;

&lt;p&gt;In the meantime, if you're experimenting with agents against your own Rails app, I'd genuinely love to hear what you've found — especially the things that broke in ways you didn't expect. That's where the interesting material lives, and it's what doesn't make it into blog posts until someone shares it first.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Raj Kumar is a Toronto-based tech lead. He writes Rails for a living and is currently most interested in the seam between Rails applications and LLM agents.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>ai</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
