Apply 37signals/DHH Rails conventions to Ruby and Rails code. This skill provides comprehensive domain expertise extracted from analyzing production 37signals codebases (Fizzy/Campfire) and DHH's code review patterns.
Core Philosophy
"The best code is the code you don't write. The second best is the code that's obviously correct."
Vanilla Rails is plenty:
- - Rich domain models over service objects
- CRUD controllers over custom actions
- Concerns for horizontal code sharing
- Records as state instead of boolean columns
- Database-backed everything (no Redis)
- Build solutions before reaching for gems
What they deliberately avoid:
- - devise (custom ~150-line auth instead)
- pundit/cancancan (simple role checks in models)
- sidekiq (Solid Queue uses database)
- redis (database for everything)
- viewcomponent (partials work fine)
- GraphQL (REST with Turbo sufficient)
- factorybot (fixtures are simpler)
- rspec (Minitest ships with Rails)
- Tailwind (native CSS with layers)
Development Philosophy:
- - Ship, Validate, Refine - prototype-quality code to production to learn
- Fix root causes, not symptoms
- Write-time operations over read-time computations
- Database constraints over ActiveRecord validations
What are you working on?
- 1. Controllers - REST mapping, concerns, Turbo responses, API patterns
- Models - Concerns, state records, callbacks, scopes, POROs
- Views & Frontend - Turbo, Stimulus, CSS, partials
- Architecture - Routing, multi-tenancy, authentication, jobs, caching
- Testing - Minitest, fixtures, integration tests
- Gems & Dependencies - What to use vs avoid
- Code Review - Review code against DHH style
- General Guidance - Philosophy and conventions
Specify a number or describe your task.
models.md |
| 3, view, frontend, turbo, stimulus, css | frontend.md |
| 4, architecture, routing, auth, job, cache | architecture.md |
| 5, test, testing, minitest, fixture | testing.md |
| 6, gem, dependency, library | gems.md |
| 7, review | Read all references, then review code |
| 8, general task | Read relevant references based on context |
After reading relevant references, apply patterns to the user's code.
Naming Conventions
Verbs: card.close, card.gild, board.publish (not set_style methods)
Predicates: card.closed?, card.golden? (derived from presence of related record)
Concerns: Adjectives describing capability (Closeable, Publishable, Watchable)
Controllers: Nouns matching resources (Cards::ClosuresController)
Scopes:
- -
chronologically, reverse_chronologically, alphabetically, INLINECODE13 - INLINECODE14 (standard eager loading name)
- INLINECODE15 ,
sorted_by (parameterized) - INLINECODE17 ,
unassigned (business terms, not SQL-ish)
REST Mapping
Instead of custom actions, create new resources:
CODEBLOCK0
Ruby Syntax Preferences
CODEBLOCK1
Key Patterns
State as Records:
CODEBLOCK2
Current Attributes:
CODEBLOCK3
Authorization on Models:
class User < ApplicationRecord
def can_administer?(message)
message.creator == self || admin?
end
end
Domain Knowledge
All detailed patterns in references/:
Concerns, state records, callbacks, scopes, POROs, authorization, broadcasting |
|
frontend.md | Turbo Streams, Stimulus controllers, CSS layers, OKLCH colors, partials |
|
architecture.md | Routing, authentication, jobs, Current attributes, caching, database patterns |
|
testing.md | Minitest, fixtures, unit/integration/system tests, testing patterns |
|
gems.md | What they use vs avoid, decision framework, Gemfile examples |
Code follows DHH style when:
- - Controllers map to CRUD verbs on resources
- Models use concerns for horizontal behavior
- State is tracked via records, not booleans
- No unnecessary service objects or abstractions
- Database-backed solutions preferred over external services
- Tests use Minitest with fixtures
- Turbo/Stimulus for interactivity (no heavy JS frameworks)
- Native CSS with modern features (layers, OKLCH, nesting)
- Authorization logic lives on User model
- Jobs are shallow wrappers calling model methods
Based on The Unofficial 37signals/DHH Rails Style Guide by Marc Köhlbrugge, generated through deep analysis of 265 pull requests from the Fizzy codebase.
Important Disclaimers:
- - LLM-generated guide - may contain inaccuracies
- Code examples from Fizzy are licensed under the O'Saasy License
- Not affiliated with or endorsed by 37signals
将37signals/DHH的Rails惯例应用于Ruby和Rails代码。此技能提供从分析生产环境37signals代码库(Fizzy/Campfire)和DHH的代码审查模式中提取的全面领域专业知识。
核心理念
最好的代码是你没有写的代码。第二好的代码是明显正确的代码。
纯Rails已足够:
- - 富领域模型优于服务对象
- CRUD控制器优于自定义操作
- 使用Concerns实现水平代码共享
- 用记录而非布尔列表示状态
- 一切基于数据库(无需Redis)
- 在引入Gem之前先构建解决方案
他们刻意避免的内容:
- - devise(改用约150行的自定义认证)
- pundit/cancancan(在模型中进行简单的角色检查)
- sidekiq(Solid Queue使用数据库)
- redis(一切使用数据库)
- viewcomponent(局部视图即可)
- GraphQL(REST配合Turbo已足够)
- factorybot(fixture更简单)
- rspec(Minitest随Rails自带)
- Tailwind(使用带层级的原生CSS)
开发理念:
- - 发布、验证、优化——将原型质量代码投入生产以学习
- 修复根本原因,而非表面症状
- 写时操作优于读时计算
- 数据库约束优于ActiveRecord验证
你在处理什么?
- 1. 控制器 - REST映射、concerns、Turbo响应、API模式
- 模型 - Concerns、状态记录、回调、作用域、POROs
- 视图与前端 - Turbo、Stimulus、CSS、局部视图
- 架构 - 路由、多租户、认证、任务、缓存
- 测试 - Minitest、fixtures、集成测试
- Gem与依赖 - 使用与避免的内容
- 代码审查 - 对照DHH风格审查代码
- 通用指导 - 理念与惯例
请指定数字或描述你的任务。
models.md |
| 3, view, frontend, turbo, stimulus, css | frontend.md |
| 4, architecture, routing, auth, job, cache | architecture.md |
| 5, test, testing, minitest, fixture | testing.md |
| 6, gem, dependency, library | gems.md |
| 7, review | 阅读所有参考文档,然后审查代码 |
| 8, general task | 根据上下文阅读相关参考文档 |
阅读相关参考文档后,将模式应用于用户的代码。
命名惯例
动词: card.close、card.gild、board.publish(而非set_style方法)
谓词: card.closed?、card.golden?(通过相关记录的存在性推导)
Concerns: 描述能力的形容词(Closeable、Publishable、Watchable)
控制器: 匹配资源的名词(Cards::ClosuresController)
作用域:
- - chronologically、reversechronologically、alphabetically、latest
- preloaded(标准预加载名称)
- indexedby、sorted_by(参数化)
- active、unassigned(业务术语,非SQL风格)
REST映射
不要使用自定义操作,而是创建新资源:
POST /cards/:id/close → POST /cards/:id/closure
DELETE /cards/:id/close → DELETE /cards/:id/closure
POST /cards/:id/archive → POST /cards/:id/archival
Ruby语法偏好
ruby
符号数组在括号内带空格
before
action :setmessage, only: %i[ show edit update destroy ]
私有方法缩进
private
def set_message
@message = Message.find(params[:id])
end
无表达式的case用于条件判断
case
when params[:before].present?
messages.page_before(params[:before])
else
messages.last_page
end
使用Bang方法实现快速失败
@message = Message.create!(params)
使用三元运算符处理简单条件
@room.direct? ? @room.users : @message.mentionees
关键模式
状态作为记录:
ruby
Card.joins(:closure) # 已关闭的卡片
Card.where.missing(:closure) # 未关闭的卡片
当前属性:
ruby
belongs_to :creator, default: -> { Current.user }
模型上的授权:
ruby
class User < ApplicationRecord
def can_administer?(message)
message.creator == self || admin?
end
end
领域知识
references/中的所有详细模式:
Concerns、状态记录、回调、作用域、POROs、授权、广播 |
|
frontend.md | Turbo Streams、Stimulus控制器、CSS层级、OKLCH颜色、局部视图 |
|
architecture.md | 路由、认证、任务、Current属性、缓存、数据库模式 |
|
testing.md | Minitest、fixtures、单元/集成/系统测试、测试模式 |
|
gems.md | 使用与避免的内容、决策框架、Gemfile示例 |
当以下条件满足时,代码遵循DHH风格:
- - 控制器映射到资源的CRUD动词
- 模型使用concerns实现水平行为
- 状态通过记录而非布尔值追踪
- 没有不必要的服务对象或抽象层
- 优先使用数据库支持的解决方案而非外部服务
- 测试使用Minitest和fixtures
- 使用Turbo/Stimulus实现交互(无重型JS框架)
- 使用现代特性的原生CSS(层级、OKLCH、嵌套)
- 授权逻辑位于User模型上
- 任务是调用模型方法的浅层包装器
基于Marc Köhlbrugge的《非官方37signals/DHH Rails风格指南》,通过对Fizzy代码库中265个拉取请求的深度分析生成。
重要免责声明:
- - LLM生成的指南——可能包含不准确之处
- 来自Fizzy的代码示例根据OSaasy许可证授权
- 与37signals无关联或未经其认可