🚃

Rails Wayとは何か

に公開

Rails Wayとは何か

Ruby on Railsを使った開発において、「Rails Way」という言葉をよく耳にします。

今までは何となくRails Wayを「Railsの提供する機能のみを使って開発する」くらいに理解していましたが、この記事では改めてRails Wayとは何かについて言語化してみました。

Railsの誕生とDHH

Rails Wayを理解するには、まずRailsの誕生背景を知る必要があります。

Ruby on Railsは2004年、David Heinemeier Hansson(通称: DHH)によって生み出されました。

DHHは当時、Basecampというプロジェクト管理ツールを開発していました。その開発過程で、Webアプリケーション開発に共通する繰り返し作業の多さに気づき、これを効率化するためのフレームワーク「Ruby on Rails」を開発し、2004年に発表しました。

Rails Wayの原則

1. DRY(Don't Repeat Yourself)

Rails Wayの最も重要な原則の一つが「DRY」です。これは「同じことを繰り返し書くな」という意味で、コードの重複を避けることを指します。

Railsでは、このDRYの原則を実現するための仕組みが多数用意されています。

Concernの活用
共通処理は Concern として実装することでコードの重複を避けることができます。

# app/models/concerns/timestampable.rb
module Timestampable
  extend ActiveSupport::Concern

  included do
    scope :recent, -> { order(created_at: :desc) }
    scope :today, -> { where(created_at: Date.current.all_day) }
  end

  def formatted_created_at
    created_at.strftime("%Y年%m月%d日")
  end
end

# 複数のモデルで共通の機能を再利用
class Article < ApplicationRecord
  include Timestampable
end

class Comment < ApplicationRecord
  include Timestampable
end

Scopeの活用
Controller 側で毎回 where の条件を書くのではなく、scopeとして定義することで Controller 側の処理をシンプルに保つことができます。

class User < ApplicationRecord
  # 共通のクエリロジックを一箇所で定義
  scope :active, -> { where(status: 'active') }
  scope :admin, -> { where(role: 'admin') }
  scope :recent, -> { order(created_at: :desc) }
end

# 呼び出し側はシンプルに
User.active.admin.recent.limit(10)

コールバックの活用
Controller内で共通で行われる事前処理などはコールバックとして実装し、action内ではaction固有の処理のみを実装していきます。

class ApplicationController < ActionController::Base
  # 全てのコントローラで共通する認証処理
  before_action :authenticate_user!
  before_action :set_current_user
  
  private
  
  def set_current_user
    Current.user = current_user
  end
end

class ArticlesController < ApplicationController
  # 特定のアクション前に実行される共通処理
  before_action :set_article, only: [:show, :edit, :update, :destroy]
  before_action :check_owner, only: [:edit, :update, :destroy]
  
  def show
    # @articleは既にset_articleで設定済み
  end
  
  def edit
    # @articleの設定とowner確認は既に完了
  end
  
  private
  
  def set_article
    @article = Article.find(params[:id])
  end
  
  def check_owner
    redirect_to root_path unless @article.user == current_user
  end
end

View Helperの活用
View Helperを活用することで、View内に表示のための複雑な分岐処理などを書く必要がなく、Viewをシンプルに保つことができます。

# app/helpers/application_helper.rb
module ApplicationHelper
  # 繰り返し使われるHTMLの生成を共通化
  def page_title(title = nil)
    base_title = "My App"
    title.present? ? "#{title} | #{base_title}" : base_title
  end
  
  def flash_message_class(type)
    case type.to_sym
    when :notice then "alert-success"
    when :alert then "alert-danger"
    when :warning then "alert-warning"
    else "alert-info"
    end
  end
  
  def format_datetime(datetime)
    return unless datetime
    datetime.strftime("%Y年%m月%d日 %H:%M")
  end
end

# ビューファイルでの使用例
# app/views/layouts/application.html.erb
<%= content_for :title, page_title(@page_title) %>

<% flash.each do |type, message| %>
  <div class="alert <%= flash_message_class(type) %>">
    <%= message %>
  </div>
<% end %>

<p>投稿日時: <%= format_datetime(@article.created_at) %></p>

2. 設定より規約(Convention over Configuration)

Rails Wayのもう一つの重要な原則が「設定より規約」です。

これは、開発者が決めなければならない設定を最小限に抑え、代わりに合理的なデフォルト値や命名規約を提供するという考え方です。

例えば:

  • Userモデルは自動的にusersテーブルにマッピングされる
  • UsersControllerindexアクションはapp/views/users/index.html.erbテンプレートを探す
  • 外部キーはuser_idという命名規約で自動的に関連付けられる

これにより、開発者は本来の業務ロジックに集中できます。

他の言語・フレームワークとの比較

多くの言語やフレームワークでは、アーキテクチャの設計から始める必要があります。

しかし、Rails Wayがあることで、Railsではこの段階を大幅に短縮できます。

アーキテクチャを考える必要がない

  • Java + Spring
    DIコンテナの設定、レイヤーの分割、設定ファイルの管理など、開発開始前に多くの設計決定が必要
  • Python + Django
    設定ファイルの構成、URLルーティングの設計、アプリケーション構造の決定が必要
  • Rails
    MVCの基本構造が決まっており、すぐにビジネスロジックやUIの実装に着手できる

0→1からIPOの規模までサポート

Railsは0→1のプロトタイプ開発から、IPOレベルの規模までサポートしており、Rails Wayに従うことでどのステージでも最速で開発ができます。

  • プロトタイプ段階
    迅速に機能を実装し、アイデアを形にできる
  • スタートアップ段階
    少ないリソースで多くの機能を実装できる
  • エンタープライズ段階
    GitHub、Shopify、Airbnbなど大規模サービスでも実証済み

Rails Wayのメリット

1. 開発効率の最大化

Rails Wayに従うことで、以下のような効果が得られます。

  • 早く少ないコード量で開発
    定型的なコードはジェネレーターで自動生成
  • 意思決定の削減
    規約に従うことで、細かな設計判断に時間を取られない
  • 保守性の向上
    一貫したコード構造により、バグの発見と修正が容易

2. チーム開発での優位性

Rails Wayの規約により、以下のような利点があります。

  • 早いキャッチアップ
    Rails経験者であれば、どのRailsアプリケーションでも基本構造は同じなので、最も重要な業務ロジックの理解という部分にすぐに入れる
  • コードレビューの効率化
    共通の規約により、レビューのポイントが明確
  • 属人化の回避
    個人の好みではなく、フレームワークの規約に従うため、コードの一貫性が保たれる

Rails Wayから逸脱するとどうなるか

保守性の低下

Rails Wayから外れたコードは、以下のような問題を引き起こします。

1. 規約に従わない命名

# ❌ Rails Wayに従わない例
class UserInformation < ApplicationRecord
  self.table_name = 'user_data'
  self.primary_key = 'user_data_id'
  
  belongs_to :account, foreign_key: 'account_data_id'
end

# ✅ Rails Wayに従った例
class User < ApplicationRecord
  # テーブル名: users(自動)
  # 主キー: id(自動)
  belongs_to :account # 外部キー: account_id(自動)
end

2. Fat Controllerの問題

# ❌ Rails Wayに従わない例(Fat Controller)
class UsersController < ApplicationController
  def create
    @user = User.new(user_params)
    
    # コントローラにビジネスロジックが混在
    if @user.email.present? && @user.email.include?('@')
      @user.email = @user.email.downcase
      
      if @user.password.length >= 8
        @user.password_digest = BCrypt::Password.create(@user.password)
        
        if @user.save
          # メール送信ロジックもコントローラに
          UserMailer.welcome_email(@user).deliver_now
          
          # ポイント付与ロジックもコントローラに
          @user.update(points: 100)
          
          redirect_to @user, notice: 'ユーザーが作成されました'
        else
          render :new
        end
      else
        @user.errors.add(:password, 'は8文字以上である必要があります')
        render :new
      end
    else
      @user.errors.add(:email, 'は有効な形式である必要があります')
      render :new
    end
  end
end

# ✅ Rails Wayに従った例(Skinny Controller)
class UsersController < ApplicationController
  def create
    @user = User.new(user_params)
    
    if @user.save
      redirect_to @user, notice: 'ユーザーが作成されました'
    else
      render :new
    end
  end
  
  private
  
  def user_params
    params.require(:user).permit(:email, :password, :name)
  end
end

# モデルにビジネスロジックを配置
class User < ApplicationRecord
  has_secure_password
  
  validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
  validates :password, length: { minimum: 8 }
  
  before_save :normalize_email
  after_create :send_welcome_email, :grant_initial_points
  
  private
  
  def normalize_email
    self.email = email.downcase if email.present?
  end
  
  def send_welcome_email
    UserMailer.welcome_email(self).deliver_now
  end
  
  def grant_initial_points
    update_column(:points, 100)
  end
end

設計思想の衝突

Railsは「あえて密結合」を選んだフレームワークです。

これは、DDD(ドメイン駆動設計)のようなレイヤードアーキテクチャとは異なる設計思想です。

  • DDD/レイヤードアーキテクチャ
    責務を疎結合に設計し、ドメインロジックを独立させる
  • Rails Way
    データアクセスやビジネスロジック、バリデーションまで全て同一モデルに配置し、あえて密結合にしている

異なる設計思想のアーキテクチャを採用する場合は、以下を理解する必要があります。

  • Rails Wayの恩恵を受けられなくなる可能性がある
  • 追加の複雑性を管理する必要がある
  • チームメンバーの学習コストが増加する
  • 保守性と開発効率のトレードオフを受け入れる覚悟が必要

まとめ

この記事ではRuby on Railsは開発者のDHHの思想に強く影響を受けていることや、Rails Wayに従って開発をすることで多くの恩恵を受けられることを紹介しました。

Rails Wayに乗って、最速で開発していきましょう💪

参考

GitHubで編集を提案
株式会社Inner Resource

Discussion