Rails applications reward speed early and punish it later. Convention-over-configuration hides complexity behind magic, and a mature Rails codebase accumulates N+1 queries, tangled callback chains, and ActiveRecord anti-patterns that no linter can trace. VibeRails reads your entire Rails application and finds the framework-specific debt that compounds across models, controllers, and services.
Convention-over-configuration is the organising principle of Rails, and it works remarkably well for small applications. Routes map to controllers, controllers delegate to models, models define the domain. But as an application grows, these conventions start to fight each other. The fat model pattern encourages putting business logic in ActiveRecord classes, which leads to models with hundreds of methods, dozens of callbacks, and validation rules that depend on context the model was never designed to carry.
Service objects were introduced to solve the fat model problem, but without enforced conventions
they create their own category of drift. Some teams put service objects in app/services,
others use app/interactors or app/operations. Some services return
booleans, others return result objects, and a few raise exceptions on failure. Over time, the
service layer becomes as inconsistent as the models it was meant to simplify. A developer
reading an unfamiliar part of the codebase cannot predict how a service object will behave
without reading its entire implementation.
Single Table Inheritance is one of the most persistent sources of hidden complexity in Rails
applications. STI starts as a clean abstraction – a Vehicle base class with
Car, Truck, and Motorcycle subclasses sharing one
database table. But as the subclasses diverge in behaviour and data requirements, the shared
table accumulates nullable columns, the base class fills with conditional logic, and queries
against the base type return unexpected mixtures of subtypes. Migrating away from STI once
the application depends on it is a major undertaking that most teams defer indefinitely.
N+1 queries are the defining performance anti-pattern in Rails applications. ActiveRecord
makes it trivially easy to traverse associations – order.line_items.each
reads naturally, but generates one database query per line item rather than a single query
for all of them. The bullet gem catches some N+1 queries during development, but only on
code paths that are exercised during manual testing. Code paths that are triggered by
background jobs, API endpoints, or admin interfaces often go unexamined.
The fix is includes, preload, or eager_load, but
knowing which to use requires understanding the query strategy. includes lets
Rails decide whether to use a separate query or a LEFT OUTER JOIN. preload
always uses separate queries. eager_load always uses a JOIN. Choosing the
wrong strategy for a deeply nested association can actually worsen performance by loading
a Cartesian product of rows. Teams that apply includes everywhere as a blanket
fix often discover that their memory consumption has increased without understanding why.
Scopes and custom finders add another layer of complexity. A scope that filters by status
might defeat the eager loading that was applied in the controller. A default_scope
that adds an ORDER BY clause can cause unexpected behaviour when composed with
other scopes. These interactions are invisible at the individual file level and only become
apparent when tracing a request from controller through model queries to view rendering.
Rails introduced strong parameters to mitigate mass assignment vulnerabilities, replacing the
older attr_accessible approach. But strong parameters are only as secure as the
developer writing them. A permit! call that allows all parameters, a nested
attributes declaration that permits :id and :_destroy without
access control checks, or a params.merge that reintroduces unpermitted keys
all create the same category of risk. These patterns are scattered across controllers and
are easy to miss during manual code review.
Callback chains remain one of the most dangerous aspects of Rails applications. A
before_save that sets a normalised field, an after_create that
enqueues a background job, and an after_commit that sends a notification can
all fire as the result of a single update! call in a controller. When callbacks
call methods that modify other records, those records trigger their own callbacks, creating
cascading side effects that are nearly impossible to predict from reading any single file.
Concerns compound the problem further. A model that includes five concerns inherits methods
and callbacks from each of them, but the interaction between those concerns is not defined
anywhere. A before_validation callback in one concern might depend on a field
that is set by an after_initialize callback in another. The execution order
depends on the order of include statements, which most developers treat as
arbitrary. These ordering dependencies create subtle bugs that only surface under specific
data conditions.
VibeRails performs a full-codebase scan using frontier large language models. Every Ruby file, ERB and HAML template, migration, initialiser, and configuration file is analysed – not just recent diffs, but the complete application including tests, rake tasks, and gem configuration.
For Rails codebases specifically, the AI reasons about:
includes, and default_scope interactions that produce unexpected query plansfind_by_sql that bypasses the query interface, and callbacks that modify unrelated recordspermit!, nested attributes without access control, parameter merging that bypasses strong parameters, and update_columns calls that skip validations entirelybefore_save, after_commit, and after_create chains that trigger cross-model side effects, concern interactions with implicit ordering dependencies, and callbacks that silently swallow exceptionsEach finding includes the file path, line range, severity, category, and a detailed description explaining why the pattern is problematic and how to address it. Findings are organised into 17 categories so teams can filter and prioritise by area of concern.
Rails applications contain genuine ambiguity that makes automated review challenging. A fat
model might be a deliberate architectural choice for a domain concept that truly belongs in
one place. A callback chain might be the correct approach for maintaining data consistency
in a system where eventual consistency is unacceptable. A default_scope might
be appropriate when soft-delete behaviour must be enforced globally.
VibeRails supports running reviews with two different AI backends – Claude Code and Codex CLI – in sequence. The first pass identifies potential issues, the second verifies them using a different model architecture. When both models independently flag the same N+1 query path or callback cascade, confidence is high. Disagreements highlight areas where the pattern may be intentional, giving your team the context to triage efficiently rather than reviewing every file manually.
After triaging findings, VibeRails can dispatch AI agents to implement fixes directly in your
local repository. For Rails projects, this typically means adding includes or
preload to N+1 query paths, extracting callbacks into explicit service objects,
replacing permit! with specific parameter allowlists, decomposing STI hierarchies
into separate tables, and standardising service object return contracts.
Each fix is generated as a local code change you can inspect, test, and commit or discard. The AI works within the conventions of your existing codebase, matching your application's naming patterns, test framework, and directory structure – whether you use RSpec or Minitest, Slim or ERB, Sidekiq or ActiveJob.
VibeRails runs as a desktop app with a BYOK model – it orchestrates Claude Code or Codex CLI installations you already have. No code is uploaded to VibeRails servers. AI analysis is sent directly to the provider you configured, billed to your existing subscription. Each licence covers one developer: $19/month (cancel anytime) or $299 lifetime. The free tier includes 5 issues per session to evaluate the workflow.
Vertel over je team en doelen. We reageren met een concreet uitrolplan.