rails

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Ruby on Rails Guide

Ruby on Rails 指南

Applies to: Rails 7+, Ruby 3.2+, Hotwire/Turbo, ActiveRecord, Action Cable
适用范围:Rails 7+, Ruby 3.2+, Hotwire/Turbo, ActiveRecord, Action Cable

Core Principles

核心原则

  1. Convention over Configuration: Follow Rails conventions -- naming, directory structure, RESTful routes
  2. Fat Models, Thin Controllers: Business logic in models and service objects, controllers handle request/response only
  3. DRY: Use concerns, partials, helpers, and shared services to avoid repetition
  4. RESTful by Default: Design resources around standard CRUD actions before adding custom routes
  5. Security by Default: CSRF, XSS protection, strong parameters, and parameterized queries are built in
  1. 约定优于配置:遵循Rails约定——命名规范、目录结构、RESTful路由
  2. 胖模型,瘦控制器:业务逻辑放在模型和服务对象中,控制器仅负责处理请求/响应
  3. DRY:使用concerns、partials、helpers和共享服务避免重复代码
  4. 默认遵循RESTful:在添加自定义路由前,围绕标准CRUD操作设计资源
  5. 默认安全:内置CSRF、XSS防护、强参数和参数化查询能力

When to Use Rails

何时使用Rails

Good fit: Full-stack web apps, MVPs, CMS, e-commerce, SaaS, Hotwire/Turbo SPA-like UX, API backends.
Consider alternatives: Minimal microservices (Sinatra, Hanami), heavy real-time streaming, strict type safety needs (Rust, Go).
适用场景:全栈Web应用、MVP、CMS、电商平台、SaaS、Hotwire/Turbo实现类SPA用户体验、API后端。
可考虑替代方案:极简微服务(Sinatra、Hanami)、重度实时流场景、严格类型安全需求(Rust、Go)。

Project Structure

项目结构

myapp/
├── app/
│   ├── controllers/
│   │   ├── application_controller.rb
│   │   ├── concerns/              # Controller concerns (auth, pagination)
│   │   └── api/
│   │       └── v1/                # Versioned API controllers
│   ├── models/
│   │   ├── application_record.rb
│   │   └── concerns/              # Model concerns (searchable, sluggable)
│   ├── views/
│   │   ├── layouts/               # Application layouts
│   │   │   └── application.html.erb
│   │   ├── shared/                # Shared partials (_navbar, _footer)
│   │   └── posts/                 # Resource-specific views
│   ├── helpers/                   # View helpers
│   ├── jobs/                      # ActiveJob classes
│   ├── mailers/                   # Action Mailer classes
│   ├── channels/                  # Action Cable channels
│   └── services/                  # Service objects (custom directory)
├── config/
│   ├── routes.rb                  # Route definitions
│   ├── database.yml               # Database configuration
│   ├── environments/              # Per-environment settings
│   └── initializers/              # Boot-time configuration
├── db/
│   ├── migrate/                   # Migration files
│   ├── schema.rb                  # Current schema snapshot
│   └── seeds.rb                   # Seed data
├── lib/
│   └── tasks/                     # Custom Rake tasks
├── test/                          # Minitest (default) or spec/ for RSpec
│   ├── models/
│   ├── controllers/
│   ├── integration/
│   └── system/
├── Gemfile
└── Gemfile.lock
  • Place business logic in
    app/services/
    ; use concerns for shared model/controller behavior
  • Namespace API controllers under
    Api::V1
    ; keep shared partials in
    views/shared/
myapp/
├── app/
│   ├── controllers/
│   │   ├── application_controller.rb
│   │   ├── concerns/              # Controller concerns (auth, pagination)
│   │   └── api/
│   │       └── v1/                # Versioned API controllers
│   ├── models/
│   │   ├── application_record.rb
│   │   └── concerns/              # Model concerns (searchable, sluggable)
│   ├── views/
│   │   ├── layouts/               # Application layouts
│   │   │   └── application.html.erb
│   │   ├── shared/                # Shared partials (_navbar, _footer)
│   │   └── posts/                 # Resource-specific views
│   ├── helpers/                   # View helpers
│   ├── jobs/                      # ActiveJob classes
│   ├── mailers/                   # Action Mailer classes
│   ├── channels/                  # Action Cable channels
│   └── services/                  # Service objects (custom directory)
├── config/
│   ├── routes.rb                  # Route definitions
│   ├── database.yml               # Database configuration
│   ├── environments/              # Per-environment settings
│   └── initializers/              # Boot-time configuration
├── db/
│   ├── migrate/                   # Migration files
│   ├── schema.rb                  # Current schema snapshot
│   └── seeds.rb                   # Seed data
├── lib/
│   └── tasks/                     # Custom Rake tasks
├── test/                          # Minitest (default) or spec/ for RSpec
│   ├── models/
│   ├── controllers/
│   ├── integration/
│   └── system/
├── Gemfile
└── Gemfile.lock
  • 业务逻辑放在
    app/services/
    目录下;共享的模型/控制器逻辑使用concerns实现
  • API控制器放在
    Api::V1
    命名空间下;共享partial放在
    views/shared/
    目录下

Guardrails

开发规范

Controllers

控制器

  • Keep controllers under 100 lines total
  • Limit each action to 10 lines (delegate to services for complex logic)
  • Always use
    before_action
    for authentication and resource loading
  • Always use strong parameters via private
    *_params
    methods
  • Return
    status: :unprocessable_entity
    for failed form submissions
  • Use
    status: :see_other
    (303) for
    redirect_to
    after DELETE
  • Use
    respond_to
    blocks when serving multiple formats
  • 控制器总代码行数控制在100行以内
  • 每个action代码行数限制在10行以内(复杂逻辑委托给服务对象处理)
  • 认证和资源加载统一使用
    before_action
    实现
  • 始终通过私有
    *_params
    方法使用强参数
  • 表单提交失败时返回
    status: :unprocessable_entity
  • DELETE操作后的
    redirect_to
    使用
    status: :see_other
    (303)
  • 支持多种返回格式时使用
    respond_to
    代码块

Models

模型

  • Always add validations for required fields and constraints
  • Always add
    dependent:
    option on
    has_many
    /
    has_one
    associations
  • Use
    scope
    for reusable queries (never build queries in controllers)
  • Use
    enum
    with explicit integer or string mappings
  • Use
    before_validation
    for data normalization (downcase, strip)
  • Avoid heavy logic in callbacks -- prefer service objects for side effects
  • Always define
    counter_cache: true
    for belongs_to when parent displays counts
  • 始终为必填字段和约束添加校验规则
  • has_many
    /
    has_one
    关联始终添加
    dependent:
    选项
  • 可复用查询使用
    scope
    实现(禁止在控制器中拼接查询)
  • enum
    使用显式的整数或字符串映射
  • 数据标准化(转小写、去空格)使用
    before_validation
    实现
  • 回调中避免放置复杂逻辑,副作用处理优先使用服务对象
  • 父模型需要显示关联计数时,
    belongs_to
    始终定义
    counter_cache: true

Migrations

迁移

  • Always add
    null: false
    for required columns
  • Always add database indexes for foreign keys and frequently queried columns
  • Always add unique indexes where uniqueness is required
  • Use
    references
    with
    foreign_key: true
    for associations
  • Set sensible defaults with
    default:
    for boolean and status columns
  • Include both
    up
    and
    down
    methods for irreversible migrations
  • Never modify a migration after it has been applied to production
  • 必填列始终添加
    null: false
    约束
  • 外键和高频查询列始终添加数据库索引
  • 需要唯一性约束的字段始终添加唯一索引
  • 关联使用带
    foreign_key: true
    references
    定义
  • 布尔和状态列设置合理的
    default:
    默认值
  • 不可逆的迁移需要同时定义
    up
    down
    方法
  • 迁移已应用到生产环境后禁止修改

Security

安全

  • Strong parameters: never use
    params.permit!
    (permit all)
  • CSRF protection: enabled by default, skip only for API controllers with token auth
  • SQL injection: use ActiveRecord query methods, never string interpolation in queries
  • XSS: ERB auto-escapes by default; never use
    raw
    or
    html_safe
    with user data
  • Mass assignment: only permit explicitly needed attributes
  • Secrets: use
    Rails.application.credentials
    or environment variables
  • Set
    force_ssl
    in production
  • Use
    content_security_policy
    configuration in production
  • 强参数:禁止使用
    params.permit!
    (允许所有参数)
  • CSRF防护:默认开启,仅使用token认证的API控制器可跳过
  • SQL注入:使用ActiveRecord查询方法,禁止在查询中使用字符串插值
  • XSS防护:ERB默认自动转义,用户输入内容禁止使用
    raw
    html_safe
  • 批量赋值:仅显式允许需要的属性
  • 密钥:使用
    Rails.application.credentials
    或环境变量存储
  • 生产环境开启
    force_ssl
  • 生产环境配置
    content_security_policy

Performance

性能

  • Always use
    includes
    (or
    preload
    /
    eager_load
    ) to prevent N+1 queries
  • Add database indexes for all foreign keys and commonly filtered columns
  • Use
    counter_cache
    for association counts displayed in views
  • Use pagination for all list endpoints (Kaminari or Pagy)
  • Use fragment caching (
    cache @record do
    ) for expensive view rendering
  • Use background jobs (ActiveJob + Sidekiq) for slow operations
  • Use
    find_each
    instead of
    each
    when iterating over large datasets
  • 始终使用
    includes
    (或
    preload
    /
    eager_load
    )避免N+1查询
  • 所有外键和常用过滤列添加数据库索引
  • 视图中展示的关联计数使用
    counter_cache
  • 所有列表接口使用分页(Kaminari或Pagy)
  • 耗时的视图渲染使用片段缓存(
    cache @record do
  • 慢操作使用后台任务(ActiveJob + Sidekiq)处理
  • 遍历大量数据集时使用
    find_each
    替代
    each

MVC Conventions

MVC约定

Models

模型

ruby
undefined
ruby
undefined

app/models/post.rb

app/models/post.rb

class Post < ApplicationRecord

1. Associations

belongs_to :user belongs_to :category, optional: true has_many :comments, dependent: :destroy has_many :taggings, dependent: :destroy has_many :tags, through: :taggings has_one_attached :featured_image

2. Validations

validates :title, presence: true, length: { maximum: 255 } validates :body, presence: true

3. Enums

enum :status, { draft: 0, published: 1, archived: 2 }

4. Scopes

scope :published, -> { where(status: :published) } scope :recent, -> { order(created_at: :desc) } scope :by_category, ->(cat) { where(category: cat) } scope :search, ->(q) { where("title ILIKE :q OR body ILIKE :q", q: "%#{sanitize_sql_like(q)}%") }

5. Callbacks (keep minimal)

before_validation :normalize_title

6. Instance methods

def publish! update!(status: :published, published_at: Time.current) end
private
def normalize_title self.title = title&.strip end end

**Model ordering convention**: Associations, validations, enums, scopes, callbacks, class methods, instance methods, private methods.
class Post < ApplicationRecord

1. Associations

belongs_to :user belongs_to :category, optional: true has_many :comments, dependent: :destroy has_many :taggings, dependent: :destroy has_many :tags, through: :taggings has_one_attached :featured_image

2. Validations

validates :title, presence: true, length: { maximum: 255 } validates :body, presence: true

3. Enums

enum :status, { draft: 0, published: 1, archived: 2 }

4. Scopes

scope :published, -> { where(status: :published) } scope :recent, -> { order(created_at: :desc) } scope :by_category, ->(cat) { where(category: cat) } scope :search, ->(q) { where("title ILIKE :q OR body ILIKE :q", q: "%#{sanitize_sql_like(q)}%") }

5. Callbacks (keep minimal)

before_validation :normalize_title

6. Instance methods

def publish! update!(status: :published, published_at: Time.current) end
private
def normalize_title self.title = title&.strip end end

**模型代码排序约定**:关联定义、校验规则、枚举、scope、回调、类方法、实例方法、私有方法。

Controllers

控制器

ruby
undefined
ruby
undefined

app/controllers/posts_controller.rb

app/controllers/posts_controller.rb

class PostsController < ApplicationController before_action :authenticate_user!, except: [:index, :show] before_action :set_post, only: [:show, :edit, :update, :destroy] before_action :authorize_post!, only: [:edit, :update, :destroy]
def index @posts = Post.published .includes(:user, :category) .recent .page(params[:page]) .per(20) end
def show; end
def new @post = current_user.posts.build end
def create @post = current_user.posts.build(post_params) if @post.save redirect_to @post, notice: "Post created." else render :new, status: :unprocessable_entity end end
def edit; end
def update if @post.update(post_params) redirect_to @post, notice: "Post updated." else render :edit, status: :unprocessable_entity end end
def destroy @post.destroy redirect_to posts_url, notice: "Post deleted.", status: :see_other end
private
def set_post @post = Post.find(params[:id]) end
def authorize_post! redirect_to posts_url, alert: "Not authorized." unless @post.user == current_user end
def post_params params.require(:post).permit(:title, :body, :category_id, :status, :featured_image, tag_ids: []) end end
undefined
class PostsController < ApplicationController before_action :authenticate_user!, except: [:index, :show] before_action :set_post, only: [:show, :edit, :update, :destroy] before_action :authorize_post!, only: [:edit, :update, :destroy]
def index @posts = Post.published .includes(:user, :category) .recent .page(params[:page]) .per(20) end
def show; end
def new @post = current_user.posts.build end
def create @post = current_user.posts.build(post_params) if @post.save redirect_to @post, notice: "Post created." else render :new, status: :unprocessable_entity end end
def edit; end
def update if @post.update(post_params) redirect_to @post, notice: "Post updated." else render :edit, status: :unprocessable_entity end end
def destroy @post.destroy redirect_to posts_url, notice: "Post deleted.", status: :see_other end
private
def set_post @post = Post.find(params[:id]) end
def authorize_post! redirect_to posts_url, alert: "Not authorized." unless @post.user == current_user end
def post_params params.require(:post).permit(:title, :body, :category_id, :status, :featured_image, tag_ids: []) end end
undefined

Views and Partials

视图和Partial

  • Use
    _form.html.erb
    partial shared between
    new
    and
    edit
  • Use collection rendering:
    render @posts
    (auto-maps to
    _post.html.erb
    )
  • Use locals:
    render partial: "post", locals: { post: @post }
  • Use
    content_for :title
    for page-specific titles
  • Use helpers for complex view logic (not inline ERB conditionals)
  • new
    edit
    页面共享
    _form.html.erb
    partial
  • 使用集合渲染:
    render @posts
    (自动映射到
    _post.html.erb
  • 使用locals传参:
    render partial: "post", locals: { post: @post }
  • 页面专属标题使用
    content_for :title
    定义
  • 复杂视图逻辑使用helper实现(不要在ERB中写内联条件判断)

Routing

路由

ruby
undefined
ruby
undefined

config/routes.rb

config/routes.rb

Rails.application.routes.draw do root "home#index"

Authentication

get "login", to: "sessions#new" post "login", to: "sessions#create" delete "logout", to: "sessions#destroy"

RESTful resources

resources :posts do resources :comments, only: [:create, :destroy] member do post :publish post :unpublish end end
resources :categories, only: [:index, :show] do resources :posts, only: [:index] end

Admin namespace

namespace :admin do root "dashboard#index" resources :users resources :posts end

API namespace

namespace :api do namespace :v1 do resources :posts, only: [:index, :show, :create, :update, :destroy] end end

Health check

get "health", to: "health#show" end

**Route conventions:**
- Use `resources` for standard CRUD (generates 7 RESTful routes)
- Use `only:` or `except:` to limit generated routes
- Use `member` for actions on a specific record, `collection` for actions on the set
- Nest resources only one level deep; use `shallow: true` for deeper nesting
- Namespace admin and API routes separately
Rails.application.routes.draw do root "home#index"

Authentication

get "login", to: "sessions#new" post "login", to: "sessions#create" delete "logout", to: "sessions#destroy"

RESTful resources

resources :posts do resources :comments, only: [:create, :destroy] member do post :publish post :unpublish end end
resources :categories, only: [:index, :show] do resources :posts, only: [:index] end

Admin namespace

namespace :admin do root "dashboard#index" resources :users resources :posts end

API namespace

namespace :api do namespace :v1 do resources :posts, only: [:index, :show, :create, :update, :destroy] end end

Health check

get "health", to: "health#show" end

**路由约定**:
- 标准CRUD使用`resources`定义(自动生成7个RESTful路由)
- 使用`only:`或`except:`限制生成的路由范围
- 单条记录的操作使用`member`定义,集合级别的操作使用`collection`定义
- 资源嵌套最多一层,更深层级嵌套使用`shallow: true`
- 后台和API路由使用独立的命名空间

Migrations

迁移

ruby
undefined
ruby
undefined

db/migrate/20240115000000_create_posts.rb

db/migrate/20240115000000_create_posts.rb

class CreatePosts < ActiveRecord::Migration[7.1] def change create_table :posts do |t| t.references :user, null: false, foreign_key: true t.references :category, foreign_key: true t.string :title, null: false t.text :body, null: false t.integer :status, default: 0, null: false t.datetime :published_at t.integer :comments_count, default: 0, null: false
  t.timestamps
end

add_index :posts, [:user_id, :status]
add_index :posts, :published_at
end end

- Use `t.references` for foreign keys (adds index automatically)
- Use `t.timestamps` for `created_at` and `updated_at`
- Add composite indexes for common query patterns
- Use `comments_count` with `counter_cache: true` on the association
class CreatePosts < ActiveRecord::Migration[7.1] def change create_table :posts do |t| t.references :user, null: false, foreign_key: true t.references :category, foreign_key: true t.string :title, null: false t.text :body, null: false t.integer :status, default: 0, null: false t.datetime :published_at t.integer :comments_count, default: 0, null: false
  t.timestamps
end

add_index :posts, [:user_id, :status]
add_index :posts, :published_at
end end

- 外键使用`t.references`定义(自动添加索引)
- 使用`t.timestamps`自动生成`created_at`和`updated_at`字段
- 常用查询模式添加联合索引
- `comments_count`配合关联上的`counter_cache: true`使用

Service Objects

服务对象

ruby
undefined
ruby
undefined

app/services/application_service.rb

app/services/application_service.rb

class ApplicationService def self.call(...) new(...).call end end
class ApplicationService def self.call(...) new(...).call end end

app/services/posts/publish_service.rb

app/services/posts/publish_service.rb

module Posts class PublishService < ApplicationService def initialize(post:, user:) @post = post @user = user end
def call
  return ServiceResult.failure(["Not authorized"]) unless @user == @post.user

  ActiveRecord::Base.transaction do
    @post.update!(status: :published, published_at: Time.current)
    notify_subscribers
  end

  ServiceResult.success(@post)
rescue ActiveRecord::RecordInvalid => e
  ServiceResult.failure(e.record.errors.full_messages)
end

private

def notify_subscribers
  PostMailer.published(@post).deliver_later
end
end end

- Use `self.call(...)` class method pattern for clean invocation
- Wrap multi-step operations in `ActiveRecord::Base.transaction`
- Return a result object (success/failure) instead of raising
- Namespace services by domain: `Posts::PublishService`, `Users::CreateService`
module Posts class PublishService < ApplicationService def initialize(post:, user:) @post = post @user = user end
def call
  return ServiceResult.failure(["Not authorized"]) unless @user == @post.user

  ActiveRecord::Base.transaction do
    @post.update!(status: :published, published_at: Time.current)
    notify_subscribers
  end

  ServiceResult.success(@post)
rescue ActiveRecord::RecordInvalid => e
  ServiceResult.failure(e.record.errors.full_messages)
end

private

def notify_subscribers
  PostMailer.published(@post).deliver_later
end
end end

- 使用`self.call(...)`类方法模式实现简洁调用
- 多步操作包裹在`ActiveRecord::Base.transaction`中
- 返回结果对象(成功/失败)而非抛出异常
- 服务按领域划分命名空间:`Posts::PublishService`、`Users::CreateService`

Rails Commands

Rails命令

bash
undefined
bash
undefined

Application

Application

rails new myapp --database=postgresql --css=tailwind rails new myapp --api # API-only mode rails server # Start dev server rails console # Interactive console rails routes # List all routes
rails new myapp --database=postgresql --css=tailwind rails new myapp --api # API-only mode rails server # Start dev server rails console # Interactive console rails routes # List all routes

Generators

Generators

rails generate model User name:string email:string rails generate controller Posts index show new create rails generate scaffold Article title:string body:text rails generate migration AddStatusToPosts status:integer
rails generate model User name:string email:string rails generate controller Posts index show new create rails generate scaffold Article title:string body:text rails generate migration AddStatusToPosts status:integer

Database

Database

rails db:create # Create database rails db:migrate # Run pending migrations rails db:rollback # Undo last migration rails db:seed # Run seeds.rb rails db:reset # Drop, create, migrate, seed
rails db:create # Create database rails db:migrate # Run pending migrations rails db:rollback # Undo last migration rails db:seed # Run seeds.rb rails db:reset # Drop, create, migrate, seed

Testing

Testing

rails test # Run all tests rails test:models # Model tests only rails test:system # System tests only
rails test # Run all tests rails test:models # Model tests only rails test:system # System tests only

Assets and dependencies

Assets and dependencies

bundle install # Install gems rails assets:precompile # Compile assets for production
undefined
bundle install # Install gems rails assets:precompile # Compile assets for production
undefined

Testing

测试

Minitest (Default)

Minitest (默认)

ruby
undefined
ruby
undefined

test/models/post_test.rb

test/models/post_test.rb

require "test_helper"
class PostTest < ActiveSupport::TestCase def setup @post = posts(:first_post) end
test "valid post" do assert @post.valid? end
test "invalid without title" do @post.title = nil assert_not @post.valid? assert_includes @post.errors[:title], "can't be blank" end
test "publish! sets status and timestamp" do @post.publish! assert @post.published? assert_not_nil @post.published_at end end
undefined
require "test_helper"
class PostTest < ActiveSupport::TestCase def setup @post = posts(:first_post) end
test "valid post" do assert @post.valid? end
test "invalid without title" do @post.title = nil assert_not @post.valid? assert_includes @post.errors[:title], "can't be blank" end
test "publish! sets status and timestamp" do @post.publish! assert @post.published? assert_not_nil @post.published_at end end
undefined

Testing Standards

测试标准

  • Use fixtures (default) or
    factory_bot
    for test data
  • Test validations, associations, scopes, and instance methods on models
  • Test authentication, authorization, and response codes on controllers
  • Use system tests (Capybara) for critical user flows
  • Coverage target: >80% for models and services, >60% overall
  • Test names describe behavior:
    test "user cannot edit others' posts"
  • See references/patterns.md for controller and system test examples
  • 使用fixtures(默认)或
    factory_bot
    生成测试数据
  • 模型测试覆盖校验规则、关联、scope和实例方法
  • 控制器测试覆盖认证、授权和响应码
  • 核心用户流程使用系统测试(Capybara)覆盖
  • 覆盖率目标:模型和服务>80%,整体>60%
  • 测试用例名称描述行为:
    test "user cannot edit others' posts"
  • 控制器和系统测试示例参考references/patterns.md

Dependencies

依赖

Core:
rails ~> 7.1
,
pg
,
puma
,
redis
,
turbo-rails
,
stimulus-rails
,
importmap-rails
Auth:
bcrypt
(has_secure_password)
Background:
sidekiq
Pagination:
kaminari
or
pagy
Dev/Test:
debug
,
capybara
,
selenium-webdriver
,
web-console
,
rack-mini-profiler
Optional:
rspec-rails
,
factory_bot_rails
,
faker
,
shoulda-matchers
,
webmock
核心依赖
rails ~> 7.1
,
pg
,
puma
,
redis
,
turbo-rails
,
stimulus-rails
,
importmap-rails
认证
bcrypt
(实现has_secure_password)
后台任务
sidekiq
分页
kaminari
pagy
开发/测试
debug
,
capybara
,
selenium-webdriver
,
web-console
,
rack-mini-profiler
可选依赖
rspec-rails
,
factory_bot_rails
,
faker
,
shoulda-matchers
,
webmock

Best Practices

最佳实践

Do

推荐

  • Follow RESTful conventions for routes and controllers
  • Use
    includes
    /
    preload
    on every association accessed in views
  • Extract business logic to service objects
  • Use scopes for all reusable query patterns
  • Use
    find_each
    for batch operations on large datasets
  • Use background jobs for email, notifications, and heavy processing
  • Use fragment caching for expensive view partials
  • Keep secrets in credentials or environment variables
  • 路由和控制器遵循RESTful约定
  • 视图中访问的每个关联都使用
    includes
    /
    preload
    预加载
  • 业务逻辑抽取到服务对象中
  • 所有可复用查询模式使用scope实现
  • 大数据集批量操作使用
    find_each
  • 邮件、通知和 heavy 计算使用后台任务处理
  • 耗时的视图partial使用片段缓存
  • 密钥存储在credentials或环境变量中

Don't

禁止

  • Put business logic in controllers or views
  • Use
    params.permit!
    (mass-assignment vulnerability)
  • Use string interpolation in SQL queries
  • Skip database indexes on foreign keys
  • Use
    Model.all
    without pagination or limits
  • Modify migrations after they have been applied to production
  • Use
    raw
    /
    html_safe
    with user-provided data
  • Rely heavily on callbacks for business logic (use services)
  • 控制器或视图中放置业务逻辑
  • 使用
    params.permit!
    (存在批量赋值漏洞)
  • SQL查询中使用字符串插值
  • 外键省略数据库索引
  • 无分页或限制使用
    Model.all
  • 迁移已应用到生产环境后修改
  • 用户提供的数据使用
    raw
    /
    html_safe
  • 业务逻辑重度依赖回调(优先使用服务对象)

Advanced Topics

高级主题

For detailed code examples and advanced patterns, see:
  • references/patterns.md -- ActiveRecord advanced patterns, Hotwire/Turbo, Action Cable, ActiveJob, API mode, deployment, and testing strategies
详细代码示例和高级模式参考:
  • references/patterns.md -- ActiveRecord高级模式、Hotwire/Turbo、Action Cable、ActiveJob、API模式、部署和测试策略

External References

外部参考