Ruby on Rails Guide: Full-Stack Web Development Framework
Ruby on Rails, created by David Heinemeier Hansson in 2004, is the framework that popularized convention over configuration, don’t repeat yourself, and rapid web application development. Rails is built on the philosophy that developers should focus on business logic rather than boilerplate configuration. Two decades of Ruby language design combine with Rails’ opinionated conventions to make it one of the most productive frameworks for building database-backed web applications. Rails powers GitHub, Shopify, Basecamp, and thousands of other successful products, proving that convention-driven development scales.
Convention Over Configuration Philosophy
Rails makes assumptions about how code should be structured, eliminating countless configuration decisions. A model named Article automatically maps to the articles database table. A controller named ArticlesController automatically looks for views in app/views/articles/. Routes follow RESTful conventions — a single resources :articles line generates seven standard routes corresponding to CRUD operations. These conventions reduce boilerplate by an order of magnitude compared to configuration-heavy frameworks.
The conventions extend to every layer of the framework. Test files mirror the application structure — test/models/article_test.rb tests the Article model. Helper modules automatically support their corresponding views. Migrations follow timestamp ordering for sequential application. Asset pipeline conventions determine file inclusion. New team members immediately understand the project structure because it follows Rails conventions.
Rails’ “you are not a beautiful and unique snowflake” mantra encourages following established patterns refined over two decades. The framework’s generators create standardized code that follows community best practices. Deviating from conventions requires explicit configuration, making the framework self-documenting — the structure reveals the architecture.
ActiveRecord ORM
ActiveRecord is Rails’ Object-Relational Mapping layer, implementing the Active Record pattern where models encapsulate both data and behavior. Each model subclass of ApplicationRecord corresponds to a database table. ActiveRecord handles CRUD operations, relationship management, validation, callbacks, and query construction. Models contain business logic, validation rules, and query scopes alongside their data mapping.
Relationships are declared declaratively — belongs_to :author, has_many :comments, has_and_belongs_to_many :categories. ActiveRecord generates appropriate JOINs and handles eager loading through includes to prevent N+1 queries. The where chain builds conditions — Article.where(published: true).where('created_at > ?', 1.week.ago). Scopes encapsulate common queries — scope :published, -> { where(published: true) } — and are chainable with other query methods.
Validations ensure data integrity at the application layer — validates :title, presence: true, uniqueness: { case_sensitive: false }. Validations run before save, returning errors that views display through error messages. Conditional validations use :if and :unless options with symbols, procs, or lambdas. Custom validation methods add complex business logic to the validation lifecycle.
Callbacks hook into the model lifecycle — before_save, after_create, around_update — for cross-cutting concerns like slug generation, cache invalidation, and audit logging. Use callbacks sparingly — excessive callbacks create hidden dependencies that make models harder to test and understand.
Database Migrations
Migrations evolve the database schema over time in a version-controlled manner. rails generate migration AddPublishedToArticles published:boolean generates a migration file with an automatically detected change method. rails db:migrate applies pending migrations sequentially. rails db:rollback reverses the last migration by calling the down method or inverting the change method.
Migrations support creating tables, adding/removing columns, changing column types, adding indices (including expression indexes in PostgreSQL), and executing raw SQL for data migrations. The schema file (db/schema.rb) provides the authoritative database state, generated by inspecting the database rather than from migration files. Never modify existing migrations after they’ve been merged to main — create new migrations for all schema changes.
Rails 8 introduced solid_queue and solid_cache as SQLite-based alternatives to Redis for queue processing and caching, enabling simpler deployment configurations. The solid_cable gem provides a database-backed Action Cable adapter, completing the “solid trilogy” for Redis-free Rails deployments.
Routing and Controllers
Rails routes map HTTP requests to controller actions. resources :articles generates GET, POST, PATCH, DELETE routes with URL helpers. Nested resources express parent-child relationships — resources :articles do resources :comments end. Concern modules share route configurations across resources. Custom member and collection routes extend standard resources with non-CRUD endpoints.
Controllers handle request processing with before_action filters that run before actions in a defined order — before_action :authenticate_user!, before_action :set_article, only: [:show, :edit, :update, :destroy]. Strong parameters protect against mass assignment — params.require(:article).permit(:title, :body, :category_id) — requiring explicit permission for all parameter mutations.
Action Cable for Real-Time Features
Action Cable integrates WebSockets into Rails, enabling real-time features like chat, notifications, and live updates. Channels handle WebSocket connections, streaming messages to subscribed clients. Action Cable runs within the Rails app as an in-process server or as a standalone server with Redis as the pub/sub adapter for multi-server deployments.
Broadcasting from models or controllers pushes updates to connected clients. ArticleChannel.broadcast_to(article, { title: article.title, body: article.body }) updates all clients viewing that article. JavaScript clients subscribe to channels and receive updates declaratively — consumer.subscriptions.create("ArticleChannel", { channel: "ArticlesChannel", id: articleId }).
Testing with RSpec
RSpec provides a behavior-driven development syntax for Rails applications with readable, descriptive test output. Model specs test validations, associations, and scopes with concise matchers. Request specs test HTTP responses, API behavior, and authentication flows by simulating HTTP requests and asserting on response bodies and status codes. System specs with Capybara test browser interactions including JavaScript execution, form submissions, and navigation flows.
Factory Bot generates test data with readable, maintainable factory definitions — FactoryBot.define do factory :article end. Factories support traits for variations — trait :published sets published status attributes. Sequences generate unique values for attributes requiring uniqueness validation.
RSpec’s let and let! define memoized test helpers for clean test setup without instance variables. Expectations use descriptive matchers — expect(response).to have_http_status(:success), expect(article.errors).to be_empty, expect { delete article }.to change(Article, :count).by(-1). Shoulda Matchers provide one-liner validation and association tests that reduce boilerplate.
Asset Pipeline and Modern Frontend
Rails 7+ uses Import Maps to manage JavaScript dependencies without a bundler. JavaScript libraries are loaded as ES modules directly from the app/javascript directory or through CDN pins in config/importmap.rb. This eliminates the Node.js build step for CSS and JavaScript during development, reducing complexity for teams that don’t need advanced JavaScript tooling.
Hotwire (Turbo + Stimulus) provides modern frontend interactivity without writing JavaScript framework code. Turbo Drive accelerates page navigation by fetching HTML and replacing the body. Turbo Frames update page sections independently with lazy loading, pagination, and form submission handling. Turbo Streams broadcast HTML updates over WebSocket for real-time multi-user interfaces. Stimulus adds sprinkles of JavaScript behavior through HTML annotations with controllers, targets, and actions.
Propshaft is the modern asset pipeline replacing Sprockets. It compiles and fingerprints CSS, JavaScript, and image assets for production caching with far-future expiration headers. Propshaft integrates with Import Maps for JavaScript management and includes cssbundling-rails and jsbundling-rails gems that wrap esbuild or Webpack for teams needing advanced CSS and JavaScript bundling.
Background Jobs with Active Job and Solid Queue
Rails’ Active Job framework provides a unified interface for background job processing across multiple backends. Jobs inherit from ApplicationJob, define a perform method, and are queued with MyJob.perform_later(args). The framework handles serialization, error handling, retry logic, and execution across queue workers.
Solid Queue, introduced as the default in Rails 8, provides database-backed job processing without Redis. It supports multiple queues with priority ordering, concurrency controls, recurring tasks scheduled via cron-like syntax, and a built-in dashboard for monitoring queue health. For existing Rails applications using Redis, Sidekiq remains the most popular production job processor with its advanced features like scheduled jobs, batch processing, and real-time monitoring.
Active Job integrates with Action Mailer for sending emails asynchronously. UserMailer.welcome_email(@user).deliver_later queues the email for background delivery.
Action Mailer and Action Text
Action Mailer handles email composition and delivery with template rendering, attachments, and multipart email support. Mailer classes define email methods with instance variables used in corresponding views. Delivery methods include SMTP, SendGrid API, Amazon SES, and Mailgun.
Action Text provides rich text content editing and rendering with Trix editor integration. Rich text content is stored as HTML in a separate rich_text_ association, with attachments managed through Active Storage. Action Text handles content sanitization, attachment embedding, and HTML rendering while maintaining security.
FAQ
Is Rails still relevant in 2025? Yes. Rails continues to power major applications — GitHub, Shopify, Basecamp, and thousands of SaaS companies. Rails 8 introduces first-class authentication, modern JavaScript defaults, and Kamal 2 deployment. Rails’ productivity advantages remain unmatched for database-backed web applications.
What is the difference between Rails and Sinatra? Rails is a full-stack framework with ORM, migrations, templating, and testing conventions. Sinatra is a minimal DSL for simple web applications and APIs. Choose Rails for database applications, Sinatra for microservices and simple APIs.
Does Rails scale? Yes. Shopify handles over a million requests per minute on Rails. GitHub serves billions of page views. Rails scales horizontally with database read replicas, caching layers, queue workers, and load-balanced application servers.
What database does Rails use? PostgreSQL is the recommended production database. Rails also supports MySQL, MariaDB, and SQLite. SQLite is the default for development and testing with Rails 8’s solid trilogy.
How do I learn Rails? Start with the official Getting Started guide, then build a real application. Rails has the most comprehensive documentation of any web framework with extensive tutorials, screencasts, and books.
Learn more about Ruby on Rails with our backend framework comparison and ORM frameworks guide.