rails-philosophy
Skill2 starsChanged 8 days agoMITView on GitHub
Core philosophies, design choices, and tacit knowledge underpinning 37signals-style Rails development
In this repository24 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- rails-activestorageSkill
- rails-apiSkill
- rails-auditSkill
- rails-clean-test-outputSkill
- rails-controllersSkill
- rails-coreSkill
- rails-database-performanceSkill
- rails-i18nSkill
- rails-jobsSkill
- rails-loggingSkill
- rails-mailersSkill
- rails-migrationsSkill
- rails-modelsSkill
- rails-multi-tenancySkill
- rails-performanceSkill
- rails-project-setupSkill
- rails-securitySkill
- rails-stimulusSkill
- rails-styleSkill
- rails-testingSkill
- rails-turboSkill
- rails-upgradeSkill
- rails-viewcomponentsSkill
The file
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 9 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈9
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
- Distinctive: line 38
The Test: If you're adding an abstraction, ask: "Does Rails already solve this?" Usually, yes.
- Very specific: line 46
The Belief: Every operation can be modeled as CRUD on a resource.
- Distinctive: line 68
The Belief: Follow Rails conventions even when they feel "limiting." When to break convention: almost never.
- Distinctive: line 102
When facing a design choice, ask in order:
- Distinctive: line 104
Does Rails already solve this?: If yes, use Rails. Don't add abstractions.
- Distinctive: line 108
Am I adding accidental complexity?: If the abstraction doesn't pay for itself immediately, don't add it.
- Distinctive: line 144
"Look for similar code.": Before writing new code, grep the codebase for similar patterns.
- Distinctive: line 145
"We love discussing code.": When uncertain, ask — a PR is a great place for the discussion.
- Distinctive: line 146
"How code makes you feel.": If code feels wrong, it probably is.
- Distinctive: line 147
The high bar: Every line of code is reviewed.
≈ “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.
Files
Rails Philosophy
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 Principles
1. Vanilla Rails Is Plenty
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.
2. The Model Is the Domain
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.
3. REST Everything
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.
4. Concerns Over Services
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]].
5. Database-Backed Everything (Solid Stack)
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]].
6. Server-Rendered First (Hotwire)
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]].
7. Convention Over Configuration (Really)
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.
8. Shallow Jobs, Rich Models
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.
9. Multi-Tenancy Without Complexity
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]].
10. Readability Over Cleverness
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
endDecision Framework
When facing a design choice, ask in order:
- Does Rails already solve this? If yes, use Rails. Don't add abstractions.
- Can this be a concern? Shared behavior → concern. Not a service.
- Can this be a resource? Non-CRUD action → model it as a resource with CRUD.
- Where does this logic belong? Data manipulation → Model. HTTP → Controller. Presentation → View/Helper. Async → Job (that calls a model).
- Am I adding accidental complexity? If the abstraction doesn't pay for itself immediately, don't add it.
Anti-Patterns to Avoid
| 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 |
When Exceptions Are OK
These principles aren't religious doctrine. Break them for:
- Form Objects — complex multi-model forms with custom validation
- Query Objects — genuinely complex reporting queries
- Service Objects — coordinating external services (payments, etc.)
- Presenters — complex view logic spanning multiple models
But these should be rare. Most apps don't need them.
The Tacit Knowledge
- "Look for similar code." Before writing new code, grep the codebase for similar patterns. Consistency matters more than theoretical correctness.
- "We love discussing code." When uncertain, ask — a PR is a great place for the discussion.
- "How code makes you feel." If code feels wrong, it probably is. Trust your instincts, then articulate why.
- The high bar. Every line of code is reviewed. Quality isn't optional.
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
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.

