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>
<intake> What are you working on?
  1. Controllers - REST mapping, concerns, Turbo responses, API patterns
  2. Models - Concerns, state records, callbacks, scopes, POROs
  3. Views & Frontend - Turbo, Stimulus, CSS, partials
  4. Architecture - Routing, multi-tenancy, authentication, jobs, caching
  5. Testing - Minitest, fixtures, integration tests
  6. Gems & Dependencies - What to use vs avoid
  7. Code Review - Review code against DHH style
  8. General Guidance - Philosophy and conventions
Specify a number or describe your task. </intake>
<routing>
ResponseReference to Read
1, controllercontrollers.md
2, modelmodels.md
3, view, frontend, turbo, stimulus, cssfrontend.md
4, architecture, routing, auth, job, cachearchitecture.md
5, test, testing, minitest, fixturetesting.md
6, gem, dependency, librarygems.md
7, reviewRead all references, then review code
8, general taskRead 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>
<intake> 你正在做什么工作?
  1. 控制器 - REST映射、Concerns、Turbo响应、API模式
  2. 模型 - Concerns、状态记录、回调、作用域、PORO
  3. 视图与前端 - Turbo、Stimulus、CSS、partial
  4. 架构 - 路由、多租户、认证、任务、缓存
  5. 测试 - Minitest、fixtures、集成测试
  6. Gem与依赖 - 工具选择建议(使用/避免)
  7. 代码评审 - 对照DHH风格评审代码
  8. 通用指导 - 理念与约定
请指定编号或描述你的任务。 </intake>
<routing>
响应参考文档
1, controllercontrollers.md
2, modelmodels.md
3, view, frontend, turbo, stimulus, cssfrontend.md
4, architecture, routing, auth, job, cachearchitecture.md
5, test, testing, minitest, fixturetesting.md
6, gem, dependency, librarygems.md
7, review阅读所有参考文档,然后评审代码
8, general task根据上下文阅读相关参考文档
阅读相关参考文档后,将模式应用到用户的代码中。 </routing>
<quick_reference>

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
    ,
    latest
  • preloaded
    (standard eager loading name)
  • indexed_by
    ,
    sorted_by
    (parameterized)
  • active
    ,
    unassigned
    (business terms, not SQL-ish)
动词:
card.close
,
card.gild
,
board.publish
(不使用
set_style
这类方法)
谓词:
card.closed?
,
card.golden?
(由关联记录的存在性派生)
Concerns: 使用描述能力的形容词(如
Closeable
,
Publishable
,
Watchable
控制器: 名称与资源匹配(如
Cards::ClosuresController
作用域:
  • chronologically
    ,
    reverse_chronologically
    ,
    alphabetically
    ,
    latest
  • preloaded
    (标准预加载命名)
  • indexed_by
    ,
    sorted_by
    (带参数的命名)
  • active
    ,
    unassigned
    (使用业务术语,而非SQL风格表述)

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/archival

Ruby Syntax Preferences

Ruby语法偏好

ruby
undefined
ruby
undefined

Symbol 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
undefined

Key Patterns

核心模式

State as Records:
ruby
Card.joins(:closure)         # closed cards
Card.where.missing(:closure) # open cards
Current 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/
:
FileTopics
controllers.mdREST mapping, concerns, Turbo responses, API patterns, HTTP caching
models.mdConcerns, state records, callbacks, scopes, POROs, authorization, broadcasting
frontend.mdTurbo Streams, Stimulus controllers, CSS layers, OKLCH colors, partials
architecture.mdRouting, authentication, jobs, Current attributes, caching, database patterns
testing.mdMinitest, fixtures, unit/integration/system tests, testing patterns
gems.mdWhat 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>
<credits> Based on [The Unofficial 37signals/DHH Rails Style Guide](https://github.com/marckohlbrugge/unofficial-37signals-coding-style-guide) by [Marc Köhlbrugge](https://x.com/marckohlbrugge), 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
</credits>
references/
目录下的所有详细模式:
文件主题
controllers.mdREST映射、Concerns、Turbo响应、API模式、HTTP缓存
models.mdConcerns、状态记录、回调、作用域、PORO、授权、广播
frontend.mdTurbo Streams、Stimulus控制器、CSS分层、OKLCH颜色、partial
architecture.md路由、认证、任务、Current属性、缓存、数据库模式
testing.mdMinitest、fixtures、单元/集成/系统测试、测试模式
gems.md工具选择(使用/避免)、决策框架、Gemfile示例
</reference_index>
<success_criteria> 代码符合DHH风格的判定标准:
  • 控制器映射到资源的CRUD动词
  • 模型使用Concerns实现横向行为
  • 通过记录而非布尔值跟踪状态
  • 无不必要的服务对象或抽象层
  • 优先使用基于数据库的解决方案而非外部服务
  • 使用Minitest搭配fixtures进行测试
  • 使用Turbo/Stimulus实现交互(不使用重型JS框架)
  • 使用带现代特性的原生CSS(分层、OKLCH、嵌套)
  • 授权逻辑位于User模型中
  • 任务是调用模型方法的浅包装 </success_criteria>
<credits> 基于Marc Köhlbrugge的《非官方37signals/DHH Rails风格指南》(链接:https://github.com/marckohlbrugge/unofficial-37signals-coding-style-guide),该指南通过深度分析Fizzy代码库的265个拉取请求生成。
重要免责声明:
  • 本指南由大语言模型生成,可能存在不准确内容
  • 来自Fizzy的代码示例采用O'Saasy许可证
  • 本指南与37signals无关,未获得其认可或赞助
</credits>