dhh-rails-style
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese<objective>
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.
</objective>
<essential_principles>
<objective>
将37signals/DHH的Rails约定应用于Ruby和Rails代码。本Skill提供的全面领域专业知识,来自对37signals生产代码库(Fizzy/Campfire)以及DHH代码评审模式的分析。
</objective>
<essential_principles>
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)
- view_component (partials work fine)
- GraphQL (REST with Turbo sufficient)
- factory_bot (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 </essential_principles>
- 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.
</intake>
<routing>
| Response | Reference to Read |
|---|---|
| 1, controller | controllers.md |
| 2, model | 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.
</routing>
<quick_reference>
"最好的代码是你无需编写的代码。次优的代码是逻辑显而易见正确的代码。"
原生Rails足够用:
- 优先使用丰富的领域模型而非服务对象
- 优先使用CRUD控制器而非自定义动作
- 使用Concerns实现横向代码共享
- 用记录而非布尔值字段跟踪状态
- 一切基于数据库实现(不使用Redis)
- 先自行构建解决方案,再考虑使用gem
他们刻意避免使用的工具:
- devise(改用约150行的自定义认证实现)
- pundit/cancancan(在模型中实现简单的角色检查)
- sidekiq(使用基于数据库的Solid Queue)
- redis(一切依赖数据库实现)
- view_component(使用partial即可满足需求)
- GraphQL(REST搭配Turbo已足够)
- factory_bot(fixtures更简单)
- rspec(Rails自带Minitest)
- Tailwind(使用带分层的原生CSS)
开发理念:
- 发布、验证、优化 - 以原型质量的代码上线,从中学习
- 修复问题根源,而非仅处理表面症状
- 优先优化编写时的操作,而非读取时的计算
- 优先使用数据库约束,而非ActiveRecord验证 </essential_principles>
- 控制器 - REST映射、Concerns、Turbo响应、API模式
- 模型 - Concerns、状态记录、回调、作用域、PORO
- 视图与前端 - Turbo、Stimulus、CSS、partial
- 架构 - 路由、多租户、认证、任务、缓存
- 测试 - Minitest、fixtures、集成测试
- Gem与依赖 - 工具选择建议(使用/避免)
- 代码评审 - 对照DHH风格评审代码
- 通用指导 - 理念与约定
请指定编号或描述你的任务。
</intake>
<routing>
| 响应 | 参考文档 |
|---|---|
| 1, controller | controllers.md |
| 2, model | 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 | 根据上下文阅读相关参考文档 |
阅读相关参考文档后,将模式应用到用户的代码中。
</routing>
<quick_reference>
Naming Conventions
命名约定
Verbs: , , (not methods)
card.closecard.gildboard.publishset_stylePredicates: , (derived from presence of related record)
card.closed?card.golden?Concerns: Adjectives describing capability (, , )
CloseablePublishableWatchableControllers: Nouns matching resources ()
Cards::ClosuresControllerScopes:
- ,
chronologically,reverse_chronologically,alphabeticallylatest - (standard eager loading name)
preloaded - ,
indexed_by(parameterized)sorted_by - ,
active(business terms, not SQL-ish)unassigned
动词: , , (不使用这类方法)
card.closecard.gildboard.publishset_style谓词: , (由关联记录的存在性派生)
card.closed?card.golden?Concerns: 使用描述能力的形容词(如, , )
CloseablePublishableWatchable控制器: 名称与资源匹配(如)
Cards::ClosuresController作用域:
- ,
chronologically,reverse_chronologically,alphabeticallylatest - (标准预加载命名)
preloaded - ,
indexed_by(带参数的命名)sorted_by - ,
active(使用业务术语,而非SQL风格表述)unassigned
REST Mapping
REST映射
Instead of custom actions, create new resources:
POST /cards/:id/close → POST /cards/:id/closure
DELETE /cards/:id/close → DELETE /cards/:id/closure
POST /cards/:id/archive → POST /cards/:id/archival不要使用自定义动作,而是创建新资源:
POST /cards/:id/close → POST /cards/:id/closure
DELETE /cards/:id/close → DELETE /cards/:id/closure
POST /cards/:id/archive → POST /cards/:id/archivalRuby Syntax Preferences
Ruby语法偏好
ruby
undefinedruby
undefinedSymbol arrays with spaces inside brackets
符号数组的方括号内保留空格
before_action :set_message, only: %i[ show edit update destroy ]
before_action :set_message, only: %i[ show edit update destroy ]
Private method indentation
私有方法缩进
private
def set_message
@message = Message.find(params[:id])
end
private
def set_message
@message = Message.find(params[:id])
end
Expression-less case for conditionals
无表达式的case条件语句
case
when params[:before].present?
messages.page_before(params[:before])
else
messages.last_page
end
case
when params[:before].present?
messages.page_before(params[:before])
else
messages.last_page
end
Bang methods for fail-fast
使用Bang方法快速失败
@message = Message.create!(params)
@message = Message.create!(params)
Ternaries for simple conditionals
简单条件使用三元运算符
@room.direct? ? @room.users : @message.mentionees
undefined@room.direct? ? @room.users : @message.mentionees
undefinedKey Patterns
核心模式
State as Records:
ruby
Card.joins(:closure) # closed cards
Card.where.missing(:closure) # open cardsCurrent Attributes:
ruby
belongs_to :creator, default: -> { Current.user }Authorization on Models:
ruby
class User < ApplicationRecord
def can_administer?(message)
message.creator == self || admin?
end
end</quick_reference>
<reference_index>
状态记录模式:
ruby
Card.joins(:closure) # 已关闭的卡片
Card.where.missing(:closure) # 未关闭的卡片Current属性:
ruby
belongs_to :creator, default: -> { Current.user }模型层授权:
ruby
class User < ApplicationRecord
def can_administer?(message)
message.creator == self || admin?
end
end</quick_reference>
<reference_index>
Domain Knowledge
领域知识
All detailed patterns in :
references/| File | Topics |
|---|---|
| controllers.md | REST mapping, concerns, Turbo responses, API patterns, HTTP caching |
| models.md | 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 |
| </reference_index> |
<success_criteria>
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 </success_criteria>
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
references/| 文件 | 主题 |
|---|---|
| controllers.md | REST映射、Concerns、Turbo响应、API模式、HTTP缓存 |
| models.md | Concerns、状态记录、回调、作用域、PORO、授权、广播 |
| frontend.md | Turbo Streams、Stimulus控制器、CSS分层、OKLCH颜色、partial |
| architecture.md | 路由、认证、任务、Current属性、缓存、数据库模式 |
| testing.md | Minitest、fixtures、单元/集成/系统测试、测试模式 |
| gems.md | 工具选择(使用/避免)、决策框架、Gemfile示例 |
| </reference_index> |
<success_criteria>
代码符合DHH风格的判定标准:
- 控制器映射到资源的CRUD动词
- 模型使用Concerns实现横向行为
- 通过记录而非布尔值跟踪状态
- 无不必要的服务对象或抽象层
- 优先使用基于数据库的解决方案而非外部服务
- 使用Minitest搭配fixtures进行测试
- 使用Turbo/Stimulus实现交互(不使用重型JS框架)
- 使用带现代特性的原生CSS(分层、OKLCH、嵌套)
- 授权逻辑位于User模型中
- 任务是调用模型方法的浅包装 </success_criteria>
重要免责声明:
- 本指南由大语言模型生成,可能存在不准确内容
- 来自Fizzy的代码示例采用O'Saasy许可证
- 本指南与37signals无关,未获得其认可或赞助