Introduction
When you run rails new, Rails generates a carefully organized directory structure. Understanding where files live and why is essential for navigating any Rails project confidently.
Key Concepts
app/: The heart of your application — models, views, controllers, helpers, mailers, and jobs live here.config/: Configuration files for routes, database, environments, and initializers.db/: Database migrations, schema file, and seed data.Gemfile: Declares all gem dependencies; Bundler uses it to install consistent versions.
Real World Context
Every Rails developer needs to know the directory structure by heart. When a senior developer says "add a concern," you should immediately know to look in app/models/concerns/. When debugging a route, you go to config/routes.rb. This mental map saves time daily.
Deep Dive
The Top-Level Structure
Here is what rails new myapp generates:
myapp/
├── app/ # Application code (models, views, controllers)
├── bin/ # Scripts (rails, rake, setup)
├── config/ # Configuration files
├── db/ # Database files (migrations, schema, seeds)
├── lib/ # Custom library code and tasks
├── log/ # Application log files
├── public/ # Static files served directly by the web server
├── storage/ # Active Storage files (uploads)
├── test/ # Test files
├── tmp/ # Temporary files (cache, pids, sockets)
├── vendor/ # Third-party code
├── Gemfile # Gem dependencies
├── Gemfile.lock # Locked gem versions
└── Rakefile # Rake task loader
Each directory has a specific purpose. Rails expects files in the right place — this is Convention over Configuration in action.
Inside app/
The app/ directory is where you spend most of your time:
app/
├── controllers/ # Handle HTTP requests
├── models/ # Business logic and database access
├── views/ # HTML templates (ERB, etc.)
├── helpers/ # View helper methods
├── mailers/ # Email sending classes
├── jobs/ # Background job classes
├── channels/ # Action Cable (WebSocket) channels
└── assets/ # CSS, JavaScript, images
Rails autoloads everything in app/, so you never need require statements for files here.
Inside config/
The config/ directory controls how your app behaves:
ruby# config/routes.rb — defines all URL routes Rails.application.routes.draw do root "articles#index" resources :articles end
The routes.rb file is one of the most frequently edited configuration files in any Rails app.
Inside db/
The db/ directory manages your database:
db/
├── migrate/ # Migration files (timestamped)
├── schema.rb # Current database schema (auto-generated)
└── seeds.rb # Seed data for development
The schema.rb file is auto-generated from your migrations. Never edit it manually.
Common Pitfalls
- Putting code in the wrong directory: A service object belongs in
app/services/, notlib/. Rails autoloadsapp/subdirectories but notlib/by default. - Editing
schema.rbmanually: This file is auto-generated. Always create a migration instead. - Ignoring
config/environments/: Each environment (development, test, production) has different settings. Understand what each one configures.
Best Practices
- Create custom subdirectories in
app/for service objects (app/services/), form objects (app/forms/), etc. Rails autoloads them automatically. - Keep
config/routes.rborganized by resource. Use comments to separate sections in large apps. - Run
bin/rails notesto find TODO, FIXME, and OPTIMIZE comments across your codebase.
Summary
app/contains all application code: models, views, controllers, helpers, mailers, and jobs.config/holds configuration: routes, database settings, environment configs, and initializers.db/manages database migrations, the auto-generated schema, and seed data.- Rails autoloads everything in
app/, so no manualrequirestatements are needed. - Never edit
schema.rbmanually — always use migrations.
Code Examples
# config/routes.rb — the central routing file
Rails.application.routes.draw do
root "articles#index"
resources :articles
resources :users, only: [:new, :create, :show]
end
# db/seeds.rb — populate development data
Article.create(title: "Welcome", body: "Hello Rails!")
User.create(email: "admin@example.com", password: "secret")