ECC Ruby Patterns 实战指南:面向 Ruby 与 Rails 的 Agent 编码规约全解
本指南以仓库中的语言级 steering 文件 ruby-patterns.md 为主体脉络,系统讲解在 ECC 驱动的 Agent 工作流中编写 Ruby / Rails 代码时应遵循的技术选型、架构分层、安全基线、后台任务与测试策略,并横向对照同主题的 rules/ruby 规范族、真实项目模板 rails-app-CLAUDE.md 与后端通用技能
backend-patterns,帮你把"约束文件里的一句话规则"还原为可直接落地、可复制运行的工程实践。读完你既能理解这些规约为何被逐条固化进仓库,也能按图索骥地为自己的 Rails 项目搭建同样的开发纪律。
一、这份文档在 ECC 仓库中的定位:语言级 steering 文件
在 Everything Claude Code(ECC)类工作流仓库中,.kiro 目录专门存放"让 Agent 按团队规约工作"的指导性文件。打开 ruby-patterns.md 即可看到它的生效方式由 YAML frontmatter 控制:
---
inclusion: fileMatch
fileMatchPattern: "*.rb"
description: Ruby-specific patterns and Rails best practices.
---
这意味着该文件属于 .kiro/steering/ 下 22 个 steering 文件中的 fileMatch 型规则——它与 inclusion: auto 的通用规则(如 coding-style.md、patterns.md)不同,只有当 Agent 正在编辑 *.rb 等 Ruby 文件时才会被自动装载(参见 .kiro/README.md 对 Steering Files 机制的说明)。这是一种典型的"按文件类型按需注入上下文"的设计:通用模式始终在场,语言专属细节只在相关语言出现时进入上下文,避免每个会话都背一整套无关噪音。
文件开头明确写到 "This file extends the common patterns with Ruby and Rails specific content",即它是通用模式文件 .kiro/steering/patterns.md(仓库模式、API 响应信封、骨架项目评估法)的 Ruby/Rails 方言扩展。仓库中与之配套的同一语言家族还包括 rules/ruby 目录下的五份规则:coding-style.md、patterns.md、security.md、testing.md、hooks.md,它们共同构成"指导文件 + 审查规则 + 钩子"的完整闭环。
二、语言与工程标准:先选对底座
原文档在 Standards 一节给出三条硬约束,仓库其他文件进一步给出了补充理由与判断前提:
| 规则 | 出处 | 补充说明(依据仓库内容) |
|---|---|---|
| 新 Rails 项目面向 Ruby 3.3+ | .kiro/steering/ruby-patterns.md | rules/ruby/coding-style.md 补充:除非项目已锁定更旧的受支持运行时 |
新文件添加 # frozen_string_literal: true |
同上 | 仅在项目已采用该约定时使用;rails-app-CLAUDE.md 的工程规范要求每个 Ruby 文件顶部都有此行 |
| 偏好清晰 Ruby,避免炫技元编程 | 同上 | 同上文件进一步约束:DSL 密集的代码要隔离在窄而可测试的边界之后 |
值得补充的是 rails-app-CLAUDE.md 给出的完整编码基线,可作为"清晰 Ruby"的操作化定义:
- 现代哈希语法
key:,除非键不是 Symbol 才用火箭=>; - 默认双引号,字符串内已含双引号时才用单引号;
- 两空格缩进,禁止 Tab;
- 类用 PascalCase、方法/变量用 snake_case、常量用 UPPER_SNAKE_CASE;
- 控制器保持在 80 行以内、模型 200 行以内,超限即抽取;
- 生产代码中禁止
puts、pp、debugger、binding.pry,统一走Rails.logger.<level>。
错误处理层面,rules/ruby/coding-style.md 要求只 rescue 具体异常,避免宽泛的 rescue StandardError(除非重抛或保留操作上下文),并用 ActiveSupport::Notifications 或应用 logger 记录运维事件——这些正好与原文档"清晰优先"的价值取向互为表里。
三、格式与 Lint:让本地与 CI 跑同一条命令
原文档给出了两条 RuboCop 命令:
bundle exec rubocop
bundle exec rubocop -A
bundle exec rubocop:只读报告,列出全部违规项;bundle exec rubocop -A:自动安全修复(autocorrect),可用于本地快速清理机械性问题。
结合 rules/ruby/coding-style.md 与 rules/ruby/hooks.md,可进一步落实三点实操细节:
- 以项目检入的 RuboCop 配置为准。Rails 8+ 新项目建议从官方默认风格
rubocop-rails-omakase起步,仅在代码库确有真实惯例时才做定制。 - 把格式化/Lint 命令收敛到 binstub 或脚本后面,保证 CI 与本地执行的永远是同一套命令,例如
bin/rubocop。 - 不要随意在行内静默禁用 cop(
# rubocop:disable),除非例外足够窄、有文档说明且难以用更干净的代码表达。 - 若将 RuboCop 接入钩子,应在编辑 Ruby 文件后运行
bundle exec rubocop -A <file>或项目更安全的格式化命令,而不是全局跑一遍。
四、架构分层:Rails Way First,再谈抽取
原文档的核心立场是 "Rails Way First":
- 小/中型功能先从纯 Rails MVC 与 Active Record 约定开始;
- 只有当 model/controller 承担了多重职责时,才引入 service objects、query objects、form objects(rules/ruby/patterns.md 还补充了 decorators、presenters);
- 保持 controller 是"传输层":只做 auth、参数处理、响应形状三件事。
关于抽取对象的命名,rules/ruby/patterns.md 有一条容易被忽略却极有价值的原则:
Name extracted objects after the business operation they perform, not after generic layers like
ManagerorProcessor.
即按业务操作命名(Invoices::Create),而不是按通用层命名(InvoiceCreator、XxxProcessor)。这与 rails-app-CLAUDE.md 中 service objects 要按领域命名空间组织(app/services/ 下如 Invoices::Create)的要求完全一致。
来看模板文件中"瘦控制器 + Service Object + Result 返回"的经典组合(节选自 rails-app-CLAUDE.md):
# app/services/invoices/create.rb —— 领域服务:一个方法入口、明确返回 Result
module Invoices
class Create
Result = Data.define(:success?, :invoice, :errors)
def self.call(...) = new(...).call
def initialize(params:, user:)
@params = params
@user = user
end
def call
invoice = build_invoice
ApplicationRecord.transaction do
invoice.save!
end
begin
send_notifications(invoice)
rescue StandardError => e
Rails.logger.error("Notification dispatch failed for invoice #{invoice.id}: #{e.message}")
end
Result.new(success?: true, invoice: invoice, errors: nil)
rescue ActiveRecord::RecordInvalid => e
Result.new(success?: false, invoice: e.record, errors: e.record.errors)
end
private
attr_reader :params, :user
def build_invoice
invoice = user.invoices.new(params.except(:line_items))
invoice.line_items.build(params[:line_items])
invoice.total = invoice.line_items.sum(&:amount)
invoice
end
end
end
# app/controllers/invoices_controller.rb —— 传输层只做三件事
class InvoicesController < ApplicationController
before_action :require_authentication
def create
authorize Invoice
result = Invoices::Create.call(params: invoice_params, user: current_user)
if result.success?
redirect_to result.invoice, notice: "Invoice created"
else
@invoice = result.invoice
render :new, status: :unprocessable_entity
end
end
private
def invoice_params
params.require(:invoice).permit(:customer_id, line_items: %i[description amount])
end
end
这段代码同时示范了原文档与通用模式 .kiro/steering/patterns.md 中"API 响应信封(success + data + error)"思想在服务层 Result 对象上的体现:成功与失败都通过返回值显式表达,服务边界内不靠抛异常做流程控制。这与 rails-app-CLAUDE.md 的错误处理约定一一对应——业务失败不跨服务边界抛错、期望内异常在服务内部捕获并写进 Result。
查询对象示例同样可在模板中找到完整版(rails-app-CLAUDE.md):
# app/queries/invoices/overdue.rb
module Invoices
class Overdue
def self.call(...) = new(...).call
def initialize(scope: Invoice.all, as_of: Time.current)
@scope = scope
@as_of = as_of
end
def call
scope
.where(status: :sent)
.where(due_date: ..as_of)
.where.not(id: paid_invoice_ids)
.includes(:customer, :line_items)
end
private
attr_reader :scope, :as_of
def paid_invoice_ids
Payment.where(created_at: ..as_of).pluck(:invoice_id)
end
end
end
注意 scope 的默认值是 Invoice.all、as_of 默认 Time.current——这使查询对象天然可组合、可注入时间做测试,正是"query objects 用于可复用、可组合的 Active Record 查询"的注解。
五、持久化:PostgreSQL 优先 + 一切动态值参数化
原文档对持久化的三条主张:
- 多主机生产环境 Rails 应用优先选用 PostgreSQL;
- 原生 SQL 要藏在 query objects 或 model scopes 之后;
- 每一个动态值都必须参数化。
rules/ruby/patterns.md 补充了一个容易被忽略的细分场景:Rails 8 默认的 SQLite 适配对单主机或负载温和的部署可行,但不应自动视为共享多服务系统的选择。这使选型判断从"默认 Postgres"细化为"按部署拓扑决策"。
关于 N+1 与回调边界,rails-app-CLAUDE.md 给出了可直接复制进团队规范的具体条款,可视为对"Raw SQL 纪律"的近邻约束:
# BAD: N+1 query —— 每条 post 触发一次 author 查询
posts = Post.published
posts.each { |post| post.author.name }
# GOOD: 一条查询完成 eager load
posts = Post.published.includes(:author)
posts.each { |post| post.author.name }
配套建议还包括:默认 eager load 关联、避免 default_scope(改用调用方主动选择的具名 scope)、按需在 .includes / .preload / .eager_load 间选择、对列表页展示 count 的 has_many 使用 counter caches、回调只做数据规整(如 before_validation :normalize_email),任何带副作用的行为都应放进 service。
六、后台任务:Solid Queue 与 Sidekiq 的分场景决策
原文档给出了一个明确的"分水岭"决策:
- Solid Queue:全新 Rails 8 应用 + 吞吐量温和 + 部署简单时使用;
- Sidekiq:需要成熟可观测性、高吞吐、或已存在 Redis 基础设施时使用。
rules/ruby/patterns.md 把决策矩阵扩展到了缓存与实时通道:Solid Cache / Solid Cable 在部署模型匹配时是 Rails 8 默认无 Redis 方案;而当存在跨服务共享行为、高扇出(high fanout)或需要高级数据结构时再回到 Redis。对应的环境变量配置可参考 rails-app-CLAUDE.md:
# SolidQueue / SolidCache / SolidCable 默认复用主数据库
# 高负载时可为它们单独建库:
QUEUE_DATABASE_URL=postgres://user:pass@host:5432/myapp_queue
CACHE_DATABASE_URL=postgres://user:pass@host:5432/myapp_cache
模板文件还沉淀了写 Job 的四条工程铁律(rails-app-CLAUDE.md):
- 向 Job 传 ID 而非 Record 对象——避免记录在入队与执行之间被删除时抛出
ActiveJob::DeserializationError; perform必须幂等——假设它会执行不止一次;- 显式声明重试策略:
retry_on与discard_on; - 按动作命名 Job(
SendInvoiceJob),而不是按名词(InvoiceJob)。
一个同时体现幂等与重试约定的示例(rails-app-CLAUDE.md):
# app/jobs/export_accounting_job.rb
class ExportAccountingJob < ApplicationJob
queue_as :exports
retry_on AccountingApi::TransientError, wait: :polynomially_longer, attempts: 5
discard_on AccountingApi::PermanentError
def perform(invoice_id)
invoice = Invoice.find(invoice_id)
return if invoice.exported_at.present? # 本地幂等检查
idempotency_key = "invoice-export-#{invoice.id}"
AccountingApi.export(invoice, idempotency_key: idempotency_key)
invoice.update!(exported_at: Time.current)
end
end
七、前端技术选型:默认 Hotwire,交互复杂度说了算
原文档的前端立场是"服务端渲染优先、默认 Hotwire",并给出了判定边界:
- Rails 服务端渲染应用优先选用 Hotwire 全家桶(Turbo、Stimulus、Importmap、Propshaft);
- 只有当交互复杂度或既有产品架构/团队归属确实需要额外客户端表面积时,才引入 React / Vue / Inertia。
rules/ruby/patterns.md 进一步强调了渲染关注点的隔离:view components、partials、presenters 只负责渲染决策,持久化与授权逻辑不得进入模板。配套的真实工程约束(rails-app-CLAUDE.md)可以作为验收清单:
- Hotwire 之上优先 ViewComponent(有复杂条件、接收多参数、出现三处以上的视图逻辑才用);简单呈现用 ERB partial;
- Turbo Frames 负责局部页面更新,Turbo Streams 负责服务端驱动的多区域响应;
- Action Cable 连接在
ApplicationCable::Connection#connect中鉴权,订阅在每个 channel 的subscribed中授权后才stream_from; - 视图更新优先走 Turbo Stream 广播(
broadcasts_to、broadcast_replace_later_to)而非手写 channel; - 把广播视为公开展示层,绝不在其中携带订阅者不该看到的数据。
八、认证选型:Rails 8 生成器起步,复杂需求再上 Devise
原文档的认证策略非常务实,按需求复杂度二分:
| 场景 | 方案 |
|---|---|
| 常规 session 认证(含密码重置) | Rails 8 authentication generator(bin/rails generate authentication) |
| 涉及 OAuth、MFA、confirmable/lockable 流程 | Devise |
rules/ruby/security.md 对会话安全补充了三个不能省的动作:登录与权限变更后轮换 session;账户恢复流程要带过期时间、一次性 token、限流与审计日志;多模型认证或已有大量 Devise 足迹时选择 Devise。rails-app-CLAUDE.md 还展示了它与授权层的衔接:session 认证用于整页应用、token 认证用于内嵌 API,授权走 Pundit 且每个 controller action 都有 authorize 或带文档说明的 skip_authorization。这条组合正是"认证归认证、授权归授权、控制器只做传输"原则在真实项目中的形态。
九、安全基线:从 CSRF 到依赖审计的一条龙命令
原文档 Security 一节浓缩为四条铁律 + 两条审计命令:
- 面向会改变状态的浏览器请求,保持 CSRF 保护开启;
- 在 mass assignment 之前使用 strong parameters 或类型化边界对象;
- 密钥存入 Rails credentials 或环境变量,绝不允许提交明文;
- 优先使用 Active Record 查询 API 与参数化 SQL,绝不把用户输入插值进 SQL。
bundle exec bundle-audit check --update
bundle exec brakeman --no-progress
rules/ruby/security.md 把这条安全面扩成了五个可审查维度,可作为原文档的逐条展开:
- Rails 默认值:CSRF、strong parameters、secrets 管理三条之上,还要求不提交
.env副本; - SQL 与 Active Record:请求、cookie、header、job、webhook 值一律不得插值进 SQL 字符串;安全敏感的回调副作用必须显式且有测试覆盖;
- 认证与会话:登录/权限变更后轮换 session;账户恢复流程需过期时间 + 一次性 token + 限流 + 审计;
- 依赖:lockfile 变更时即跑 audit;引入新 gem 前审查维护者活跃度、原生扩展风险、传递依赖,以及"同样的行为 Rails 核心是否已能实现";
- Web 安全面:模板输出默认转义,
html_safe、raw、自定义 sanitizer 一律视为敏感代码;上传文件按 content type、扩展名、大小与存储目标校验;后台任务、webhooks、Action Cable 消息、Turbo Stream 输入都被视为不可信边界。
原文档 Security 与 Testing 中的命令在 CI 闸门与钩子侧还有对应的落地形态:rules/ruby/hooks.md 建议在安全敏感的 Rails 修改后跑 bundle exec brakeman --no-progress,在 Gemfile / Gemfile.lock 变化且项目装有 bundler-audit 时跑 audit,并对"禁用 CSRF、扩大 mass assignment、加无参数化裸 SQL、破坏性且不可逆的迁移"给出警告。完整 CI 建议命令集为:
bundle exec rubocop
bundle exec brakeman --no-progress
bin/rails test
bundle exec rspec
原则是只用项目里真实存在的命令,未获维护者批准不得为钩子安装新依赖。
十、测试策略:金字塔纪律与工具归属
原文档的测试主张清晰区分了两种情况:
- 跟随 Rails 默认测试栈的应用用 Minitest;
- 项目已确立 RSpec 就用 RSpec(rules/ruby/testing.md 补充:同一功能区域内不要混用两者)。
分层要求上,原文档要求快领域行为放 model/service/query 测试、仅浏览器关键路径用 Capybara system tests。测试金字塔的完整展开(rules/ruby/testing.md)是:
- model / service / query / policy / job 测试承载快的领域行为;
- request/controller 测试覆盖 HTTP 契约、认证行为、重定向、状态码与响应形状;
- Capybara system tests 只用于浏览器关键流程,保持聚焦与稳定;
- 后台任务用单元测试测行为、集成测试测队列/入队契约;
- fixture 是项目默认且数据图小时使用;复杂构造与 traits 才上
factory_bot;测试数据贴近被测行为,避免隐藏成本的全局 fixture; - 使用 SimpleCov 时把阈值放在 CI,且不以低价值测试去刷分支覆盖率;bug 修复先补回归测试。
原文档给出的运行命令为:
bin/rails test
bundle exec rspec
按 rules/ruby/testing.md 可扩展为"最窄匹配"执行法(也正是钩子触发的粒度):
bin/rails test
bin/rails test test/models/user_test.rb
bundle exec rspec
bundle exec rspec spec/models/user_spec.rb
配套的 RSpec 测试示例(服务对象 + Result + 邮件入队断言)可参见 rails-app-CLAUDE.md;模板还建议以 90% 行覆盖为下限而非目标,"覆盖 85% 的锋利测试优于覆盖 100% 的堆积测试",system tests 默认 rack_test 驱动、仅在需要 JavaScript 时才切换到 headless Chrome。
十一、闭环落地:规则如何与 Agent、技能与钩子协同
原文档在末尾以两条 Reference 指向技能,实际上勾勒了这套 Ruby 规约的完整生态:
See skill: `backend-patterns` for service boundaries and adapter patterns.
See skill: `security-review` for secure-by-default review patterns.
两条引用分别指向仓库技能目录下的 backend-patterns SKILL 与 security-review SKILL(根目录同名的 skills/backend-patterns、skills/security-review 为该技能在 ECC 主仓的源版本),再加上 Ruby 专属 agent 如 python-reviewer / ruby 系审查者 可类比扩展,形成如下工作链路:
- Agent 开始编辑
*.rb文件 → 自动装载 ruby-patterns.md,并叠加通用 patterns.md; - 需要确定服务边界/适配器模式时 → 按需调用
backend-patterns技能; - 涉及安全默认值时 → 调用
security-review技能做 secure-by-default 检查; - 写完文件 →
rules/ruby/hooks.md中的钩子建议在本地就近跑 RuboCop / Brakeman / 最窄测试 / bundler audit; - 提交前 → 以
bundle exec rubocop、bundle exec brakeman --no-progress、bin/rails test/bundle exec rspec组成本项目的 CI 闸门。
若希望看到这套规约落进真实 Rails 8 项目的完整形态(含目录结构、环境变量、部署与 ECC 工作流 /plan、/tdd、/code-review、/security-scan),可直接参考仓库内置的 rails-app-CLAUDE.md。
小结:把约束变成纪律
.kiro/steering/ruby-patterns.md 的价值不在于它的每一条都是新知识,而在于它把一群团队反复踩过的 Ruby/Rails 决策压缩成了 Agent 可自动装载、可逐条执行、可被引用追溯的规则。当你把本指南中的命令、矩阵与代码模式反哺回自己的项目时,不妨遵循仓库自身的沉淀方式:通用原则放进 auto 型文件,Ruby 专属内容挂在 fileMatchPattern: "*.rb" 上,安全、测试与钩子各归其位,再让 lessons-learned 型的项目文件持续吸收新教训——这正是 ECC 类工作流让工程规约"活"起来的方式。
延伸阅读(仓库内相对路径):
- 规则族:rules/ruby/coding-style.md、rules/ruby/patterns.md、rules/ruby/security.md、rules/ruby/testing.md、rules/ruby/hooks.md
- 通用基线:rules/common/patterns.md、rules/common/security.md、rules/common/testing.md
- 真实模板:examples/rails-app-CLAUDE.md
- 关联技能:skills/backend-patterns/SKILL.md、skills/security-review/SKILL.md
- Steering 机制说明:.kiro/README.md
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00