troessner/reek
Code smell detector for Ruby
Healthy across all four use cases
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 1w ago
- ✓7 active contributors
- ✓MIT licensed
Show 3 more →Show less
- ✓CI configured
- ✓Tests present
- ⚠Single-maintainer risk — top contributor 86% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/troessner/reek)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/troessner/reek on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: troessner/reek
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/troessner/reek shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- Last commit 1w ago
- 7 active contributors
- MIT licensed
- CI configured
- Tests present
- ⚠ Single-maintainer risk — top contributor 86% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live troessner/reek
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/troessner/reek.
What it runs against: a local clone of troessner/reek — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in troessner/reek | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 38 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of troessner/reek. If you don't
# have one yet, run these first:
#
# git clone https://github.com/troessner/reek.git
# cd reek
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of troessner/reek and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "troessner/reek(\\.git)?\\b" \\
&& ok "origin remote is troessner/reek" \\
|| miss "origin remote is not troessner/reek (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "bin/reek" \\
&& ok "bin/reek" \\
|| miss "missing critical file: bin/reek"
test -f "lib/reek/examiner.rb" \\
&& ok "lib/reek/examiner.rb" \\
|| miss "missing critical file: lib/reek/examiner.rb"
test -f "lib/reek/configuration/configuration.rb" \\
&& ok "lib/reek/configuration/configuration.rb" \\
|| miss "missing critical file: lib/reek/configuration/configuration.rb"
test -f "lib/reek/report.rb" \\
&& ok "lib/reek/report.rb" \\
|| miss "missing critical file: lib/reek/report.rb"
test -f "lib/reek/smells" \\
&& ok "lib/reek/smells" \\
|| miss "missing critical file: lib/reek/smells"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 38 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~8d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/troessner/reek"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Reek is a static analysis tool that detects code smells in Ruby source code—patterns like uncommunicative names, duplicate method calls, long parameter lists, and feature envy. It parses Ruby AST and applies 25+ configurable detectors to identify maintainability issues before they become technical debt. Monolithic CLI tool: bin/reek is the entry point; lib/ likely contains detectors (one per smell type), a configuration loader (references .reek.yml), an AST parser, and formatters for different output modes (JSON, XML, Code Climate via bin/code_climate_reek). Extensive documentation in docs/ mirrors the detector names.
👥Who it's for
Ruby developers and teams who want automated feedback on code quality during development; CI/CD pipelines that enforce code smell thresholds; Rails projects and gem maintainers seeking to improve readability and reduce cognitive load in reviews.
🌱Maturity & risk
Production-ready and actively maintained. The project shows 24M+ downloads, comprehensive test coverage (Gherkin feature specs in docs/), GitHub Actions CI/CD configured in .github/workflows/ruby.yml, and recent commits. Version history in CHANGELOG.md and Reek 4→5 migration docs indicate long-term evolution.
Low risk for most users. Single maintainer (troessner) is a known concern for long-term maintenance, but the tool is stable with broad ecosystem adoption. Dependencies are managed via Gemfile and Dependabot is configured. Primary risk is Ruby version constraints—verify your target Ruby version compatibility against .ruby-version expectations.
Active areas of work
Actively developed with CI/CD checks on PRs. Dependabot is configured for dependency updates. The repo maintains detailed migration guides (Reek-4-to-Reek-5-migration.md) and recent CHANGELOG entries suggest ongoing detector improvements and configuration enhancements.
🚀Get running
git clone https://github.com/troessner/reek.git && cd reek && bundle install && bundle exec bin/reek --help
Daily commands: bundle exec reek [options] [dir_or_source_file]* (default: current directory). For CI: bundle exec reek --format=json > report.json. For development: bundle exec rake to run full test suite.
🗺️Map of the codebase
bin/reek— Entry point for the CLI tool; essential for understanding how users invoke reek and the command-line argument flow.lib/reek/examiner.rb— Core analyzer that orchestrates smell detection; all other detectors funnel through this class.lib/reek/configuration/configuration.rb— Handles configuration loading and merging; critical for understanding how detection rules are applied across the codebase.lib/reek/report.rb— Generates output in multiple formats (JSON, YAML, text); required for understanding result reporting.lib/reek/smells— Directory containing all smell detector classes; the heart of the detection engine with 30+ detector implementations.lib/reek/tree_dresser.rb— AST augmentation layer that decorates parsed Ruby code with smell-detection hooks; bridges parser and detectors.docs/How-To-Write-New-Detectors.md— Primary guide for extending reek with new detectors; essential onboarding for contributors adding smell types.
🧩Components & responsibilities
- Examiner (Ruby, ruby_parser) — Orchestrates analysis: parses source, invokes detectors, collects violations
- Failure mode: Parse error on invalid Ruby → returns exception; handles gracefully with line-level reporting
- TreeDresser (Ruby, AST traversal) — Decorates AST with context (scope, variable lifecycle); enables detectors to query semantic info
- Failure mode: Incorrect context → false positives/negatives in smell detection; caught by test suite
- Smell Detectors (30+ classes) —
🛠️How to make changes
Add a New Code Smell Detector
- Create a new detector class inheriting from SmellDetector in lib/reek/smells/ (
lib/reek/smells/your_new_smell.rb) - Implement required methods: #smell_type, #contexts, and #sniff to detect violations (
lib/reek/smells/your_new_smell.rb) - Add configuration defaults to lib/reek/configuration/defaults.reek.yml (
lib/reek/configuration/defaults.reek.yml) - Create documentation page following the pattern in docs/Code-Smells.md (
docs/Your-New-Smell.md) - Write feature specs in features/ folder testing detection rules (
features/your_new_smell.feature) - Implement unit tests in spec/reek/smells/ (
spec/reek/smells/your_new_smell_spec.rb)
Add Support for a New Output Format
- Create a new formatter class in lib/reek/formatter/ (
lib/reek/formatter/your_format_formatter.rb) - Register the formatter in the formatter registry (typically in lib/reek/formatter.rb) (
lib/reek/formatter.rb) - Implement #format method to convert Violation objects to output string (
lib/reek/formatter/your_format_formatter.rb) - Add feature tests in features/reports/your_format.feature (
features/reports/your_format.feature)
Customize Detection Rules via Configuration
- Add configuration options to the detector's #configuration property in lib/reek/smells/ (
lib/reek/smells/your_smell.rb) - Add schema definition and defaults in lib/reek/configuration/defaults.reek.yml (
lib/reek/configuration/defaults.reek.yml) - User creates .reek.yml in project root with custom thresholds (
.reek.yml) - Add feature tests for the new configuration option (
features/configuration_files/your_option.feature)
🔧Why these technologies
- Ruby Parser (ruby_parser gem) — Parses Ruby source code into AST; foundational for all smell detection
- YAML for Configuration — Human-readable configuration; allows project-level smell thresholds and exclusions
- Cucumber + RSpec for Testing — Behavior-driven acceptance tests (Cucumber) verify CLI/configuration; unit tests (RSpec) validate detector logic
- JSON/YAML Reporting — Multiple output formats enable CI/CD integration and tool consumption (e.g., Code Climate)
⚖️Trade-offs already made
-
AST-based analysis rather than regex/pattern matching
- Why: Semantic correctness: AST understands scope, control flow, and context
- Consequence: More accurate detection but slower on large codebases; parser overhead per file
-
Configuration via YAML files rather than API-only
- Why: Developers can version control and customize detection per project
- Consequence: Configuration loading adds startup overhead; multiple config sources can cause confusion
-
Modular detector architecture (one class per smell type)
- Why: Loose coupling; easy to add/disable smells without touching core engine
- Consequence: Detectors may duplicate logic; requires developers to understand SmellDetector interface
-
TreeDresser layer decorating AST rather than modifying parser output
- Why: Decouples smell logic from parser; allows extending detection without fork
- Consequence: Extra abstraction layer; potential performance cost from tree traversal
🚫Non-goals (don't propose these)
- Real-time/incremental analysis (full file scan on each invocation)
- Automatic code correction (reports smells, does not fix them)
- Support for non-Ruby languages
- Integrated IDE refactoring UI (reports only; integration via plugins)
- Performance optimization for files >10K lines (designed for typical Ruby projects)
🪤Traps & gotchas
No hidden env vars required. Reek respects .reek.yml in the working directory or parent directories—ensure your config is correct or suppress false positives with # :reek:IgnoredSmell inline comments. The parser gem is sensitive to Ruby syntax; ensure your Ruby version matches the target codebase or detection may fail silently.
🏗️Architecture
💡Concepts to learn
- Code Smell — Reek's entire purpose is detecting code smells—surface-level patterns that suggest deeper design problems but are not bugs. Understanding what constitutes a smell vs. a style issue is critical to using Reek effectively.
- Abstract Syntax Tree (AST) — Reek parses Ruby source into an AST and walks it to detect patterns. Detectors examine AST nodes (method defs, variable assignments, conditionals). Understanding AST structure is essential for writing custom detectors.
- Visitor Pattern (Detector Architecture) — Each detector is a visitor that traverses the AST. The base Reek::SmellDetector class implements the traversal; subclasses override #examine(node) to inspect specific node types. This is the core extensibility mechanism.
- Feature Envy — One of Reek's signature detectors; identifies methods that use another object's data more than their own, indicating misplaced responsibility. Understanding this smell teaches dependency isolation principles.
- Configuration Suppression (Inline Comments) — Reek allows per-line smell suppression via # :reek:SmellName comments. This is essential for managing false positives without modifying .reek.yml globally and keeps code review intent visible.
- Output Formatters (JSON, XML, CLI, Code Climate) — Reek ships multiple output formats; --format=json for CI integration, Code Climate format for CodeClimate.com integration, and CLI for humans. Selecting the right formatter is critical for tool integration.
- Configuration Hierarchy (.reek.yml, CLI args, inline comments) — Reek resolves config from three sources with precedence: inline comments override file config, CLI args override file config. Understanding this hierarchy prevents confusion when detector behavior doesn't match expectations.
🔗Related repos
rubocop/rubocop— Complementary Ruby linter focused on style & formatting; often used alongside Reek for comprehensive code quality checksthoughtbot/brakeman— Security-focused static analyzer for Rails; covers vulnerability detection where Reek focuses on maintainability smellsflay/flay— Detects structural duplication in Ruby code; covers DRY violations that overlap with but complement Reek's duplicate detectionmetric_fu/metric_fu— Meta-analyzer aggregating multiple Ruby linters (Reek, Roodi, RuboCop, etc.) into unified reports for Rails projectswhitesmith/rubycritic— Web UI for combined RuboCop, Reek, and Flay output; useful for visualizing code smells across a codebase
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add integration tests for Code Climate reporter (bin/code_climate_reek)
The repo has a dedicated Code Climate reporter script (bin/code_climate_reek) but there's no evidence of dedicated tests for this integration format. Given that Code Climate is a popular CI/CD integration, adding tests would ensure the reporter output format remains compatible and complete. This is especially valuable since output format changes could break user integrations.
- [ ] Create spec/integration/code_climate_reporter_spec.rb with test fixtures
- [ ] Test JSON output format compliance with Code Climate engine spec
- [ ] Add tests for various smell types and ensure they map correctly to Code Climate format
- [ ] Add test case for edge cases (empty results, large result sets, special characters in file paths)
- [ ] Update docs/Code-Climate-Integration.md (or create it) if it doesn't exist to document the expected output format
Create RSpec matcher integration tests (docs/RSpec-matchers.md exists but lacks test coverage)
The repo documents RSpec matchers in docs/RSpec-matchers.md but this feature likely lacks comprehensive test coverage. RSpec matchers are a key integration point for Ruby developers using Reek in their test suites. Adding dedicated integration tests would ensure the matchers work reliably across RSpec versions and edge cases.
- [ ] Create spec/integration/rspec_matchers_spec.rb with comprehensive examples
- [ ] Test matchers against various code smell scenarios (single smell, multiple smells, no smells)
- [ ] Add tests for matcher chaining and negation (.to_not syntax)
- [ ] Test integration with current supported RSpec versions (list from Gemfile)
- [ ] Ensure all examples from docs/RSpec-matchers.md have corresponding passing tests
Add GitHub Actions workflow for testing against multiple Ruby versions with matrix strategy
The repo has .github/workflows/ruby.yml but likely tests against a limited set of Ruby versions. Given that Reek supports multiple Ruby versions (per the README), a matrix-based workflow would catch version-specific regressions early. This is critical for a linting tool since users run it across diverse Ruby environments.
- [ ] Review current .github/workflows/ruby.yml and identify tested Ruby versions
- [ ] Extend workflow with matrix strategy for Ruby 2.7, 3.0, 3.1, 3.2, 3.3 (adjust based on Gemfile's supported_versions)
- [ ] Add separate matrix jobs for bundler versions if applicable
- [ ] Add workflow step to report coverage per Ruby version
- [ ] Test that gem dependencies resolve correctly across matrix combinations
🌿Good first issues
- Add test coverage for the Configuration loader when .reek.yml includes invalid detector names—currently error handling is unclear. See lib/reek/configuration/ and add specs to features/
- Extend docs/Command-Line-Options.md with examples for each flag combination (--no-documentation, --format json, --stdin-filename). Current docs are sparse compared to --help output.
- Create a new detector for 'Verbose Boolean Logic' (e.g.,
if condition == true) as a learning exercise; follow docs/How-To-Write-New-Detectors.md, add 5 Gherkin scenarios in features/, and document it in docs/
⭐Top contributors
Click to expand
Top contributors
- @mvz — 86 commits
- @troessner — 6 commits
- @etagwerker — 2 commits
- @dependabot[bot] — 2 commits
- @le-santos — 2 commits
📝Recent commits
Click to expand
Recent commits
e38ae4d— Merge pull request #1834 from troessner/update-cucumber-11 (mvz)3abf7a2— Update development dependencies to allow cucumber version 11.0 (mvz)0ba3f88— Merge pull request #1833 from troessner/update-rubocop-1-86-0 (mvz)68b0675— Update rubocop to version 1.86.0 (mvz)4028ea0— Merge pull request #1832 from troessner/update-rubocop-1-85-0 (mvz)5ad875f— Correct Style/OneClassPerFile offenses in code climate reek runner (mvz)9be6588— Correct Style/ReduceToHash offense (mvz)654637c— Update rubocop to version 1.85.0 (mvz)06d172b— Merge pull request #1831 from troessner/update-rubocop (mvz)9b0f55c— Update rubocop to version 1.84.0 (mvz)
🔒Security observations
The Reek codebase shows generally good security practices (non-root Docker user, exclusion of dev dependencies), but has critical issues with outdated Ruby version and unversioned gem installations. The most significant concern is the use of Ruby 2.6.0 which is no longer maintained and may contain known vulnerabilities. Code smell detection tool itself presents low injection risk as it performs static analysis rather than dynamic code execution. Recommend prioritizing Ruby upgrade and gem version pinning in Dockerfile.
- High · Outdated Ruby Version in Docker Image —
Dockerfile, line 7. The Dockerfile uses Ruby 2.6.0-alpine, which reached end-of-life in 2020. This version no longer receives security patches and may contain known vulnerabilities. Fix: Update to a currently supported Ruby version (3.2 or later). Use 'FROM ruby:3.2-alpine' or newer to ensure security patches are available. - Medium · Missing Security Headers in YAML Configuration —
.reek.yml. The .reek.yml configuration file may not have security-focused defaults. No indication of secure configuration for code analysis output. Fix: Review configuration to ensure sensitive data patterns are not exposed in reports. Consider adding exclusions for test files and sensitive patterns. - Medium · Insecure Gem Installation Without Version Pinning —
Dockerfile, lines 22-24. The Dockerfile installs gems without specifying versions: 'gem install rake', 'gem install codeclimate-engine-rb'. This could lead to installing vulnerable versions. Fix: Pin gem versions explicitly: 'gem install rake VERSION' and specify exact versions in Gemfile with lock file (Gemfile.lock should be committed). - Medium · Running Container as Non-Root User But With Full App Access —
Dockerfile, lines 19-20. While the container runs as non-root user 'app' (UID 9000), the application directory and all its contents are owned by this user. No additional sandboxing or capabilities dropping is performed. Fix: Consider implementing additional security measures: use read-only file systems where possible, drop unnecessary Linux capabilities, and implement resource limits. - Low · Potentially Exposed Debug Information in Development Dependencies —
Dockerfile, lines 14-15. The Dockerfile uses '--without debugging development' flag, but build-essential and git are installed globally, which may not be necessary in production. Fix: Use multi-stage Docker builds to remove build tools from the final image. Keep only runtime dependencies in the final stage.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.