Overview
Reek examines Ruby code and detects code smells—indicators of poor design and maintenance problems. The tool analyzes source files, classes, methods, and modules to identify patterns that suggest refactoring opportunities. Reek operates as both a command-line utility and a Ruby library, scanning code for over 20 different smell types including feature envy, data clumps, long parameter lists, and nested iterators.
The core functionality centers around the Reek::Source
class which parses Ruby code, and Reek::Examiner
which applies detection rules. Smell detectors inherit from Reek::SmellDetector
and implement pattern recognition for specific code quality issues. Each detector produces Reek::SmellWarning
objects containing details about detected problems.
require 'reek'
# Analyze a string of code
source = Reek::Source.from_s(<<~RUBY)
class Customer
def initialize(name, email, phone, address)
@name = name
@email = email
@phone = phone
@address = address
end
end
RUBY
examiner = Reek::Examiner.new(source)
examiner.smells.each do |smell|
puts "#{smell.smell_type}: #{smell.message}"
end
# => LongParameterList: Customer#initialize has 4 parameters
Reek integrates with development workflows through configuration files, rake tasks, and continuous integration systems. The configuration system uses YAML files to customize detection rules, disable specific smells, and exclude code patterns from analysis.
# Analyze a file directly
examiner = Reek::Examiner.new(Reek::Source.from_file('app/models/user.rb'))
puts examiner.smells.map(&:message)
The tool supports multiple output formats including text, JSON, XML, and custom formatters. Reek maintains compatibility with Ruby static analysis ecosystems and provides programmatic access to smell detection results for custom tooling and reporting systems.
Basic Usage
Installing Reek requires adding the gem to your project and running basic analysis commands. The command-line interface provides immediate feedback on code quality issues across entire codebases or specific files.
gem install reek
# or add to Gemfile: gem 'reek'
The primary command reek
accepts file paths, directories, or reads from STDIN. Running reek without arguments analyzes all Ruby files in the current directory structure.
# Analyze specific files
system("reek app/models/user.rb app/controllers/")
# Analyze with specific format
system("reek --format json app/")
# Check exit status
result = system("reek lib/")
puts "Analysis passed" if result
Programmatic analysis through the Ruby API provides finer control over smell detection and result processing. The Reek::Examiner
class serves as the primary interface for code analysis.
require 'reek'
code = <<~RUBY
class OrderProcessor
def process(order)
if order.valid?
if order.payment_method == 'credit_card'
charge_credit_card(order)
elsif order.payment_method == 'paypal'
charge_paypal(order)
elsif order.payment_method == 'bank_transfer'
charge_bank(order)
end
send_confirmation(order)
update_inventory(order)
else
log_error(order.errors)
end
end
end
RUBY
source = Reek::Source.from_s(code)
examiner = Reek::Examiner.new(source)
examiner.smells.each do |smell|
puts "File: #{smell.source}"
puts "Line: #{smell.lines.first}"
puts "Smell: #{smell.smell_type}"
puts "Method: #{smell.context}"
puts "Message: #{smell.message}"
puts "---"
end
Configuration files control which smells Reek detects and how strictly rules apply. The .reek.yml
file in the project root defines global settings, while inline comments provide method-level control.
# .reek.yml
detectors:
LongParameterList:
max_params: 3
NestedIterators:
enabled: false
UtilityFunction:
exclude:
- 'MyClass#helper_method'
Inline configuration uses magic comments to disable specific smells for individual methods or classes.
class DataProcessor
# :reek:LongMethod
def complex_calculation(a, b, c, d)
# Method implementation with many lines
result = a * b
result += c if c > 0
result -= d if d < 0
result *= 1.1 if weekend?
result
end
# :reek:UtilityFunction
def self.format_currency(amount)
"$#{amount.round(2)}"
end
end
The rake integration provides convenient access to Reek analysis within Ruby build systems and continuous integration pipelines.
# Rakefile
require 'reek/rake/task'
Reek::Rake::Task.new do |t|
t.source_files = 'lib/**/*.rb'
t.config_file = '.reek.yml'
t.fail_on_error = true
end
Advanced Usage
Reek supports extensive customization through detector configuration, custom smell patterns, and integration with complex development workflows. Advanced configuration enables fine-tuning smell detection thresholds and excluding specific code patterns from analysis.
The configuration system provides granular control over individual smell detectors. Each detector accepts specific parameters that modify detection behavior and sensitivity levels.
# Advanced .reek.yml configuration
detectors:
LongMethod:
max_statements: 15
exclude:
- 'DatabaseMigration#up'
- 'DatabaseMigration#down'
FeatureEnvy:
enabled: true
exclude:
- 'ActiveRecord::Base#' # Exclude all AR methods
DataClump:
max_copies: 3
min_clump_size: 3
DuplicateMethodCall:
max_calls: 2
allow_calls:
- 'new'
- 'size'
- 'length'
Custom smell detection patterns extend Reek's analysis capabilities through Ruby metaprogramming and AST manipulation. Creating custom detectors requires understanding Reek's internal architecture and the Parser gem's abstract syntax trees.
# Custom smell detector example
module Reek
module SmellDetectors
class ExcessiveComments < SmellDetector
def self.smell_type
'ExcessiveComments'
end
def self.default_config
{
'max_comments_per_method' => 5
}
end
def sniff(ctx)
return [] unless ctx.is_a?(Reek::Context::MethodContext)
comment_count = count_comments(ctx)
return [] if comment_count <= max_comments
[warning(
ctx,
message: "Method #{ctx.full_name} has #{comment_count} comments",
lines: ctx.exp.line_numbers
)]
end
private
def max_comments
config['max_comments_per_method']
end
def count_comments(ctx)
# Implementation to count comments in method context
source_lines = ctx.exp.source_buffer.source.lines
method_lines = source_lines[ctx.exp.line_numbers.min..ctx.exp.line_numbers.max]
method_lines.count { |line| line.strip.start_with?('#') }
end
end
end
end
Multi-project configurations handle complex codebases with different quality standards across components. Directory-specific configuration files override global settings for targeted analysis.
# Project structure with layered configuration
project_root/
├── .reek.yml # Global settings
├── lib/
│ ├── .reek.yml # Library-specific rules
│ └── core/
│ └── .reek.yml # Core module rules
└── spec/
└── .reek.yml # Test-specific configuration
# lib/.reek.yml - stricter rules for core code
detectors:
LongMethod:
max_statements: 10
ComplexityScore:
max_score: 8
# spec/.reek.yml - relaxed rules for tests
detectors:
LongMethod:
enabled: false
UtilityFunction:
enabled: false
Programmatic configuration modification allows dynamic rule adjustment based on runtime conditions or external factors.
class AdaptiveReekAnalysis
def analyze_with_context(source_path, strictness_level)
config = base_configuration
case strictness_level
when :strict
config.merge!(strict_overrides)
when :legacy
config.merge!(legacy_overrides)
end
examiner = Reek::Examiner.new(
Reek::Source.from_file(source_path),
configuration: Reek::Configuration::AppConfiguration.new(config)
)
process_results(examiner.smells, strictness_level)
end
private
def base_configuration
{
'detectors' => {
'LongMethod' => { 'max_statements' => 12 },
'LongParameterList' => { 'max_params' => 4 }
}
}
end
def strict_overrides
{
'detectors' => {
'LongMethod' => { 'max_statements' => 8 },
'ComplexityScore' => { 'max_score' => 6 }
}
}
end
end
Integration with code review workflows requires custom formatting and filtering based on changed files or specific review criteria.
class CodeReviewAnalyzer
def analyze_diff(base_commit, head_commit)
changed_files = git_changed_files(base_commit, head_commit)
ruby_files = changed_files.select { |f| f.end_with?('.rb') }
results = {}
ruby_files.each do |file|
examiner = Reek::Examiner.new(Reek::Source.from_file(file))
new_smells = filter_new_smells(examiner.smells, base_commit, file)
results[file] = new_smells if new_smells.any?
end
generate_review_report(results)
end
private
def filter_new_smells(smells, base_commit, file)
base_content = git_file_content(base_commit, file)
return smells if base_content.nil?
base_examiner = Reek::Examiner.new(Reek::Source.from_s(base_content))
base_smell_signatures = base_examiner.smells.map(&:smell_signature)
smells.reject { |smell| base_smell_signatures.include?(smell.smell_signature) }
end
end
Error Handling & Debugging
Reek analysis failures occur when code contains syntax errors, encoding issues, or when detector logic encounters unexpected AST structures. Error handling strategies ensure analysis continues despite individual file problems and provide diagnostic information for troubleshooting.
Syntax error handling prevents analysis failures when examining codebases with syntax problems. Reek catches parser exceptions and reports them as analysis errors rather than crashing the entire process.
class RobustAnalyzer
def analyze_directory(path)
results = { successes: [], errors: [] }
Dir.glob("#{path}/**/*.rb").each do |file|
begin
source = Reek::Source.from_file(file)
examiner = Reek::Examiner.new(source)
results[:successes] << {
file: file,
smells: examiner.smells
}
rescue Reek::Errors::IncomprehensibleSourceError => e
results[:errors] << {
file: file,
error: :syntax_error,
message: e.message
}
rescue Encoding::UndefinedConversionError => e
results[:errors] << {
file: file,
error: :encoding_error,
message: "Encoding issue: #{e.message}"
}
rescue => e
results[:errors] << {
file: file,
error: :unexpected,
message: e.message,
backtrace: e.backtrace.first(5)
}
end
end
results
end
end
Configuration validation prevents runtime errors from invalid detector settings or malformed YAML files. Validation catches common configuration mistakes before analysis begins.
class ConfigurationValidator
def self.validate(config_path)
config = YAML.load_file(config_path)
errors = []
if config['detectors']
config['detectors'].each do |detector_name, settings|
errors.concat(validate_detector(detector_name, settings))
end
end
if config['directories']
config['directories'].each do |dir_config|
errors.concat(validate_directory_config(dir_config))
end
end
unless errors.empty?
raise Reek::Errors::ConfigurationError,
"Configuration errors:\n#{errors.join("\n")}"
end
config
rescue Psych::SyntaxError => e
raise Reek::Errors::ConfigurationError,
"YAML syntax error in #{config_path}: #{e.message}"
end
private
def self.validate_detector(name, settings)
detector_class = "Reek::SmellDetectors::#{name}".constantize
valid_keys = detector_class.default_config.keys
invalid_keys = settings.keys - valid_keys
invalid_keys.map { |key| "Unknown setting '#{key}' for #{name}" }
rescue NameError
["Unknown detector: #{name}"]
end
end
Debugging smell detection requires understanding how Reek analyzes code structure and why specific patterns trigger detection rules. Debug output reveals the analysis process and helps identify false positives.
class SmellDebugger
def debug_method_analysis(source_code, method_name)
source = Reek::Source.from_s(source_code)
examiner = Reek::Examiner.new(source)
# Find the specific method context
method_context = find_method_context(examiner.context, method_name)
return unless method_context
puts "Method: #{method_context.full_name}"
puts "Lines: #{method_context.exp.line_numbers}"
puts "Statements: #{count_statements(method_context)}"
puts "Parameters: #{method_context.parameters.length}"
# Run individual detectors with debug info
examiner.configuration.smell_detectors.each do |detector_class|
detector = detector_class.new(examiner.configuration)
smells = detector.sniff(method_context)
if smells.any?
puts "\n#{detector_class.smell_type}:"
smells.each do |smell|
puts " #{smell.message}"
puts " Config: #{detector.config}"
end
end
end
end
private
def find_method_context(context, method_name)
return context if context.matches?([method_name])
context.children.each do |child|
result = find_method_context(child, method_name)
return result if result
end
nil
end
end
False positive management requires systematic approaches to identify legitimate code patterns that trigger smell detection incorrectly. Documenting and excluding false positives maintains analysis accuracy while preserving code quality standards.
class FalsePositiveManager
def initialize(config_file = '.reek.yml')
@config_file = config_file
@config = load_config
end
def suppress_smell(file_path, method_name, smell_type, reason)
exclusion_path = "#{extract_class_name(file_path)}##{method_name}"
@config['detectors'] ||= {}
@config['detectors'][smell_type] ||= {}
@config['detectors'][smell_type]['exclude'] ||= []
unless @config['detectors'][smell_type]['exclude'].include?(exclusion_path)
@config['detectors'][smell_type]['exclude'] << exclusion_path
add_comment_to_config(smell_type, exclusion_path, reason)
save_config
end
end
def review_suppressed_smells
return {} unless @config['detectors']
suppressed = {}
@config['detectors'].each do |detector, config|
next unless config['exclude']
config['exclude'].each do |exclusion|
suppressed[exclusion] = {
detector: detector,
reason: extract_comment(detector, exclusion)
}
end
end
suppressed
end
private
def load_config
File.exist?(@config_file) ? YAML.load_file(@config_file) : {}
end
def save_config
File.write(@config_file, @config.to_yaml)
end
end
Performance debugging identifies analysis bottlenecks in large codebases. Profiling Reek execution reveals which files or detectors consume excessive processing time.
class ReekProfiler
def profile_analysis(directory)
require 'benchmark'
results = {
total_time: 0,
file_times: {},
detector_times: Hash.new(0),
slow_files: []
}
Dir.glob("#{directory}/**/*.rb").each do |file|
file_time = Benchmark.realtime do
analyze_with_detector_profiling(file, results[:detector_times])
end
results[:file_times][file] = file_time
results[:total_time] += file_time
results[:slow_files] << [file, file_time] if file_time > 1.0
end
results[:slow_files].sort_by! { |_, time| -time }
results
end
private
def analyze_with_detector_profiling(file, detector_times)
source = Reek::Source.from_file(file)
examiner = Reek::Examiner.new(source)
examiner.configuration.smell_detectors.each do |detector_class|
time = Benchmark.realtime do
detector = detector_class.new(examiner.configuration)
detector.run(examiner.context)
end
detector_times[detector_class.smell_type] += time
end
end
end
Production Patterns
Production deployments of Reek require integration with continuous integration systems, automated quality gates, and team-wide development workflows. Effective production usage balances code quality enforcement with development velocity and handles diverse codebase characteristics across team members and project phases.
Continuous integration integration establishes quality gates that prevent code quality regression while avoiding false failures from legitimate code patterns. CI configurations must account for legacy code, emergency fixes, and gradual quality improvement strategies.
# ci/quality_check.rb
class QualityGate
def initialize(config = {})
@baseline_file = config[:baseline_file] || '.reek_baseline.yml'
@fail_threshold = config[:fail_threshold] || 0
@warning_threshold = config[:warning_threshold] || 5
end
def run_quality_check
current_smells = analyze_current_codebase
baseline_smells = load_baseline_smells
new_smells = identify_new_smells(current_smells, baseline_smells)
regression_count = new_smells.length
generate_ci_report(current_smells, new_smells, regression_count)
case
when regression_count > @fail_threshold
exit_with_failure("Quality gate failed: #{regression_count} new smells detected")
when regression_count > @warning_threshold
puts "Warning: #{regression_count} new smells detected (threshold: #{@warning_threshold})"
exit 0
else
puts "Quality gate passed: #{regression_count} new smells"
exit 0
end
end
private
def analyze_current_codebase
results = {}
Dir.glob("app/**/*.rb", "lib/**/*.rb").each do |file|
next if file.match?(/\A(db\/migrate|vendor)/)
examiner = Reek::Examiner.new(Reek::Source.from_file(file))
smells = examiner.smells.map do |smell|
{
file: file,
line: smell.lines.first,
type: smell.smell_type,
context: smell.context,
signature: smell_signature(smell)
}
end
results[file] = smells
end
results
end
def identify_new_smells(current, baseline)
current_signatures = extract_all_signatures(current)
baseline_signatures = extract_all_signatures(baseline)
new_signature_set = current_signatures - baseline_signatures
find_smells_by_signatures(current, new_signature_set)
end
end
Monitoring and alerting systems track code quality trends over time and alert teams to significant quality degradation. Production monitoring integrates with existing observability platforms and provides actionable quality metrics.
class QualityMonitor
def initialize(metrics_client)
@metrics = metrics_client
end
def collect_quality_metrics(repository_path)
analysis_start = Time.now
smell_counts = Hash.new(0)
file_count = 0
total_lines = 0
Dir.glob("#{repository_path}/**/*.rb").each do |file|
next if excluded_path?(file)
file_count += 1
total_lines += count_lines(file)
examiner = Reek::Examiner.new(Reek::Source.from_file(file))
examiner.smells.each { |smell| smell_counts[smell.smell_type] += 1 }
end
analysis_duration = Time.now - analysis_start
send_metrics(smell_counts, file_count, total_lines, analysis_duration)
check_quality_thresholds(smell_counts, file_count)
end
private
def send_metrics(smell_counts, file_count, total_lines, duration)
@metrics.gauge('code_quality.files_analyzed', file_count)
@metrics.gauge('code_quality.total_lines', total_lines)
@metrics.gauge('code_quality.analysis_duration_ms', duration * 1000)
smell_counts.each do |smell_type, count|
@metrics.gauge("code_quality.smells.#{smell_type.downcase}", count)
end
total_smells = smell_counts.values.sum
smell_density = total_lines > 0 ? (total_smells.to_f / total_lines * 1000).round(2) : 0
@metrics.gauge('code_quality.smell_density_per_kloc', smell_density)
end
def check_quality_thresholds(smell_counts, file_count)
total_smells = smell_counts.values.sum
smell_ratio = total_smells.to_f / file_count
if smell_ratio > 10.0
@metrics.increment('code_quality.alerts.high_smell_ratio')
alert_team("High smell ratio detected: #{smell_ratio.round(2)} smells per file")
end
smell_counts.each do |smell_type, count|
threshold = quality_thresholds[smell_type]
if threshold && count > threshold
@metrics.increment("code_quality.alerts.#{smell_type.downcase}_threshold")
alert_team("#{smell_type} threshold exceeded: #{count} instances")
end
end
end
end
Team workflow integration requires balancing individual developer feedback with team-wide quality standards. Workflow integration includes pre-commit hooks, editor integration, and development environment setup that provides immediate feedback without disrupting development flow.
# tools/pre_commit_quality.rb
class PreCommitQualityCheck
def run
staged_files = git_staged_ruby_files
return exit_success("No Ruby files staged") if staged_files.empty?
problems = []
staged_files.each do |file|
file_problems = analyze_staged_file(file)
problems.concat(file_problems) if file_problems.any?
end
if problems.any?
display_problems(problems)
offer_auto_fixes(problems)
else
puts "Quality check passed for #{staged_files.length} files"
exit 0
end
end
private
def analyze_staged_file(file)
# Get staged content, not working directory content
staged_content = `git show :#{file}`
return [] if staged_content.empty?
examiner = Reek::Examiner.new(Reek::Source.from_s(staged_content))
examiner.smells.map do |smell|
{
file: file,
line: smell.lines.first,
type: smell.smell_type,
message: smell.message,
context: smell.context,
auto_fixable: auto_fixable_smell?(smell)
}
end
end
def offer_auto_fixes(problems)
auto_fixable = problems.select { |p| p[:auto_fixable] }
if auto_fixable.any?
puts "\nSome issues can be automatically fixed:"
auto_fixable.each do |problem|
puts " #{problem[:file]}:#{problem[:line]} - #{problem[:type]}"
end
print "Apply automatic fixes? [y/N]: "
if STDIN.gets.chomp.downcase == 'y'
apply_auto_fixes(auto_fixable)
puts "Fixes applied. Please review and commit again."
exit 1
end
end
exit 1
end
end
Scaling strategies for large codebases require parallel analysis, incremental improvement approaches, and selective quality enforcement. Large-scale production usage must handle repositories with thousands of files while maintaining reasonable analysis times.
class ScalableAnalyzer
def initialize(config = {})
@worker_count = config[:workers] || processor_count
@chunk_size = config[:chunk_size] || 50
@analysis_timeout = config[:timeout] || 300
end
def analyze_repository(repo_path)
files = discover_ruby_files(repo_path)
puts "Analyzing #{files.length} files with #{@worker_count} workers"
file_chunks = files.each_slice(@chunk_size).to_a
results = Parallel.map(file_chunks, in_processes: @worker_count) do |chunk|
analyze_file_chunk(chunk)
end
aggregate_results(results.flatten)
end
private
def analyze_file_chunk(files)
chunk_results = []
files.each do |file|
begin
Timeout.timeout(@analysis_timeout / @chunk_size) do
examiner = Reek::Examiner.new(Reek::Source.from_file(file))
chunk_results << {
file: file,
smells: examiner.smells.map(&:to_h),
analysis_time: Time.now
}
end
rescue Timeout::Error
chunk_results << {
file: file,
error: "Analysis timeout",
analysis_time: Time.now
}
rescue => e
chunk_results << {
file: file,
error: e.message,
analysis_time: Time.now
}
end
end
chunk_results
end
def aggregate_results(all_results)
successful = all_results.reject { |r| r[:error] }
failed = all_results.select { |r| r[:error] }
total_smells = successful.sum { |r| r[:smells]&.length || 0 }
{
summary: {
files_analyzed: successful.length,
files_failed: failed.length,
total_smells: total_smells,
analysis_date: Time.now
},
results: successful,
failures: failed
}
end
end
Common Pitfalls
Reek analysis produces false positives when code patterns appear problematic but serve legitimate purposes. Understanding common false positive scenarios prevents over-configuration and maintains code quality standards while accommodating necessary design patterns.
Configuration proliferation occurs when teams add exclusions reactively without understanding root causes. Excessive configuration undermines code quality goals and creates maintenance overhead that reduces analysis effectiveness.
# Problematic: Over-configured .reek.yml
detectors:
LongMethod:
exclude:
- 'UserController#create'
- 'UserController#update'
- 'OrderProcessor#process'
- 'PaymentHandler#charge'
- 'ReportGenerator#generate'
- 'DataImporter#import'
# ... 50+ exclusions
# Better: Address underlying design issues
class UserController
def create
user_creator = UserCreationService.new(user_params)
if user_creator.create
redirect_to user_path(user_creator.user)
else
handle_creation_errors(user_creator.errors)
end
end
private
def handle_creation_errors(errors)
flash[:error] = errors.full_messages.join(', ')
render :new
end
end
Smell interpretation mistakes lead to inappropriate refactoring that degrades code quality while attempting to satisfy Reek requirements. Common misinterpretations include treating all long methods as problematic and misunderstanding feature envy detection.
# Problematic: Misinterpreting FeatureEnvy
class Order
def calculate_total
# Reek reports FeatureEnvy because this method uses @line_items extensively
@line_items.sum { |item| item.price * item.quantity } +
@line_items.sum { |item| item.tax_amount } +
@line_items.sum(&:shipping_cost)
end
end
# Wrong solution: Moving logic to LineItem
class LineItem
def contribute_to_total
price * quantity + tax_amount + shipping_cost
end
end
class Order
def calculate_total
@line_items.sum(&:contribute_to_total)
end
end
# Better solution: Understanding the smell context
class Order
def calculate_total
subtotal + total_tax + total_shipping
end
private
def subtotal
@line_items.sum { |item| item.price * item.quantity }
end
def total_tax
@line_items.sum(&:tax_amount)
end
def total_shipping
@line_items.sum(&:shipping_cost)
end
end
Legacy code analysis requires different strategies than greenfield development. Applying modern quality standards to existing codebases without considering evolutionary approaches leads to analysis paralysis and resistance to quality improvements.
class LegacyAnalysisStrategy
def analyze_legacy_system(base_path)
# Establish baseline without failing builds
current_state = capture_current_quality_state(base_path)
establish_quality_baseline(current_state)
# Identify improvement opportunities
improvement_candidates = identify_low_hanging_fruit(current_state)
technical_debt_hotspots = find_high_change_files(base_path)
generate_improvement_roadmap(improvement_candidates, technical_debt_hotspots)
end
private
def establish_quality_baseline(current_state)
baseline_config = {
'detectors' => {}
}
# Set thresholds based on current worst cases plus buffer
current_state[:detector_stats].each do |detector, stats|
baseline_config['detectors'][detector] = {
'max_allowed_violations' => stats[:count] * 1.1
}
end
File.write('.reek_baseline.yml', baseline_config.to_yaml)
end
def identify_low_hanging_fruit(current_state)
# Focus on files with recent changes and fixable smells
current_state[:files].select do |file_analysis|
has_recent_changes?(file_analysis[:file]) &&
has_easily_fixable_smells?(file_analysis[:smells])
end
end
def has_easily_fixable_smells?(smells)
easily_fixed = %w[LongParameterList DuplicateMethodCall NestedIterators]
smells.any? { |smell| easily_fixed.include?(smell[:type]) }
end
end
Performance degradation in large codebases occurs when analysis configurations become inefficient or when file patterns trigger expensive detection algorithms. Performance issues compound in continuous integration environments where analysis time directly impacts development velocity.
class PerformanceOptimizer
def optimize_analysis_config(repo_path)
# Identify expensive detectors through profiling
detector_performance = profile_detectors(repo_path)
file_size_distribution = analyze_file_sizes(repo_path)
optimization_recommendations = []
# Recommend disabling expensive detectors on large files
expensive_detectors = detector_performance
.select { |_, time| time > 100 } # ms per file
.keys
if expensive_detectors.any? && file_size_distribution[:large_files].any?
optimization_recommendations << {
type: :detector_exclusion,
detectors: expensive_detectors,
file_pattern: file_size_distribution[:large_files],
reason: 'Performance optimization for large files'
}
end
# Recommend excluding auto-generated files
generated_patterns = detect_generated_file_patterns(repo_path)
if generated_patterns.any?
optimization_recommendations << {
type: :directory_exclusion,
patterns: generated_patterns,
reason: 'Skip analysis of generated code'
}
end
apply_optimizations(optimization_recommendations)
end
private
def profile_detectors(repo_path)
sample_files = Dir.glob("#{repo_path}/**/*.rb").sample(20)
detector_times = Hash.new { |h, k| h[k] = [] }
sample_files.each do |file|
source = Reek::Source.from_file(file)
examiner = Reek::Examiner.new(source)
examiner.configuration.smell_detectors.each do |detector_class|
time = Benchmark.realtime do
detector = detector_class.new(examiner.configuration)
detector.run(examiner.context)
end
detector_times[detector_class.smell_type] << time * 1000 # Convert to ms
end
end
# Calculate average times
detector_times.transform_values { |times| times.sum / times.length }
end
end
Team adoption resistance stems from perceived workflow disruption and disagreement about code quality standards. Successful adoption requires gradual introduction, team buy-in, and clear communication about quality goals versus development productivity.
class TeamAdoptionStrategy
def implement_gradual_adoption(team_config)
phases = [
{
name: 'Awareness Phase',
duration: '2 weeks',
actions: [
'Install Reek locally for interested developers',
'Run analysis on small modules only',
'Share interesting findings in team meetings'
],
success_criteria: 'Team understands Reek capabilities'
},
{
name: 'Experiment Phase',
duration: '4 weeks',
actions: [
'Add Reek to CI without failing builds',
'Establish quality baseline',
'Create team coding standards document'
],
success_criteria: 'Baseline established, no workflow disruption'
},
{
name: 'Enforcement Phase',
duration: 'Ongoing',
actions: [
'Enable quality gates for new code only',
'Regular quality review meetings',
'Incremental improvement goals'
],
success_criteria: 'Quality gates integrated into workflow'
}
]
execute_adoption_phases(phases, team_config)
end
private
def execute_adoption_phases(phases, config)
phases.each do |phase|
puts "Starting #{phase[:name]}"
phase[:actions].each do |action|
puts " #{action}"
# Implementation depends on team infrastructure
end
wait_for_phase_completion(phase, config)
end
end
end
Reference
Core Classes and Methods
Class | Purpose | Key Methods |
---|---|---|
Reek::Examiner |
Main analysis interface | #initialize(source) , #smells , #context |
Reek::Source |
Code source abstraction | .from_file(path) , .from_s(string) , #origin |
Reek::SmellWarning |
Individual smell result | #message , #smell_type , #lines , #context |
Reek::Configuration::AppConfiguration |
Configuration management | #smell_detectors , #directory_directives |
Reek::SmellDetector |
Base detector class | #run(context) , #sniff(context) , #config |
Smell Detector Types
Smell Type | Description | Default Threshold | Configurable Options |
---|---|---|---|
LongMethod |
Methods with too many statements | 15 statements | max_statements |
LongParameterList |
Methods with excessive parameters | 4 parameters | max_params |
FeatureEnvy |
Methods using external objects excessively | N/A | enabled , exclude |
DataClump |
Same group of parameters across methods | 3 occurrences | max_copies , min_clump_size |
LargeClass |
Classes with too many methods | 25 methods | max_methods |
DuplicateMethodCall |
Repeated identical method calls | 2 calls | max_calls , allow_calls |
NestedIterators |
Blocks within blocks | 2 levels | max_allowed_nesting |
ComplexityScore |
High cyclomatic complexity | 8 score | max_score |
UtilityFunction |
Public methods not using instance variables | N/A | public_methods_only |
IrresponsibleModule |
Classes/modules without comments | N/A | enabled |
Configuration File Structure
# Global detector settings
detectors:
DetectorName:
enabled: true|false
max_statements: integer
exclude:
- 'ClassName#method_name'
- 'ModuleName#'
# Directory-specific overrides
directories:
"lib/":
detectors:
LongMethod:
max_statements: 10
"spec/":
detectors:
UtilityFunction:
enabled: false
# File exclusions
exclude_paths:
- "vendor/"
- "db/migrate/"
- "*.generated.rb"
Command Line Options
Option | Argument | Description |
---|---|---|
--format |
text , json , xml , yaml , html |
Output format |
--config |
path/to/config.yml |
Configuration file path |
--smell |
SmellType |
Report only specific smell type |
--no-documentation-url |
N/A | Omit documentation URLs from output |
--sort-by |
smelliness , file |
Sort order for results |
--single-line |
N/A | Show one smell per line |
--no-progress |
N/A | Disable progress indicator |
--force-exclusion |
N/A | Exclude files even if explicitly specified |
--stdin |
N/A | Read source code from STDIN |
Exit Codes
Code | Meaning |
---|---|
0 |
No smells detected or analysis successful |
2 |
Smells detected (configurable threshold) |
1 |
Application error (invalid options, syntax errors) |
Inline Comment Directives
# Disable all smells for method
# :reek:all
def problematic_method
end
# Disable specific smell types
# :reek:LongMethod:FeatureEnvy
def complex_method
end
# Disable for entire class
class LegacyClass
# :reek:all
end
Integration Patterns
Integration | Implementation | Configuration |
---|---|---|
Rake Task | require 'reek/rake/task' |
Reek::Rake::Task.new |
Git Hook | Shell script with reek --format json |
Exit code checking |
CI/CD | Docker container or gem installation | Quality gate thresholds |
Editor | Language server protocol integration | Real-time feedback |
Metrics | Programmatic API with custom reporting | Trend analysis |