Why Ruby Needs Specific AI Coding Rules
Ruby is one of the most expressive languages in mainstream use — there are often five valid ways to accomplish the same thing. This flexibility is Ruby's greatest strength for developers and its biggest liability for AI assistants. Without rules, the AI picks whichever approach it saw most in training data, which may not be your team's preferred idiom.
The most common AI failures in Ruby: mixing procedural and OOP styles inconsistently, ignoring Rails conventions (convention over configuration only works if you follow the conventions), generating raw SQL instead of ActiveRecord scopes, creating fat models instead of service objects, and writing Minitest-style tests when the project uses RSpec.
Ruby's philosophy of 'making programmers happy' means there's no compiler to enforce conventions. Your CLAUDE.md fills that role — it tells the AI which of Ruby's many valid approaches your team prefers.
Rule 1: Follow Rails Conventions Strictly
Rails' power comes from convention over configuration. When the AI ignores conventions — putting logic in the wrong directory, naming things incorrectly, or bypassing the Rails way — you lose the framework's superpowers and get a generic Ruby app with extra dependencies.
The rule: 'Follow Rails conventions strictly. Models in app/models/, controllers in app/controllers/, views in app/views/. Use Rails naming conventions: plural table names, singular model names, snake_case for methods and variables, CamelCase for classes. Use Rails generators when creating new resources. Never bypass ActiveRecord callbacks with raw SQL unless performance requires it.'
For the directory structure: 'Service objects go in app/services/. Form objects in app/forms/. Query objects in app/queries/. Presenters in app/presenters/. Jobs in app/jobs/. Mailers in app/mailers/. Follow the Rails autoloading conventions — Zeitwerk expects the file path to match the class name.'
Rails' power comes from convention over configuration. When the AI ignores conventions — wrong directory, wrong naming — you lose the framework's superpowers. Spell out the directory structure explicitly.
Rule 2: ActiveRecord Best Practices
AI assistants generate ActiveRecord code that works but creates performance problems at scale: N+1 queries, missing indexes, eager loading everything, and fat models with hundreds of lines of business logic.
The rule: 'Use scopes for reusable query conditions. Use includes() or preload() for eager loading to prevent N+1 queries — always verify with Bullet gem in development. Keep models under 200 lines — extract business logic to service objects. Use strong parameters in controllers, never mass-assign without whitelisting. Use database-level constraints (not null, unique indexes) in addition to model validations.'
For queries: 'Use ActiveRecord query methods (where, joins, group) instead of raw SQL. Use pluck() for selecting single columns efficiently. Use find_each() for batch processing instead of all.each. Use exists?() instead of count > 0 for existence checks.'
- Scopes for reusable queries — scope :active, -> { where(active: true) }
- includes() to prevent N+1 — verify with Bullet gem
- Models under 200 lines — extract to service objects
- Strong parameters in controllers — never unfiltered mass assignment
- find_each() for batch processing — not all.each on large tables
- exists?() over count > 0 — more efficient existence checks
Rule 3: Service Objects and Business Logic
Fat models and fat controllers are the two most common Rails anti-patterns AI assistants generate. Business logic ends up in the wrong place — either a 500-line model or a controller method that does everything from validation to email sending.
The rule: 'Extract business logic into service objects in app/services/. Each service has a single public method: call. Services accept explicit parameters (not request objects). Services return a result object or raise a domain-specific exception. Controllers should only: parse params, call a service, render a response. Models should only: define relationships, validations, scopes, and data access.'
For naming: 'Name services as verbs: CreateUser, ProcessPayment, SendInvitation. Use the pattern: UserCreator.new(params).call or UserCreator.call(params) with a class method delegate.'
AI loves putting everything in the model. The rule: controllers parse params + call service + render. Models define relationships + validations + scopes. Business logic goes in app/services/.
Rule 4: Testing with RSpec
AI assistants sometimes generate Minitest tests in RSpec projects (or vice versa) because both are common in the Ruby ecosystem. They also tend to create shallow tests that only verify happy paths.
The rule: 'Use RSpec exclusively. Use describe for the class/method, context for scenarios, it for specific behaviors. Use let() for lazy-loaded test data, before for setup. Use FactoryBot for test data creation — never create records with Model.create! directly in tests. Use shared_examples for common behavior across models.'
For request specs: 'Test API endpoints with request specs (spec/requests/), not controller specs. Test through HTTP — verify status codes, response bodies, and side effects. Use have_http_status matcher. Mock external services with WebMock or VCR.'
- RSpec exclusively — describe/context/it structure
- let() for lazy test data — before blocks for setup
- FactoryBot for test data — never raw Model.create! in specs
- Request specs for APIs — not controller specs (deprecated pattern)
- shared_examples for common behavior across models
- WebMock/VCR for external HTTP — never hit real APIs in tests
Rule 5: Ruby Idioms and Style
Ruby has strong community idioms that AI assistants sometimes violate by applying patterns from other languages. The result reads like 'Java written in Ruby' — technically correct but not idiomatic.
The rule: 'Use Ruby idioms: trailing conditionals (return if invalid?), safe navigation operator (&.), symbol to proc (&:method_name), implicit return (last expression is the return value). Use frozen_string_literal: true pragma. Use guard clauses instead of deep nesting. Prefer single-quoted strings unless interpolation is needed.'
For Rubocop alignment: 'Code must pass Rubocop with the project's .rubocop.yml configuration. Do not disable Rubocop cops with inline comments unless there's a documented reason. Follow the community Ruby style guide for anything not covered by project rules.'
AI trained on Java/Python generates 'foreign' Ruby — explicit returns, deep nesting, verbose conditionals. Ruby idioms: guard clauses, trailing conditionals, implicit return, safe navigation (&.).
Complete Ruby/Rails Rules Template
Consolidated template for Ruby on Rails teams. Covers conventions, ActiveRecord, service objects, RSpec, and idioms.
- Follow Rails conventions strictly — correct directories, naming, autoloading
- ActiveRecord: scopes, includes() for N+1, models under 200 lines
- Service objects for business logic — single call method, explicit params
- Controllers: parse params → call service → render response (nothing else)
- RSpec + FactoryBot — request specs for APIs, shared_examples for common behavior
- Ruby idioms: guard clauses, safe navigation, implicit return, frozen_string_literal
- Rubocop clean — follow project .rubocop.yml, no inline cop disabling
- Background jobs with Sidekiq — never long-running operations in requests