agentleFS
mickzijdelmickzijdel/rails-toolkit/skills/rails-audit/SKILL.md

rails-audit

Skill2 starsChanged 8 days agoMITView on GitHub

Use to audit or health-check an existing or inherited Rails app: onboarding to a legacy codebase, a tech-debt assessment, a pre-engagement code review, or \"review my Rails app\".

  • Reads credentials

2 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

3 rules. About 0 are common, 2 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    When a section hands off to another skill (shown as "→ [[skill-name]]"), run only the quick detection here and note the handoff.

    line 31
  2. Distinctive:

    If those two numbers are drastically different, indexes have likely been overlooked → 🟡.

    line 156
  3. Very specific:

    Reporting from memory instead of running the commands: Every finding must cite a real grep hit / scanner line / file:line.

    line 252

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Rails App Audit

Overview

A top-level health-check for an existing Rails app — inheriting a legacy codebase, onboarding to an unfamiliar project, or doing a pre-engagement review. This skill is the entry point for reviewing an app, the counterpart to [[rails-core]] which is the entry point for writing one.

It orchestrates: it owns the broad health-check items nothing else covers (version pinning, dependency CVEs, exposed secrets, seeds, tech-debt) and hands off the deep dives to the specialist skills ([[rails-database-performance]], [[rails-security]], [[rails-performance]], [[rails-testing]], [[rails-upgrade]]). Do not re-derive what those skills already do — run the cheap detection here, then delegate the fix.

The audit ends in a written, severity-ranked report (see Producing the Report).

How to Run This Audit

  1. Confirm you are at the app root (Gemfile, app/, config/ present).
  2. Work through every numbered section below — do not stop early.
  3. For each finding, record three things:
    • Severity — 🔴 high (security / data loss / broken in prod), 🟡 medium (tech debt, performance, missing safety net), 🟢 low (polish, style, docs).
    • Location — file:line (the audit must point at real code, not generalities).
    • Fix — the concrete remediation, and which skill owns the deep fix if it's a delegated area.
  4. When a section hands off to another skill (shown as "→ [[skill-name]]"), run only the quick detection here and note the handoff; the named skill carries the authoritative checklist.

1. Ruby & Rails Version Pinning

A shared, explicit version is the baseline for reproducible builds. Check the version is pinned and still supported.

cat .ruby-version 2>/dev/null || echo "MISSING .ruby-version"
grep -nE '^\s*ruby\s+["'\'']' Gemfile
grep -nE '^\s*gem\s+["'\'']rails["'\'']' Gemfile
ruby -v; bin/rails -v 2>/dev/null
  • No .ruby-version → 🟡 add one so every dev/CI uses the same interpreter.
  • Rails not locked to a specific version (gem "rails" with no version, or a loose >=) → 🟡 a bundle update can silently jump majors. Lock it.
  • EOL Ruby or Rails → 🔴/🟡. Check the running version against the support schedule. An upgrade is its own project — hand off to [[rails-upgrade]] (do not hand-roll the version bump).

2. Gemfile Hygiene

head -1 Gemfile                                          # source must be HTTPS
grep -nE 'cooldown' Gemfile .bundle/config 2>/dev/null   # supply-chain cooldown set?
grep -A1 'BUNDLED WITH' Gemfile.lock                     # Bundler >= 4.0.13? (older ignores cooldown)
grep -nE 'group\s+:' Gemfile                             # dev/test gems grouped out of production?
  • source 'http://...' (not HTTPS) → 🔴.
  • No supply-chain cooldown → 🟡. Recommend source "https://rubygems.org", cooldown: 3 so freshly-hijacked gem releases can't resolve for 3 days. Rationale and scope rules: [[rails-project-setup]].
  • Cooldown set but BUNDLED WITH below 4.0.13 → 🟡. Older Bundler silently ignores cooldown, so the protection isn't actually on; recommend pinning an explicit version, e.g. bundle update --bundler=4.0.13 (a bare --bundler jumps to the newest release, itself bypassing any cooldown).
  • Dev/test gems not grouped → 🟡. Wrap development- and test-only gems (rspec-rails, capybara, debug, factory_bot, brakeman, rubocop) in group :development, :test do … end, so a production deploy run with BUNDLE_WITHOUT=development:test (or bundle install --without development test) neither installs nor loads them: smaller image, faster boot, smaller attack surface.
  • Undocumented/obscure gems → 🟢 add a one-line comment for the next maintainer.

Don't flag missing per-gem version pins. With a committed Gemfile.lock (and bundle install --deployment / BUNDLE_FROZEN=true on deploy), every version is already frozen; loose constraints in the Gemfile are fine.


3. Dependency Vulnerabilities & Static Security Scan

Two free, fast scanners. Run both.

gem install bundler-audit 2>/dev/null; bundle exec bundler-audit check --update 2>/dev/null || bundler-audit check --update
gem install brakeman 2>/dev/null; bundle exec brakeman -q -A 2>/dev/null || brakeman -q -A
  • bundler-audit flags Gemfile.lock gems with known CVEs and the patched version → 🔴/🟡 by criticality. Record each advisory + the upgrade target.
  • brakeman flags code-level issues (SQLi, mass-assignment, unsafe redirects, XSS). Triage each warning; confidence High first → 🔴.
  • For the design of auth/authorization behind these findings, hand off to [[rails-security]].

4. Exposed Secrets

Secrets in the repo (or in git history) are the highest-impact, easiest-to-miss finding.

grep -rinE '(password|secret|api[_-]?key|access[_-]?key|token)\s*[:=]\s*["'\''][^"'\'' ]{6,}' config/ app/ lib/ 2>/dev/null
git log --oneline -- config/master.key config/credentials.yml.enc 2>/dev/null
grep -nE 'config/master.key|config/credentials.*\.key|\.env' .gitignore 2>/dev/null
  • Literal credentials in config/environments/production.rb, initializers, or *.yml → 🔴. Move to encrypted credentials (bin/rails credentials:edit) or ENV.
  • config/master.key or any .env NOT gitignored → 🔴. Modern Rails stores secrets in config/credentials.yml.enc (encrypted, committable) decrypted by master.key (which must stay out of git).
  • A secret already committed → 🔴. Rotate it first (assume it's compromised), then purge history. See [[rails-security]] for what to rotate and how to scope it.

5. Test Health

A suite that nobody has touched in a year is a liability, not a safety net.

git log -1 --pretty=format:'%ci  %s' -- test/ spec/ 2>/dev/null; echo
ls -d test spec 2>/dev/null
grep -c . <(find app -name '*.rb') ; find test spec -name '*_test.rb' -o -name '*_spec.rb' 2>/dev/null | wc -l
  • Tests last touched long before app code → 🟡 likely abandoned after an upgrade broke them, or never written. Note the staleness.
  • No test//spec/ at all, or near-empty → 🔴 no regression safety net.
  • For coverage strategy, fixtures vs factories, system tests, and parallelization → hand off to [[rails-testing]]. To quantify, run the suite and a coverage tool (SimpleCov) and report the percentage.

6. Seed Data

Developers must be able to stand up a working local DB without a production dump.

test -s db/seeds.rb && echo "seeds.rb present ($(wc -l < db/seeds.rb) lines)" || echo "MISSING/empty db/seeds.rb"
grep -nE 'find_or_create_by|destroy_all|delete_all' db/seeds.rb 2>/dev/null
  • Missing or empty db/seeds.rb → 🟡 onboarding requires copying prod data (privacy + size risk).
  • Seeds that aren't idempotent (plain create! that dupes on re-run) → 🟢 prefer find_or_create_by so seeds can be loaded, cleared, and re-loaded cleanly.

7. Lint & Style Drift

bundle exec rubocop --format offenses 2>/dev/null | tail -20 || bundle exec standardrb 2>/dev/null | tail -20
ls .rubocop.yml .standard.yml 2>/dev/null
  • No linter configured → 🟢 add rubocop-rails or standard for consistent style.
  • Configured but thousands of offenses → 🟡 style has drifted; consider rubocop --auto-gen-config to baseline, then fix incrementally.
  • For the project's actual conventions (method ordering, conditionals, REST routing, naming) → [[rails-style]].

8. Schema & Index Quick Heuristic

A fast smell test before the full schema audit. Foreign keys without indexes are the most common Rails performance bug.

echo "FK-ish columns:"; grep -E '_id' db/schema.rb | grep -v 'add_index\|t\.index' | wc -l
echo "indexes:";        grep -E 'add_index|t\.index' db/schema.rb | wc -l
  • If those two numbers are drastically different, indexes have likely been overlooked → 🟡.
  • This is only a heuristic. The authoritative index/query/N+1-at-the-DB audit (polymorphic, scope, status, auth, counter-cache, pagination) lives in [[rails-database-performance]] — run it next.

9. Performance & N+1 Quick Scan

grep -rnE '\.(each|map)\b' app/views/ app/controllers/ 2>/dev/null | head -20   # loops that may trigger per-row queries
grep -rnE '\.includes\(|\.preload\(|\.eager_load\(' app/ 2>/dev/null | wc -l      # is eager loading used at all?
  • Views/controllers iterating an association with no matching includes/preload → 🟡 probable N+1. Spot-check the dev log (or add prosopite — near-zero false positives, unlike bullet) to confirm.
  • For caching, ETags, batching, and the full N+1 treatment → [[rails-performance]].

Production footguns — cheap greps for configurations that cause outages, not just slowness:

grep -nE 'puma_worker_killer|unicorn-worker-killer' Gemfile                      # memory-threshold worker killers
grep -rnE 'send_data|send_file' app/controllers/ | head                          # large files served from the web process
grep -rnE 'queue_adapter\s*=\s*:inline|Resque\.inline' config/ | grep -v development  # inline jobs outside dev
grep -rnE 'timeout' config/initializers/*elastic* config/initializers/*search* config/initializers/*redis* 2>/dev/null  # client timeouts set at all?
  • Worker-killer gem present → 🟡 hidden ENV thresholds silently turn the app into a restart loop (processes recycled after a handful of requests); removal has halved response times in practice. Modern instances have the RAM — fix leaks instead. → [[rails-performance]]
  • send_data of generated content (CSV/zip/PDF) → 🟡 memory bloat + blocked threads; move generation to a job, serve from storage. → [[rails-jobs]]
  • :inline job adapter in test/production config → 🟡 → [[rails-testing]]
  • No timeout configured on search/cache clients → 🟡 default 30s timeouts let a degraded Elasticsearch/Redis hold every Puma thread and take the app down. → [[rails-performance]]

10. Architecture Smells

Cheap size heuristics surface the files most likely to hide problems.

echo "Fattest models:";      wc -l app/models/**/*.rb 2>/dev/null | sort -rn | head -10
echo "Fattest controllers:"; wc -l app/controllers/**/*.rb 2>/dev/null | sort -rn | head -10
echo "Service-object sprawl:"; ls app/services 2>/dev/null | wc -l
grep -rnE '^\s*def ' app/controllers/ 2>/dev/null | grep -vE 'index|show|new|create|edit|update|destroy' | head   # non-REST actions
echo "ApplicationController methods:"; grep -cE '^\s*def ' app/controllers/application_controller.rb 2>/dev/null   # dumping-ground smell
  • Controllers fat with business logic / many non-REST actions → 🟡 push behaviour into models, extract concerns. See [[rails-controllers]] and [[rails-style]] (REST routing).
  • ApplicationController is a dumping ground (many methods — say 8+ — especially unrelated ones not backing a before_action, with no grouping) → 🟡. It accretes current-user checks, default redirects, env (production?/staging?) checks, www-canonicalisation, response-header setters, etc., that nobody dares move. Group related methods under comments, push any method used by only 1–2 controllers into a concern included just there, and extract each cluster of 2+ related methods into a named concern (e.g. include Akamai). The base controller should read as a manifest of includes. → [[rails-controllers]] (Pattern 1).
  • God models (many hundreds of lines) → 🟡 extract concerns / POROs. See [[rails-models]].
  • A large app/services/ tree → 🟡 in 37signals-style Rails this is usually logic that belongs on rich models, not a service layer. Weigh against [[rails-philosophy]] before recommending more of it.

11. Technical-Debt Inventory

grep -rinE 'todo|fixme|hack|xxx|deprecated' app/ lib/ 2>/dev/null | wc -l
grep -rinE 'todo|fixme|hack|xxx' app/ lib/ 2>/dev/null | head -40
grep -rnE '^\s*#\s*[a-z].*\b(def|end|do|=)\b' app/ lib/ 2>/dev/null | head   # commented-out code
  • Inventory every TODO/FIXME/HACK → 🟢/🟡. Decide per item: do it, ticket it, or delete the stale note.
  • Large blocks of commented-out code → 🟢 delete; git is the history.

Producing the Report

End the audit with a written report, grouped by severity (🔴 → 🟡 → 🟢). Each item:

### 🔴 <short title>
- **Where:** path/to/file.rb:42  (or: Gemfile, db/schema.rb)
- **Finding:** what's wrong, with the evidence (the grep hit / scanner line).
- **Impact:** why it matters (security / perf / maintainability / onboarding).
- **Fix:** the concrete change, and **→ which skill** to use for the deep fix.

Close with a one-paragraph summary: overall health, the top 3 things to fix first, and the recommended order (security & exposed secrets before refactors).

Quick Reference

#CheckCommand / signalDeep-dive skill
1Version pinning.ruby-version, locked gem "rails", EOL?[[rails-upgrade]]
2Gemfile hygieneHTTPS source, cooldown, dev/test groups—
3Vulnerabilitiesbundler-audit, brakeman[[rails-security]]
4Exposed secretsgrep config/, master.key gitignored[[rails-security]]
5Test healthgit log -1 -- test/ spec/, coverage[[rails-testing]]
6Seed datadb/seeds.rb present & idempotent—
7Lint/stylerubocop / standard offenses[[rails-style]]
8Schema/indexFK-count vs index-count[[rails-database-performance]]
9N+1/perfloops without includes[[rails-performance]]
10Architecturefat models/controllers, service sprawl[[rails-philosophy]], [[rails-models]], [[rails-controllers]]
11Tech debtTODO/FIXME, dead code—

Common Mistakes

  • Reporting from memory instead of running the commands. Every finding must cite a real grep hit / scanner line / file:line. No evidence, no finding.
  • Re-deriving a deep-dive skill inline. Sections 8–10 are detection heuristics only; the authoritative checklist lives in the linked skill. Delegate, don't duplicate.
  • Findings without severity or location. A report the user can't triage or act on is noise.
  • Burying the lede. Lead with 🔴 security/secrets; refactors and style come last.
  • Treating an EOL version as a quick fix. Version upgrades are a project — route to [[rails-upgrade]], don't bump in place.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.