ce-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代码。本技能提供的全面领域专业知识,源自对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, controller
references/controllers.md
2, model
references/models.md
3, view, frontend, turbo, stimulus, css
references/frontend.md
4, architecture, routing, auth, job, cache
references/architecture.md
5, test, testing, minitest, fixture
references/testing.md
6, gem, dependency, library
references/gems.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, controller
references/controllers.md
2, model
references/models.md
3, view, frontend, turbo, stimulus, css
references/frontend.md
4, architecture, routing, auth, job, cache
references/architecture.md
5, test, testing, minitest, fixture
references/testing.md
6, gem, dependency, library
references/gems.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 Attributes:
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
references/controllers.md
REST mapping, concerns, Turbo responses, API patterns, HTTP caching
references/models.md
Concerns, state records, callbacks, scopes, POROs, authorization, broadcasting
references/frontend.md
Turbo Streams, Stimulus controllers, CSS layers, OKLCH colors, partials
references/architecture.md
Routing, authentication, jobs, Current attributes, caching, database patterns
references/testing.md
Minitest, fixtures, unit/integration/system tests, testing patterns
references/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>
<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/
目录下:
文件主题
references/controllers.md
REST映射、Concerns、Turbo响应、API模式、HTTP缓存
references/models.md
Concerns、状态记录、回调、作用域、PORO、授权、广播
references/frontend.md
Turbo Streams、Stimulus控制器、CSS分层、OKLCH颜色、partial
references/architecture.md
路由、认证、任务、Current attributes、缓存、数据库模式
references/testing.md
Minitest、fixtures、单元/集成/系统测试、测试模式
references/gems.md
工具选择与避坑、决策框架、Gemfile示例
</reference_index>
<success_criteria> 代码符合DHH风格的判定标准:
  • 控制器映射到资源的CRUD动词
  • 模型使用Concerns实现横向行为
  • 通过记录而非布尔值跟踪状态
  • 无不必要的服务对象或抽象层
  • 优先使用基于数据库的解决方案而非外部服务
  • 测试使用Minitest搭配fixtures
  • 使用Turbo/Stimulus实现交互(不使用重型JS框架)
  • 使用带现代特性的原生CSS(分层、OKLCH、嵌套)
  • 授权逻辑位于User模型中
  • 任务是调用模型方法的轻量包装 </success_criteria>
<credits> 基于[Marc Köhlbrugge](https://x.com/marckohlbrugge)编写的《非官方37signals/DHH Rails风格指南》(https://github.com/marckohlbrugge/unofficial-37signals-coding-style-guide),通过深度分析Fizzy代码库的265个拉取请求生成。
重要免责声明:
  • 本指南由大语言模型生成,可能存在不准确内容
  • Fizzy的代码示例采用O'Saasy许可证
  • 与37signals无关联,未获其认可
</credits>