What makes a programming framework good for autonomous AI agents? It's how well the language and APIs communicates intent within the tight constraints that LLMs operate under. Agents need to read code, understand it, and generate more of it, all within a finite context window.

That means three things matter most: token efficiency (how much meaning fits in how few tokens), predictability (how consistently code is structured so the model doesn't have to guess), and ecosystem ergonomics (how pleasant and productive the tooling is for iterative agent workflows).

Ruby on Rails happens to excel at all three.

Ruby on Rails logo

What are AI agents?

Traditional AI usage like running a single ChatGPT prompt is mostly linear and reactive. You ask a question and receive an answer. An autonomous agent is dynamic by contrast. It can break a task into steps, follow them one by one, recover from errors, and adjust their behavior based on new information.

Agents are processes with access to tools and memory to orchestrate complex work. Tools let agents do things beyond the usual text APIs like running code or searching the web. Memory allows agensts to keep track of context across individual steps or even across sessions. Control logic defines how they decide what to do next.

Token efficiency for both humans and robots

When it comes to AI efficiency, most will bring up tokens. But what are tokens anyway? When it comes to large language models (LLMs), tokens are the basic units of text that model either reads or generates. They are processable text chunks that don't always map to words but which these models understand.

A token can be a whole word, part of a word, punctuation, or special characters. A word unbelievable might be split into un, believ, and able. Different models rely on different tokenization rules, so these chunks are not always the same. The process of turning raw text into these chunks is called tokenization.

Context windows

LLMs currently cannot process unlimited amount of text. The amount of data models can process is limited by computer memory which significantly increases with bigger context length. We call the maximum length a model can work with at a given time a context window and we measure it in tokens.

This memory constrain includes the entire prompt, system instructions, conversation history, and the model's response. Less tokens as input enables longer answers and vice versa. When it comes to programming, the usage compounds between source files being read, changes being applied, and diffs being reviewed.

Token windows significantly increased from the GPT-3.5 era of 4096 tokens and are now approching numbers higher than 1 million with models like Opus 4.6 and Sonnet 4.6. But even today the efficiency factor is significant when it comes to entire codebases. We are still spending energy, prolonging response times, and increasing cost.

Ruby token efficiency

Different programming languages means different token efficiency and context windows. This is where Ruby stands out. Martin Alderson, a co-founder of CatchMetrics, made a bit of a fuss lately with a post titled Which programming languages are most token-efficient?. You might not know Martin, but his writing was previously praised by OpenAI co-founder Andrej Karpathy and his posts put him to top 100 most highest-ranking personal blogs on Hacker News for 2025.

In his post, Alderson looks at tasks from the RosettaCode project that have implementation across all 19 most popular mainstream languages. He then ran them through the GPT-4 tokenizer to get the number of tokens required for the solution. The results showed up to 2.6x gap between the most efficient and least efficient languages from the list. That’s pretty significant difference as it's the result between one to almost three files in the context window. Have a look yourself:

Martin Alderson's graph of most token-efficient languages

The updated post also features a mention of the J language which could get efficiency around the half of Closure which dominates the graph. Looks like array languages can be extremely token-efficient. Ruby isn't first on this list, but it is number one when only the major languages are considered.

As the author of Rails, David Heinemeier Hansson (DHH), has said many times "Rails wouldn't exist without Ruby." Ruby was always putting the human experience of writing and reading above other metrics. No, not just the reading part like many others. Writing too. And it's interesting to realize that coming close to English language isn't just helpful for people, but also just as effective for AI.

Language models got increasingly better in working with larger context windows over time, but the numbers of exchanged tokens still play a vital role in their practicality. More tokens means smaller available context, slower responses, and higher cost to run. As DHH puts it:

The answer is (of course) agentic workflows. If you've used coding agents for any length of time, you'll quickly reach a point where you hit the dreaded 'compaction' stage. Compaction is the process most agents use when they reach the limit of the context window. It condenses earlier parts of the conversation - preserving recent context and key artifacts but losing a lot of the detail from earlier in the session.

Rails token efficiency

Of course, Rails is more than Ruby, and lots of people will point out that the framework is large with lots of project generated files and other ceremony. There are not wrong, but is that a problem? Well, a software engineer and researcher Felipe Maciel Ramos Vieira tried to answer this question in his comparative view called SyntaxTax that prioritizes the most context-efficient web stacks

SyntaxTax report of popular web frameworks

Felipe's report implement the same API features in different stacks and compare how many tokens the handwritten code consumes. The Rails framework won at lowest handwritten costs and it was second as lowest total cost. When we talk about big opinionated frameworks it was also first in total efficiency #1
primary handwritten efficiency.

While neither report is detailed and comprehensive enough to be considered scientific there are definitely indicators that being compact, terse, and expressive might as well mean token efficient.

Less ambiguity for the model

Ruby's brevity is a good start but can mean very little without the generated tokens being correct. Modern LLMs can write valid code in almost any language. Agents don't fail at syntax, but they may struggle with ambiguity. They have harder time with architectural decisions. They must ask questions like Where should this logic go?, What pattern should I follow?, and How does this connect to the rest of the system?

Very flexible frameworks leave these questions wide open, and every unanswered question is a chance for the agent to do guess work. Not in Rails. These questions tend to be already answered at the start because of the Rails' Convention over Configuration approach to building applications. An approach that had already lifted a lot of weight from people teams finds its new value in agentic workflows.

Convention over Configuration

Rails conventions act as implicit instructions. If there's a resource :posts route, there's a PostsController controller with CRUD action. If there's a PostsController, there is likely a Post model with user business logic and persistance conveniently provided by Active Record. If there's a has_many relationship, there is a foreign key of a certain name.

Naming things, structure of files, configuration (or lack of). These are building stones that an agent can rely on. Just a simple thing as agreeing on singular vs plural naming saves a lot of overhead. It also makes things consistent. Consistency was good for humans and is equally helpful now with robots.

Configuration

Convention over Configuration means less need for writing configuration files, although default configuration files exist in Rails. And funnily enough, they also follow Convention over Configuration by specifying predefined environment like development or production, and standardizing how Rails components or gems accept configuration.

Rails projects start with many files by default so it seems as if Rails cannot compete with micro frameworks on that minimalism front. However, that's deliberate choice with its own benefits and if we only really cared for the size, we can build a single file application as Greg Molnar shows us:

# frozen_string_literal: true
require 'bundler/inline'

gemfile(true) do
  source 'https://rubygems.org'

  gem 'rails', '~> 7.0.4'
  gem 'sqlite3'
end

require 'rails/all'
database = 'development.sqlite3'

ENV['DATABASE_URL'] = "sqlite3:#{database}"
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: database)
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
  create_table :posts, force: true do |t|
  end

  create_table :comments, force: true do |t|
    t.integer :post_id
  end
end

class App < Rails::Application
  config.root = __dir__
  config.consider_all_requests_local = true
  config.secret_key_base = 'i_am_a_secret'
  config.active_storage.service_configurations = { 'local' => { 'service' => 'Disk', 'root' => './storage' } }

  routes.append do
    root to: 'welcome#index'
  end
end

class WelcomeController < ActionController::Base
  def index
    render inline: 'Hi!'
  end
end

App.initialize!

run App

But this kind of minimalism is only helpful in micro examples. Typical applications will inevitably grow, so this is less of a concern. Generating more files at the beginning shows where we want things to live. It complements all of the other important design choices in Rails.

Effortlessly RESTful

A big part of any web application, be it something user facing or just an API, is its RESTful interface, coming from Representational State Transfer (REST) coined by Roy Fielding in 2000. There is lot of plumbing in representing state and data, and it's something that's practically solved since the Rails early days.

When it comes to implementing Rails applications, you don't need to prompt "Create a RESTful controller with standard CRUD actions, following the naming convention…" — you just say "Add posts to this app", and the agent can fill in the rest correctly, because the conventions tell it everything it needs to know. These conventions fundamentally changes how agents behave. Instead of guessing, they can infer. Instead of generating plausible-but-wrong code, they land on the idiomatic solution from the start.

Less decision-making means better performance. Agents are not creative problem solvers in the human sense; they are pattern completion systems. Every extra decision you force them to make increases the chance of incorrect structure, inconsistent naming, or broken integration. Rails removes entire categories of these decisions this way. The result is more reliable code generation, faster iteration loops, and fewer corrections by humans.

Baked-in features

Rails packs a lot of functionality out of the box. This is by design as the framework has always believed that Convention over Configuration isn't just about file structure, it's about shipping defaults that are genuinely production-ready. Rails is extracted, not designed. There is no committee adding imaginary nice-to-have features. New features are extracted from real world applications.

As an example, I was thinking about using Django early in my career and followed its releases over the years. Perhaps it's not entirely fair given the smaller team and different commercial attention, but Django always missed things Rails already had. Many things were added over time to Django as well, but Rails continues to have opinionated answers to almost anything. Django still largely leaves these as "pick your own adventure."

Rails is a framework of frameworks.

When you create a Rails application, you're not starting with a router and a blank slate. You're getting Active Record, Action Mailer, Active Job, Action Cable, Active Storage, and more. All integrated, all following the same conventions, all tested together, and on top supported by Hotwire and Solid libraries, which expand on these core frameworks even further.

Some of these main frameworks are:

  • Active Record is the database layer. Models map to tables, associations are declared with one line (has_many, belongs_to), and queries are chainable Ruby instead of raw SQL. Migrations version your schema alongside your code.
  • Active Model handles model's validations, serialization, type casting, dirty tracking, and more even without full database-backed Active Record so you can use it in your own plain Ruby objects modeling your domain layer.
  • Action View is the templating layer. ERB templates, partials, layouts, helpers, and form builders. Form helpers handle CSRF tokens, URL generation, and type-appropriate inputs automatically.
  • Action Controller & Action Dispatch are the HTTP layer. Controllers handle requests, dispatch routes them. You declare routes in one file (routes.rb) and they map to controller actions. Action Dispatch also handles middleware and request/response objects.
  • Action Mailer brings email as controllers. Each mailer is a class with methods that correspond to email templates. You call UserMailer.welcome(user).deliver_later and get templated HTML/text email with the same layout system as your views.
  • Active Job is the common interface for background jobs. You define a job class with a perform method, call MyJob.perform_later(args), and Rails queues it. It works with many adapters from Sidekiq, to Solid Queue.
  • Action Cable is the Rails WebSockets integration. Channels work like controllers for persistent connections. You subscribe to channels and broadcast data from anywhere in your application.
  • Action Mailbox routes inbound emails to mailbox classes, similar to how Action Controller routes HTTP requests to controllers. Handles ingestion from Postmark, Sendgrid, Mailgun, or raw SMTP.
  • Active Storage handles upload, storage, URL generation, transformations, and variants of your model's file attachments. It comes with swappable backends and supports direct uploads to S3/GCS/Azure in production.
  • Action Text brings rich text content editing to your application backed by the editor of your choice (defaults to Lexxy). It's an abstraction for your rich text fields that integrates with Active Storage for embedded attachments. This tight integration is unique to Rails.

All and more is available to your agents in a single package. Take just the storage for example. It's not just that you can attach a file to your model, you can immediately use a form helper for that, make it a direct upload, create more efficient variants, use it later in a background job or WYSIWYG editor.

This kind of internal standardization can make complex features feel easy during agentic programming as it's the experience of our Engineering Director Illia Zub when it came to using Rails Engines:

I asked @GitHubCopilot to research the options and propose a plan. Then I kept reviewing it and writing, "Nah, I feel this can be simpler and more consistent." After several rounds of feedback to the plan, it landed on a Rails engine (...) The result is a whole new feature in only a handful of files. It reuses our existing controller directly instead of making a separate API call, which is super nice.

And that's not all.

Solid Trifecta

Another example of this philosophy is the so-called Solid Trifecta stack: Solid Cache, Solid Queue, and Solid Cable. These three libraries let you replace Redis and other external services with your primary database, either with MySQL, PostgreSQL or SQLite.

Solid Trifecta is designed to reduce complexity. Even if they are not technically part of the Rails code directly, Rails ships with all three as defaults in new applications. Most frameworks would tell you to figure out your own message queue and caching layer, but Rails has one.

They all plug into to a higher level APIs like Active Job which still lets you replace them with alternatives at any moment while keeping the same API interface. The new directions around SQLite is especially interesting given how much simplification you can do to your architecture.

Testing

Even the default test suite reflects this philosophy. Minitest, the Rails' default testing framework, supports parallel test execution leveraging multiple cores with a single configuration line. You don't need to reach for a separate gem or configure a CI matrix differently. It's there, it works, and it's fast. The assumption is that your tests should run quickly on the hardware you already have.

On the other hand, lots of applications (including SerpApi) still choose RSpec, mainly for its DSL syntax focused on specifying behavior. Since it's a more popular option, it's also a great alternative for agentic coding. Rails usually gives you something out of the box, but still makes choosing an alternative easy.

The alternatives in Rails are usually limited to a few of options, like here with RSpec being the only common alternative to Minitest, but that's a good thing. Coding agents do better with things that are well used and understood.

Training data

The Rails predictable nature and long list of features are great, but it's the real world use that makes the training possible. Rails is what I call the OG of all modern web frameworks; one that brought MVC and Active Record patterns to the masses, and inspired a whole generation of frameworks since that. Laravel author Taylor Otwell mentioned several times how he got inspired by Rails, namely by Active Record, to create Eloquent ORM.

This kind of history and pedigree helped Rails accumulate a list of famous open source projects with well-structured codebases. Some of the projects you might heard about include Spree (eCommerce), Redmine (project management), GitLab (DevOps), Diaspora (social network), Discourse (forums), Maybe (finance), and Chatwoot (customer support).

But it's not just the grand past. Recently, 37signals specifically made many of its applications source available. Campfire, Writebook, and Fizzy sources are now available for LLM training as well. The Rails foundation even started to publish this kind of applications as a small directory directly on the web as Reference Apps.

The new Rails apps showcase

The Rails documentation, namely the part called Rails Guides, also stands out as a source on how to write Rails code. Since Rails applications tend to look similar, models get better generalizing patterns they can follow for writing new code or refactoring. Rails Guides are also coming through a bit of an update now, with completely new guides being written like the one on Accessibility made by a blind programmer Bruno Prieto.

Ruby also had an agent-friendly documentation long before we heard about AI agents. Apart from RDoc, the usual documentation generator, Ruby has a tool called RI which is a command line interface documentation:

ri Hash             # Document for class Hash.
ri Array#sort       # Document for instance method sort in class Array.
ri read             # Documents for methods ::read and #read in all classes and modules.
ri ruby:dig_methods # Document for page dig_methods.

By passing the --no-pager option, we get results printed directly to the terminal.

Today's agents are powerful enough to fetch even HTML documentation, but it's nice to have this option, especially for completely local development.

World-class libraries

I think that originally lots of people thought that with all LLM training happening in other languages, Ruby could felt behind. That's no longer true. The thing is that Ruby doesn't need to have the best ecosystem for LLM training. Very few companies can afford to train a model from scratch anyways.

On the other hand, every developer wants to take advantage of calling LLM, embedding data, fine tune models, and run their own agents. Using agents to build AI features might look like a scene from Inception, but it's actually our reality. And that's where both Ruby and Rails have a lot to offer today.

The RubyLLM revolution

The things started to look better with an unofficial ruby-openai gem for integrating ChatGPT, today also with an option to use the official openai-ruby library from OpenAI. But a small revolution started with RubyLLM which brought the most beautiful API for Ruby chat interfaces:

chat = RubyLLM.chat model: "gpt-4o-mini"
response = chat.ask "What's the day today?"
response = response.content

A simple chat is just the beginning. RubyLLM's elegance really shows as you reach for more advanced features. Need to analyze an image, a PDF, or an audio file? It's the same interface:

chat.ask "What's in this image?", with: "ruby_conf.jpg"
chat.ask "Summarize this document", with: "contract.pdf"
chat.ask "Describe this meeting", with: "meeting.wav"

Want to stream a response in real time? Pass a block:

chat.ask "Tell me a story about Ruby" do |chunk|
  print chunk.content
end

RubyLLM doesn't stop with an interface to text models. Even image generation, transcribing audio, and embeddings follow the same pattern of radical simplicity:

# Generate images
RubyLLM.paint "a sunset over mountains in watercolor style"

# Transcribe audio to text
RubyLLM.transcribe "meeting.wav"

# Create embedding
RubyLLM.embed "Ruby is elegant and expressive"

These calls will use image, speech-to-text, and embedding models instead, but the DSL is similar.

The problem with simple model calls is that they are not complete systems. They understand intent well, but are not that good at executing arbitrary tasks. That's why early tools like LangChain came with a way to abstract tools which is something that's already offered by all leading LLM companies.

Tools enable models to interact with external tools by outputting JSON objects that specify functions and their arguments. This allows models to retrieve live data, something they otherwise lack as they are trained on existing data.

Imagine you need for a model to help you with Search Engine Optimization (SEO). You can now write a tool to get live Google search results using SerpApi and provide it to your chat context:

class SearchGoogle < RubyLLM::Tool
  description "Search Google and return top organic results for a query"

  params do
    string :query, description: "The search query"
    integer :num, description: "Number of results to return (max 100, default 10)"
  end

  def execute(query:, num: 10)
    client = SerpApi::Client.new(engine: "google", api_key: ENV.fetch("SERPAPI_API_KEY"))
    results = client.search(q: query, num: [num, 100].min)
    client.close

    (results[:organic_results] || []).map do |r|
      { position: r[:position], title: r[:title], url: r[:link], snippet: r[:snippet] }
    end
  end
end

chat = RubyLLM.chat(model: "gpt-4o-mini").with_tools(SearchGoogle)
chat.ask "What are the top 5 Google results for 'ruby on rails'?"

Want a reusable agent with its own personality and tools? Then you can define the above tool-augmented chat as its own agent:

class SeoAgent < RubyLLM::Agent
  model "gpt-4o-mini"

  instructions <<~PROMPT
    You are an SEO research assistant. You help users investigate Google search rankings,
    analyze competitors, and review historical position data for the sites and keywords.
    Use the tools available to you whenever a question requires live search data.
  PROMPT

  tools CheckKeywordPosition, SearchGoogle, FindCompetitors
end

agent = SeoAgent.new
response = agent.ask("What are the top 5 Google results for 'ruby on rails'?")

Now your application has agents with access to internal and external tools to do their best work. The DSL for that is small, compact, and beautiful. Yes, it's just an abstraction really. These agents aren't autonomous agents that writes your code.

Abstractions do matter though. Good abstractions will DRY your code and become the predictable patterns for your code writing agents. If you want to see another example, go read my previous post How to implement AI agents in Rails with RubyLLM.

RubyLLM and Rails

RubyLLM isn't just a Ruby library, it comes with a first-class ActiveRecord integration. A single acts_as_chat declaration gives you a persistent, database-backed chat complete with message history, tool calls, and model switching:

class SeoChat < ApplicationRecord
  acts_as_chat
end

chat = Chat.create! model: "claude-sonnet-4"
chat.ask "What's onpage SEO?"

Even better, we can now update our agent to also have persistence using this model by declaring chat_model:

class SeoAgent < RubyLLM::Agent
  chat_model SeoChat
  model "gpt-4o-mini"
  instructions "You are an SEO research assistant..."
  tools CheckKeywordPosition, SearchGoogle, FindCompetitors
end

Compare all of this to the usual experience where you configure clients, define message schemas, manage request formats and write case statements for provider-specific quirks.

MCPs

Model Context Protocol (MCP) is making AI-assisted Rails development far more practical by giving agents structured access to application context instead of forcing them to rely on massive prompts. Rather than pasting files into a chat window, developers can expose tools that let agents inspect routes, models, database schemas, controllers, jobs, and other parts of an app directly.

The rails-mcp-server project brings this idea to Rails by exposing application internals through MCP-compatible tooling. This allows AI agents such as Claude Code, Cursor, or terminal-based assistants to query the application in a much more targeted and reliable way, similarly as they would do for SerpApi API using the SerpApi MCP.

Another interesting project is rails-ai-context, which focuses on automatically generating structured Rails context for AI tools. Instead of manually curating prompts, the gem helps agents understand relationships between routes, models, Stimulus controllers, validations, and other parts of the stack.

More tools

The RubyLLM and MCP tooling are excellent additions to an already rich ecosystem of gems handling anything from agentic workflows (like
Active Agent) and authorization (like Action Policy), to analytics (like Ahoy) and search (like Searchkick).

Rails also comes with admin panels like Active Admin or Avo

Kamal, the recent addition to the default Rails application Gemfile, is especially interesting given the agents can now use it to deploy your application on your own terms. This simple deploy tool comes with built-in gapless deployments, local Docker registry, or SSL/TLS support. Your agents now only need something like a Digital Ocean or Hetzner token for handling everything.

Rails doesn't always come with many gem alternatives given the relatively smaller community, but the libraries it has are usually of a higher quality and the consolidation of choices once again benefit the training and output of coding agents.

Future

What about the future? Can Rails still keep up? Luckily, it seems that Rails is well positioned for a successful future thanks to its leadership and the Rails Foundation which now heavily invests in the documentation and ecosystem. We are now getting new guides and improvements every day.

37signals, the company DHH co-founded, and the one that's constantly giving back to Rails is on the trajectory to adopt agents in their apps, be it with agent skills or the new CLIs. This adoption is important, because DHH and 37signals still remain the stewardship of Rails. In one of the latest piece Promoting AI agents, DHH shares his optimistic view on the progress in agent coding:

But with these autonomous agents, the experience is very different. It's more like working on a team and less like working with an overly-zealous pair programmer who can't stop stealing the keyboard to complete the code you were in the middle of writing. With a team of agents, they're doing their work autonomously, and I just review the final outcome, offer guidance when asked, and marvel at how this is possible at all.

DHH seems to have a healthy view on AI, continuously adopting new ways of working with it and reevaluating the status quo.

Similarly, the new Rails documentation work aimed directly at AI agents is also another step forward. Better machine-readable documentation combined with MCP tooling gives agents access to authoritative framework knowledge and paves the way for agents to work on the Rails codebase itself.

Fin

It's not that Rails is the winner in every single category we could consider for agentic coding. There are more token-efficient languages than Ruby, they are frameworks that are more popular, more minimal, with a faster runtime, and with project leaders more bullish on AI. But the thing with Rails is that it ranks high enough in all these categories, from token efficiency to high quality guides.

Considering all of that, Rails is the most attractive framework for robots today, just as it once was for humans. At SerpApi we continue to bet on Rails to build the world-class SERP API and we invest in its thriving ecosystem and community. We joined the Rails foundation, we are sponsoring the upcoming Rails World in Austin, and we continue to work on our Open Source projects like SerpTrail and Nokolexbor.

Join us in our effort to make Rails better.