Why We Avoid Helpers
My Convention โ data logic in Controller, minimal Helpers
Rails Helpers are modules that define methods usable in views. Adding a method to ApplicationHelper makes it callable from all views.
While seemingly convenient, actively using Helpers in real projects accumulates these problems.
Why Avoid Helpers?
1. Global Scope Pollution
Rails Helpers are included in all views by default. A method in PostsHelper is callable from UsersController views too. Result: as Helper files grow, namespace collision risk increases and method origins become hard to trace.
2. Data Logic in the Wrong Place
When DB queries, array transforms, and conditional data processing enter Helpers, MVC boundaries break down.
# โ Bad โ data processing in Helper
def recent_posts_for_sidebar
Post.published.order(created_at: :desc).limit(5)
end
# โ
Good โ prepare in Controller
# controller
@recent_posts = Post.published.order(created_at: :desc).limit(5)
Passing via @instance_variables from Controller makes the flow visible at a glance: request โ Controller(data prep) โ View(display).
3. Testing Difficulty
Helper methods run in view context (self is ActionView). The moment they depend on url_for, content_tag, t(), you must reproduce this context in unit tests.
4. Bloating Helper Files
Helpers easily become "junk drawers" โ classification criteria are vague and unrelated methods pile up.
5. Accidental Deletion Risk
Helper method names are often generic: format_date, status_label, badge_color. Since Helpers are global, the definition file and actual usage can be in completely different places. During refactoring, someone might delete a method thinking it's unused, causing undefined method errors in completely unrelated views.
Alternatives
Recommended: Decorator / Presenter Pattern
The fundamental problem with Helpers is that display logic belonging to a model floats around as global functions. Decorators solve this by wrapping the model object.
# 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 โ use like a regular model
<span class="bg-<%= @user.badge_color %>-500"><%= @user.display_name %></span>
Other Alternatives
Controller @variables: for data processing/queries
View inline: ternary-level display logic used in 1-2 places
Partial: reusable UI fragments
Service Object: complex business logic
Architecture Diagram
display_name, etc.
Key Points
Before adding a Helper method โ ask "Is this model display logic?"
If model display logic โ create Decorator (SimpleDelegator) wrapping the model
If DB query/data processing โ pass via @variables from Controller
If complex business logic โ extract to Service Object
If reusable UI fragment โ extract to Partial
If simple display used in 1-2 places โ inline in view (ternary)
If Helper still needed โ pure display functions only (SVG, date format), verify 5+ reuse points
Pros
- ✓ Clear MVC flow: Controller(data) โ View(display)
- ✓ Naturally testable in Request Spec
- ✓ Easy @variable tracking in IDE
- ✓ Prevents global namespace pollution
- ✓ Helper files stay lean
- ✓ New team members quickly understand code flow
Cons
- ✗ Controller may get slightly longer
- ✗ Common display functions without Helpers may cause duplication
- ✗ Somewhat different from official Rails guide (many resources actively recommend Helpers)
- ✗ Pure display functions like render_pattern_icon are natural as Helpers