Comparisons

Rails vs Laravel: AI Rules Comparison

Rails and Laravel are both convention-over-configuration MVC frameworks, but their conventions differ. Each needs specific AI rules for model patterns, routing, validation, testing, and the idiomatic way to build features.

7 min read·April 28, 2025

Ruby syntax in a PHP file, RSpec in a PHPUnit project — AI conflates convention-over-configuration siblings

ActiveRecord vs Eloquent, routing, validation, RSpec vs PHPUnit, generators, and rule templates

Convention-Over-Configuration Siblings

Rails (Ruby) and Laravel (PHP) are both MVC frameworks that embrace convention over configuration. Both provide: an ORM (ActiveRecord vs Eloquent), migrations, routing, validation, authentication scaffolding, CLI generators, and a rich ecosystem of packages. Laravel was directly inspired by Rails — many concepts are parallel. But the conventions differ in naming, file organization, syntax, and idiomatic patterns. AI trained on one frequently bleeds patterns into the other.

Without framework rules: AI generates ActiveRecord callbacks in Laravel (Eloquent uses model events, not callbacks with the same API). AI uses RSpec syntax in a Laravel project (Laravel uses PHPUnit). AI generates rails generate commands in a Laravel project (Laravel uses php artisan make). AI writes Ruby string interpolation in PHP files. The languages and frameworks are similar enough conceptually that AI conflates them — producing code in the wrong language or the wrong framework convention.

This article provides: the AI rules needed for each framework, the key convention differences the AI must know, and copy-paste CLAUDE.md templates. The rules tell the AI: this is a Rails project (Ruby, ActiveRecord, RSpec) or this is a Laravel project (PHP, Eloquent, PHPUnit) — preventing every cross-framework and cross-language error.

ORM: ActiveRecord vs Eloquent

Rails ActiveRecord: models inherit from ApplicationRecord. class User < ApplicationRecord; has_many :posts; validates :email, presence: true, uniqueness: true; end. Queries: User.where(role: 'admin').order(created_at: :desc). Associations: has_many, belongs_to, has_one, has_and_belongs_to_many. Callbacks: before_save, after_create, around_destroy. Scopes: scope :active, -> { where(active: true) }. AI rule: 'Rails ORM: ActiveRecord. Models in app/models/. Inherit ApplicationRecord. Associations: has_many/belongs_to. Validations in the model. Queries: Model.where().order().'

Laravel Eloquent: models extend Illuminate\Database\Eloquent\Model. class User extends Model { public function posts() { return $this->hasMany(Post::class); } }. Queries: User::where('role', 'admin')->orderBy('created_at', 'desc')->get(). Relationships: hasMany, belongsTo, hasOne, belongsToMany. Events: creating, updating, deleting. Scopes: public function scopeActive($query) { return $query->where('active', true); }. AI rule: 'Laravel ORM: Eloquent. Models in app/Models/. Extend Model. Relationships as methods. Queries: Model::where()->orderBy()->get().'

The ORM rule prevents: AI generating Ruby syntax (User.where) in a Laravel PHP file, using ActiveRecord callback names (before_save) instead of Eloquent events (creating), or writing scope syntax from the wrong framework. The ORMs are conceptually identical but syntactically different. One rule about which ORM and which syntax prevents every query and model definition error.

  • Rails: User.where(role: 'admin') — Ruby hash syntax, implicit .all
  • Laravel: User::where('role', 'admin')->get() — PHP string args, explicit ->get()
  • Rails: has_many :posts (symbol) vs Laravel: hasMany(Post::class) (class reference)
  • Rails: validates :email, presence: true vs Laravel: separate FormRequest or $rules array
  • AI error: Ruby syntax in PHP files is a syntax error. PHP syntax in Ruby files is a syntax error.
💡 Same Concept, Different Syntax

Rails: User.where(role: 'admin'). Laravel: User::where('role', 'admin')->get(). Conceptually identical (find admin users). Syntactically incompatible (Ruby hash vs PHP string args). One ORM rule tells the AI which syntax to generate — preventing every query error.

Routing: resources vs Route::resource

Rails routing: config/routes.rb. resources :users generates RESTful routes (index, show, new, create, edit, update, destroy). Nested: resources :users do; resources :posts; end. Custom: get '/dashboard', to: 'pages#dashboard'. Namespaces: namespace :admin do; resources :users; end. The routes map to controller#action pairs (UsersController#index). AI rule: 'Rails routes in config/routes.rb. Use resources for RESTful CRUD. Controllers: app/controllers/users_controller.rb with index/show/create/update/destroy actions.'

Laravel routing: routes/web.php (web) and routes/api.php (API). Route::resource('users', UserController::class) generates RESTful routes. Nested: Route::resource('users.posts', PostController::class). Custom: Route::get('/dashboard', [PageController::class, 'dashboard']). Groups: Route::prefix('admin')->group(function() { Route::resource('users', AdminUserController::class); }). AI rule: 'Laravel routes in routes/api.php. Route::resource for CRUD. Controllers: app/Http/Controllers/UserController.php with index/show/store/update/destroy methods.'

The routing rule prevents: AI writing resources :users (Ruby) in routes/api.php (PHP), using controller#action syntax (Rails) instead of [Controller::class, 'method'] (Laravel), or creating routes.rb in a Laravel project. The routing convention defines how every URL maps to code — one rule aligns every route definition.

Validation and Testing Conventions

Rails validation: in the model. class User < ApplicationRecord; validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }; end. Validation runs on save. Errors: user.errors.full_messages. Testing: RSpec (describe, it, expect, before, let). Test files: spec/ directory. Factories: FactoryBot. AI rule: 'Rails: model validations, RSpec for testing (spec/ directory), FactoryBot for test data. Test command: bundle exec rspec.'

Laravel validation: in FormRequest classes or inline. class StoreUserRequest extends FormRequest { public function rules() { return ['email' => 'required|email|unique:users']; } }. Validation runs before the controller action. Errors: $errors->all(). Testing: PHPUnit (test methods, $this->assert*, setUp). Test files: tests/ directory. Factories: Laravel model factories. AI rule: 'Laravel: FormRequest for validation, PHPUnit for testing (tests/ directory), model factories for test data. Test command: php artisan test.'

The testing rule is critical: AI generating RSpec syntax (describe, it, expect) in a Laravel project produces undefined method errors. AI generating PHPUnit assertions ($this->assertEquals) in a Rails project fails. The testing framework determines: file naming, directory structure, assertion syntax, setup/teardown methods, and mocking APIs. One testing rule prevents every test file syntax error.

  • Rails: RSpec (describe/it/expect), spec/ directory, FactoryBot, bundle exec rspec
  • Laravel: PHPUnit ($this->assert*, test methods), tests/ directory, model factories, php artisan test
  • Rails validation: in the model (validates :field, rule). Laravel: FormRequest class or inline rules
  • Rails generators: rails generate model/controller. Laravel: php artisan make:model/controller
  • AI error: RSpec in Laravel = undefined. PHPUnit in Rails = wrong testing framework entirely
⚠️ RSpec in Laravel = Undefined Everything

RSpec: describe UserController do; it 'returns users' do; expect(response).to have_http_status(:ok); end; end. PHPUnit: public function test_returns_users() { $this->getJson('/users')->assertStatus(200); }. Same test, different everything. Wrong testing framework = every test file is broken.

CLI Generators and Scaffolding

Rails generators: rails generate model User email:string name:string (creates model, migration, test). rails generate controller Users index show (creates controller with actions, views, routes). rails generate scaffold User email:string (creates model, migration, controller, views, routes, tests — full CRUD). The generators are central to Rails development — most Rails features start with a generate command. AI rule: 'Use rails generate for scaffolding. Generate model for model+migration. Generate controller for controller+views. Generate scaffold for full CRUD.'

Laravel generators: php artisan make:model User -mfc (creates model, migration, factory, controller). php artisan make:controller UserController --resource (creates RESTful controller). php artisan make:request StoreUserRequest (creates form request validation). Laravel's artisan commands are similar to Rails generators but with different flags and naming. AI rule: 'Use php artisan make:* for scaffolding. make:model -mfc for model+migration+factory+controller. make:controller --resource for CRUD controller.'

The generator rule prevents: AI suggesting rails generate in a Laravel project (command does not exist), using artisan flags in Rails commands, or generating the wrong set of files. The generators create the initial file structure — wrong generator = wrong files. One rule about which CLI tool and which flags aligns every scaffolding operation.

Ready-to-Use Rule Templates

Rails CLAUDE.md template: '# Framework (Ruby on Rails). Language: Ruby. ORM: ActiveRecord (app/models/). Routes: config/routes.rb with resources. Controllers: app/controllers/ with index/show/new/create/edit/update/destroy. Validations: in models (validates :field, rules). Testing: RSpec (spec/ directory), FactoryBot for fixtures. Generators: rails generate model/controller/scaffold. CLI: bundle exec rails, bundle exec rspec. Migrations: rails db:migrate. Never PHP, never Laravel patterns, never PHPUnit.'

Laravel CLAUDE.md template: '# Framework (Laravel). Language: PHP. ORM: Eloquent (app/Models/). Routes: routes/api.php with Route::resource. Controllers: app/Http/Controllers/ with index/show/store/update/destroy. Validation: FormRequest classes (app/Http/Requests/). Testing: PHPUnit (tests/ directory), model factories. Generators: php artisan make:model/controller/request. CLI: php artisan, php artisan test. Migrations: php artisan migrate. Never Ruby, never Rails patterns, never RSpec.'

The language rule is the most fundamental: Rails = Ruby, Laravel = PHP. AI mixing languages produces immediate syntax errors. But the framework rule is more subtle: both are MVC, both have ORMs, both have generators — the AI must know which specific conventions to follow. The template handles both: language boundary (Ruby vs PHP) and framework boundary (ActiveRecord vs Eloquent, RSpec vs PHPUnit).

ℹ️ Language Boundary Is Rule #1

Rails = Ruby. Laravel = PHP. AI mixing languages produces immediate syntax errors (Ruby string interpolation in PHP, PHP arrow operators in Ruby). The language rule is the most fundamental — it prevents errors before the framework conventions even matter.

Comparison Summary

Summary of Rails vs Laravel AI rules.

  • Language: Rails = Ruby vs Laravel = PHP — the most fundamental rule (syntax errors if mixed)
  • ORM: ActiveRecord (User.where) vs Eloquent (User::where()->get()) — similar concepts, different syntax
  • Routes: resources :users (Ruby) vs Route::resource('users', Controller::class) (PHP)
  • Validation: Rails model validations vs Laravel FormRequest classes
  • Testing: RSpec (describe/it/expect) vs PHPUnit ($this->assert*, test methods)
  • Generators: rails generate vs php artisan make: — different CLI, different flags
  • Both: convention-over-configuration MVC — the conventions are the critical rules
  • Templates: language + framework boundary rules prevent all cross-contamination