🚫

Helperを使わない理由

My Convention — データロジックはControllerに、Helperは最小限に

RailsのHelperはビューで使用できるメソッドを定義するモジュールです。ApplicationHelperにメソッドを追加すると全てのビューで呼び出し可能になります。

一見便利に見えますが、実際のプロジェクトでHelperを積極的に使用すると以下の問題が蓄積します。

なぜHelperを避けるのか?

1. グローバルスコープ汚染

Rails Helperはデフォルトで全ビューにincludeされます。PostsHelperに定義したメソッドがUsersControllerのビューでも呼べます。結果: Helperファイルが増えるほどネームスペース衝突リスクが高まり、メソッドの定義場所の追跡が困難に。

2. データ加工ロジックの間違った場所

DB照会、配列変換、条件付きデータ加工がHelperに入るとMVC境界が崩れます。

# ❌ Bad — Helperにデータ加工
def recent_posts_for_sidebar
  Post.published.order(created_at: :desc).limit(5)
end

# ✅ Good — Controllerで準備
# controller
@recent_posts = Post.published.order(created_at: :desc).limit(5)

Controllerから@インスタンス変数で渡せばフローが一目で分かります: リクエスト → Controller(データ準備) → View(表示)。

3. テスト困難

Helperメソッドはビューコンテキスト(selfがActionView)で実行されます。url_forcontent_tagt()に依存した瞬間、単体テストでこのコンテキストを再現する必要があります。

4. 肥大化するHelperファイル

Helperは「雑多な引き出し」になりやすいです。分類基準が曖昧で、関連のないメソッドが積み上がります。

5. 意図しない削除事故

Helperメソッド名は平凡な場合が多い: format_datestatus_labelbadge_color等。Helperはグローバルなため、定義ファイルと実際の使用箇所が全く異なる場所にあることがあります。リファクタリング中に使われていないと思って削除すると、全く別のビューでundefined methodエラーが発生します。


代替案

推奨: Decorator / Presenterパターン

Helperの根本問題はモデルに属する表示ロジックがグローバル関数として漂っていること。Decoratorはこのロジックをモデルオブジェクトにラップして解決します。

# app/decorators/user_decorator.rb
class UserDecorator < SimpleDelegator
  def badge_color
    active? ? 'green' : 'gray'
  end

  def display_name
    name.presence || email.split('@').first
  end
end

# Controller
def show
  @user = UserDecorator.new(User.find(params[:id]))
end

# View — 普通のモデルのように使う
<span class="bg-<%= @user.badge_color %>-500"><%= @user.display_name %></span>

その他の代替案

  • Controller @変数: データ加工/照会用

  • ビューインライン: 1〜2箇所で使う三項演算子レベルの表示ロジック

  • Partial: 再利用可能なUI断片

  • Service Object: 複雑なビジネスロジック

構造ダイアグラム

❌ アンチパターン: Helperにデータロジック
Controller
(空)
Helper
DB照会 + 加工
グローバル汚染!
View
helper_method()
View Helper (DBアクセス) Controllerスキップ!
MVCフロー破壊 — Viewが直接データを取得
✅ Good: Controllerから@変数で渡す
Controller
@data = 加工結果
データ準備担当
Helper
SVGアイコンのみ
(最小限)
View
&lt;%= @data %&gt;
Controller @変数 View (表示のみ)
MVCフロー維持 — データはController、表示はView
💥 実際の事故: ありふれた名前 + 出所不明 = 削除事故
posts_helper.rb
def badge_color(s)
定義場所
posts/index.erb
badge_color(post)
使用箇所 A
admin/users/index.erb
badge_color(user)
使用箇所 B(全く別の場所!)
🗑️ リファクタリング中にposts_helper.rbを整理
"badge_color? Posts専用だと思ったが使われてないようなので削除"
💥 admin/users/index.erbでエラー発生!
NoMethodError: undefined method 'badge_color'
Helperがグローバルなため、Postsと無関係な場所で壊れる
✨ 推奨代替案: Decorator / Presenter
Decorator
badge_color
display_name
モデルに帰属!
Controller
@user =
UserDecorator.new(user)
View
@user.badge_color
@user.display_name
Controller Decorator(モデルをラップ) View (@user.method)
表示ロジックがモデルオブジェクトに帰属 — 追跡明確、グローバル汚染なし
⚖️ Helper vs Decorator 比較
Helper
Decorator
呼び出し方式
badge_color(user)
@user.badge_color
定義場所
グローバル(どこ?)
UserDecorator
スコープ
全ビューに公開
該当モデルのみ
削除安全性
どこで壊れるか不明
影響範囲明確
テスト
ビューコンテキスト必要
通常のRubyテスト
🤔 判断基準
Helperにメソッドを追加しようとした瞬間 →
モデルの表示ロジック?
badge_color,
display_name, etc.
→ Decorator
データ照会/加工?
DBアクセス、配列変換、条件付きデータ構成
→ Controller @変数
純粋な表示ユーティリティ?
SVGアイコン、日付フォーマット、5箇所以上で再利用
→ Helper (最後の手段)
核心: モデルの表示ロジックはDecorator、データ準備はController — Helperは最後の手段

キーポイント

1

Helperにメソッドを追加しようとする瞬間 → 「これはモデルの表示ロジックか?」自問

2

モデルの表示ロジックなら → Decorator(SimpleDelegator)を作成してモデルをラップ

3

DB照会/データ加工なら → Controllerで@変数で伝達

4

複雑なビジネスロジックなら → Service Objectに分離

5

再利用可能なUI断片なら → Partialに抽出

6

1~2箇所でのみ使う簡単な表示なら → ビューにインライン(三項演算子)

7

それでもHelperが必要なら → 純粋表示関数(SVG、日付フォーマット)のみ、5箇所以上の再利用を確認

メリット

  • MVCフローが明確: Controller(データ) → View(表示)
  • Request Specで自然にテスト可能
  • IDEで@変数の追跡が容易
  • グローバルネームスペース汚染防止
  • Helperファイルが肥大化しない
  • 新メンバーがコードフローを素早く把握

デメリット

  • Controllerが少し長くなることがある
  • 共通表示関数もHelperなしで実装すると重複の可能性
  • Rails公式ガイドとやや異なるアプローチ(Helperを積極的に推奨する資料が多い)
  • render_pattern_iconのような純粋表示関数はHelperが自然

ユースケース

user.badge_color, user.display_name → Decorator post.reading_time, post.status_label → Decorator DB照会結果をビューに伝達 → Controller @変数 配列加工/マッピング → Controllerで処理 複数箇所で再利用されるUIカード/バッジ → Partial インライン条件付きCSSクラス → ビューで直接三項演算子 SVGアイコンレンダリング → Helper(唯一許可)