Reek: Ruby Code Smell Detection Made Simple
As Ruby projects grow, subtle design issues can creep in and quietly erode code quality. Reek helps you catch these problems early by detecting code smells—patterns that hint at deeper structural flaws.
It scans your code for anti-patterns like long methods, feature envy, and data clumps, surfacing opportunities for refactoring before they turn into maintenance headaches. Unlike formatters that only polish style, Reek focuses on the overall health and design of your codebase.
This guide walks through installing, configuring, and integrating Reek to keep your Ruby projects clean and maintainable.
Prerequisites
To work through this guide, you'll need Ruby 2.7 or later installed:
ruby --version
ruby 3.4.5
This guide assumes familiarity with Ruby programming concepts including classes, methods, and basic object-oriented design principles. Understanding common refactoring patterns will help you interpret and act on Reek's findings.
Understanding code smells and quality issues
Code smells are symptoms of deeper design problems that make code harder to understand, modify, and maintain. These issues often develop incrementally as features are added without careful attention to overall design quality.
Consider this example that demonstrates several common code quality issues:
class UserManager
def process_user_data(name, email, age, role, department, salary, manager_id, start_date)
# Long parameter list - data clump smell
if role == "admin"
if age > 18
if email.include?("@")
if salary > 50000
# Nested conditionals - complexity smell
user = create_user_hash(name, email, age, role, department, salary, manager_id, start_date)
@admin_users << user
send_welcome_email(user)
setup_admin_permissions(user)
return "Admin user created successfully"
else
return "Insufficient salary for admin role"
end
end
end
elsif role == "guest"
# Duplicated logic - duplication smell
if age > 16 && email.include?("@")
user = create_user_hash(name, email, age, role, department, 0, nil, start_date)
@guest_users << user
send_welcome_email(user)
return "Guest user created successfully"
end
end
return "Unknown role type"
end
def send_welcome_email(user)
# Feature envy - accessing too much data from user
puts "Welcome #{user[:name]} to #{user[:department]} as #{user[:role]}"
end
end
This code exhibits multiple quality issues: excessively long methods, too many parameters, deep nesting, duplicated logic, and methods that access too much external data. These problems make the code difficult to test, modify, and understand.
Manual code review might miss some of these issues, especially in larger codebases where patterns emerge across multiple files. Reek automates this analysis, consistently identifying quality problems regardless of code review attention.
Create a project directory to explore Reek's capabilities:
mkdir reek-analysis-demo && cd reek-analysis-demo
Installing and basic usage
Reek installs as a Ruby gem and works immediately with sensible defaults for detecting common code quality issues. The tool provides both command-line analysis and programmatic integration options.
Create a Gemfile for your project:
bundle init
Add Reek to your project's Gemfile:
source 'https://rubygems.org'
gem 'reek', '~> 6.5'
Install the dependencies:
bundle install
Fetching gem metadata from https://rubygems.org/...
Resolving dependencies...
Fetching reek 6.5.0
Installing reek 6.5.0
Bundle complete! 1 Gemfile dependency, 18 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
Create the problematic code example from above:
class UserManager
def process_user_data(name, email, age, role, department, salary, manager_id, start_date)
if role == "admin"
if age > 18
if email.include?("@")
if salary > 50000
user = create_user_hash(name, email, age, role, department, salary, manager_id, start_date)
@admin_users << user
send_welcome_email(user)
setup_admin_permissions(user)
return "Admin user created successfully"
else
return "Insufficient salary for admin role"
end
end
end
elsif role == "guest"
if age > 16 && email.include?("@")
user = create_user_hash(name, email, age, role, department, 0, nil, start_date)
@guest_users << user
send_welcome_email(user)
return "Guest user created successfully"
end
end
return "Unknown role type"
end
def send_welcome_email(user)
puts "Welcome #{user[:name]} to #{user[:department]} as #{user[:role]}"
end
def setup_admin_permissions(user)
puts "Setting up admin permissions for #{user[:name]} in #{user[:department]}"
end
end
Run Reek to analyze the code quality:
bundle exec reek user_manager.rb
Inspecting 1 file(s):
S
user_manager.rb -- 9 warnings:
[5, 18]:DuplicateMethodCall: UserManager#process_user_data calls 'email.include?("@")' 2 times
[9, 21]:DuplicateMethodCall: UserManager#process_user_data calls 'send_welcome_email(user)' 2 times
[30, 30, 30]:FeatureEnvy: UserManager#send_welcome_email refers to 'user' more than self
[34, 34]:FeatureEnvy: UserManager#setup_admin_permissions refers to 'user' more than self
[1]:InstanceVariableAssumption: UserManager assumes too much for instance variable '@admin_users'
[1]:InstanceVariableAssumption: UserManager assumes too much for instance variable '@guest_users'
[1]:IrresponsibleModule: UserManager has no descriptive comment
[2]:LongParameterList: UserManager#process_user_data has 8 parameters
[2]:TooManyStatements: UserManager#process_user_data has approx 11 statements
Reek identified multiple code quality issues with specific line numbers and helpful descriptions. Each warning includes the smell type and actionable suggestions, such as moving methods to other classes or reducing parameter counts.
The analysis reveals issues like duplicate method calls, feature envy (methods accessing too much external data), missing documentation, and overly complex methods. These findings provide concrete starting points for refactoring efforts.
Understanding Reek's code smell detection
Reek categorizes code quality issues into distinct smell types, each representing a different aspect of design problems. Understanding these categories helps prioritize refactoring efforts and build better code structure habits.
Method-level smells
Method-level smells indicate problems with individual method design:
Long Parameter List: Methods with too many parameters suggest the method is doing too much or that related data should be grouped into objects.
Too Many Statements: Methods with many lines often violate the single responsibility principle and become difficult to test and understand.
Nested Iterators: Deep nesting creates complex control flow that's hard to follow and prone to bugs.
Create an example demonstrating method-level issues:
class ReportGenerator
# Long Parameter List and Too Many Statements
def generate_user_report(user_id, start_date, end_date, format, include_stats, include_graphs, email_recipient, department_filter)
user = find_user(user_id)
activities = fetch_activities(user_id, start_date, end_date)
activities = activities.select { |a| a.department == department_filter } if department_filter
report_data = {}
report_data[:total_activities] = activities.count if include_stats
report_data[:avg_duration] = calculate_average_duration(activities) if include_stats
if include_graphs
# Nested iteration complexity
activities.each do |activity|
activity.tasks.each do |task|
task.subtasks.each { |subtask| process_subtask(subtask, report_data) }
end
end
end
formatted_report = format_report(report_data, format)
send_report_email(formatted_report, email_recipient) if email_recipient
save_and_log_report(formatted_report, user_id)
return formatted_report
end
end
Class-level smells
Class-level smells reveal problems with class design and responsibility distribution:
Feature Envy: Methods that use more data from other classes than from their own class suggest misplaced behavior.
Data Clump: Groups of variables that consistently appear together indicate missing abstractions.
Utility Function: Methods that don't use instance state might belong elsewhere or suggest a class with unclear purpose.
Duplication smells
Duplication smells identify repeated code patterns that should be extracted into reusable components:
Duplicate Method Call: The same method called repeatedly with identical parameters.
Repeated Conditional: The same conditional logic appearing in multiple places.
Analyze the method smells example:
bundle exec reek method_smells.rb
Inspecting 1 file(s):
S
method_smells.rb -- 7 warnings:
[13]:ControlParameter: ReportGenerator#generate_user_report is controlled by argument 'include_graphs'
[10, 11]:ControlParameter: ReportGenerator#generate_user_report is controlled by argument 'include_stats'
[1]:IrresponsibleModule: ReportGenerator has no descriptive comment
[3]:LongParameterList: ReportGenerator#generate_user_report has 8 parameters
[17]:NestedIterators: ReportGenerator#generate_user_report contains iterators nested 3 deep
[3]:TooManyStatements: ReportGenerator#generate_user_report has approx 15 statements
[7]:UncommunicativeVariableName: ReportGenerator#generate_user_report has the variable name 'a'
The analysis reveals several additional smell types beyond the basic ones we discussed:
Control Parameter: Methods that change behavior based on boolean or flag parameters, suggesting the method might be doing too much.
Uncommunicative Variable Name: Variables with unclear names like single letters that don't convey meaning.
Irresponsible Module: Classes or modules lacking documentation comments.
These findings demonstrate how Reek catches subtle design issues that might be missed during manual code review, providing specific guidance for improving code clarity and maintainability.
Configuring Reek for your project
Reek provides configuration options that let you customize detection rules for your project's specific requirements and coding standards. Configuration helps reduce noise from acceptable design decisions while maintaining focus on genuine quality issues.
Create a Reek configuration file:
# Disable specific smells globally
detectors:
TooManyStatements:
max_statements: 20
LongParameterList:
max_params: 5
FeatureEnvy:
enabled: false
UtilityFunction:
enabled: false
# Configure per-directory rules
directories:
"lib/":
TooManyStatements:
max_statements: 15
"spec/":
TooManyStatements:
max_statements: 30
LongParameterList:
max_params: 8
# Exclude specific files
exclude_paths:
- db/migrate
- vendor
- tmp
This configuration demonstrates several customization approaches:
- Adjusting thresholds for specific smells based on project needs
- Disabling smells that don't align with your design philosophy
- Setting different rules for different directories (stricter for lib, more relaxed for tests)
- Excluding generated or third-party code from analysis
Per-file smell suppression
You can also suppress specific smells using inline comments:
class DataProcessor
# :reek:LongParameterList
def process_data(id, name, email, department, role, salary, start_date, manager)
# Method intentionally accepts many parameters for batch processing
create_user_record(id, name, email, department, role, salary, start_date, manager)
end
# :reek:TooManyStatements
def complex_calculation
# This method performs a multi-step calculation that's clearer as one unit
step_one = initial_processing
step_two = intermediate_processing(step_one)
step_three = advanced_processing(step_two)
step_four = final_processing(step_three)
validate_result(step_four)
log_calculation_result(step_four)
return step_four
end
end
Test the configuration by running Reek with your updated settings:
bundle exec reek configured_example.rb
Inspecting 1 file(s):
S
configured_example.rb -- 1 warning:
[1]:IrresponsibleModule: DataProcessor has no descriptive comment
The inline suppressions eliminated the Long Parameter List and Too Many Statements warnings for methods where the design decisions are intentional and well-justified. The remaining warning about missing documentation can be addressed by adding a class comment or suppressed if documentation isn't required for this example.
Interpreting and acting on Reek reports
Understanding how to interpret Reek's output and prioritize refactoring efforts helps maximize the value of code quality analysis. Not all smells require immediate action, and some indicate architectural decisions rather than problems.
Output formats and analysis
Reek supports multiple output formats for different use cases:
Human-readable format (default):
bundle exec reek user_manager.rb
JSON format for programmatic processing:
bundle exec reek --format json user_manager.rb
HTML format for detailed reporting:
bundle exec reek --format html user_manager.rb > quality_report.html
The JSON format proves useful for integration with other tools or custom analysis scripts:
bundle exec reek --format json user_manager.rb
{
"user_manager.rb": [
{
"context": "UserManager#process_user_data",
"lines": [4],
"message": "has too many parameters (8)",
"smell_type": "LongParameterList",
"source": "user_manager.rb"
}
]
}
Final thoughts
Reek transforms subjective code quality discussions into objective analysis based on established design principles. Start with default configuration, then customize detection rules to match your project's specific needs.
Focus on high-impact smells that affect maintainability and readability. Use Reek's findings as starting points for design discussions rather than absolute requirements - some smells may be acceptable trade-offs for specific architectural needs.
Regular analysis builds awareness of code quality patterns and helps develop intuition for better design decisions. The tool works best as part of normal development workflows rather than occasional cleanup sessions.
Explore the Reek documentation for advanced configuration options and integration patterns.