rails-philosophy — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited rails-philosophy (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
The foundational principles behind this style of Rails development, distilled from 37signals/Basecamp's approach.
"We aim to write code that is a pleasure to read, and we have a lot of opinions about how to do it well. We care about how code reads, how code looks, and how code makes you feel when you read it."
The Belief: Rails provides everything you need. Resist the urge to add layers of abstraction: no service objects wrapping simple operations, no repository pattern over ActiveRecord, no CQRS for typical apps, no dependency injection containers.
# Over-engineered
class CreateCardService
def initialize(board, params, current_user)
@board = board
@params = params
@current_user = current_user
end
def call
Card.create!(@params.merge(board: @board, creator: @current_user))
end
end
# Just use Rails
@card = @board.cards.create!(card_params.merge(creator: Current.user))The Test: If you're adding an abstraction, ask: "Does Rails already solve this?" Usually, yes.
The Belief: ActiveRecord models aren't just database wrappers — they ARE your domain. Business logic lives in models with intention-revealing public APIs (card.gild, card.close); controllers invoke model methods, they don't orchestrate operations. See [[rails-controllers]] Pattern 8.
The Belief: Every operation can be modeled as CRUD on a resource. This isn't limiting — it's clarifying. State changes become resources: POST /cards/:id/close → POST /cards/:id/closure; reopen → DELETE /cards/:id/closure.
Why It Works: consistent mental model, standard HTTP semantics, easier caching (resources have URLs), clear controller responsibilities. The routing pattern lives in [[rails-controllers]] Pattern 3.
The Belief: Composition through concerns is more natural in Rails than service objects. Concerns extend the class rather than wrap it — no indirection, the behavior IS on the model, and testing means testing the model directly. Shared behavior goes in app/models/concerns/ (Eventable, Searchable); model-specific behavior in namespaced concerns (Card::Closeable). Structure and template-method patterns: [[rails-models]].
The Belief: You probably don't need Redis. Solid Queue for jobs, Solid Cache for caching, Solid Cable for WebSockets; SQLite for small deployments, MySQL for scale.
Why It Works: one less service to operate, transactions across jobs and data, ACID guarantees. The trade-off — slightly higher pub/sub latency for simpler operations — is right for most apps. Setup: [[rails-project-setup]].
The Belief: HTML over the wire beats JSON APIs for most web applications. Turbo Drive for navigation, Frames for partial updates, Streams for real-time, Stimulus for sprinkles. No SPA.
Why It Works: less JavaScript to maintain, server controls the state, progressive enhancement built-in, faster initial render. See [[rails-turbo]] and [[rails-stimulus]].
The Belief: Follow Rails conventions even when they feel "limiting." When to break convention: almost never. If you're fighting Rails, you're probably wrong.
The Belief: Background jobs are just async method calls. The job class is a shell that calls a model method; *_later enqueues, *_now is the synchronous version. See [[rails-jobs]] Patterns 1 and 3.
The Belief: URL-based tenancy (/123456/boards/... + Current.account) is simpler than subdomains and just as effective: simpler local development, no wildcard SSL, easier testing, works behind load balancers. Implementation: [[rails-multi-tenancy]].
The Belief: Code is read far more often than written. Expanded conditionals over guard clauses, methods ordered by invocation flow, intention-revealing names, no metaprogramming for its own sake.
# Clever but hard to read
def process
return unless valid?
return if processed?
do_processing
end
# Clear and readable
def process
if valid? && !processed?
do_processing
end
endWhen facing a design choice, ask in order:
| Anti-Pattern | What to Do Instead |
|---|---|
| Service objects for simple operations | Direct model calls |
| Repository pattern | ActiveRecord directly |
| Presenters/Decorators everywhere | Helpers and partials |
| Form objects for simple forms | Strong parameters |
| Command pattern | Model methods |
| Event sourcing | Simple callbacks |
| Microservices | Monolith (really) |
| Redis for everything | Solid Stack |
| SPA frontend | Hotwire |
| GraphQL | REST/JSON/Turbo |
These principles aren't religious doctrine. Break them for:
But these should be rare. Most apps don't need them.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.