第 5 课:Rules(下)— 语言特定规则与实战

3 阅读8分钟

所属阶段:第二阶段「组件精讲」(第 4-15 课) 前置条件:第 4 课 本课收获:能对比通用与语言规则的差异,能为新语言草拟规则


一、本课概述

上节课我们学习了 10 个通用规则文件。这节课进入语言特定规则层:

  1. 继承机制 — 语言规则如何"继承并覆盖"通用规则
  2. 覆盖案例 — 真实的继承覆盖场景分析
  3. 14 种语言的规则结构 — 每种语言通常有 5 个文件
  4. 添加新语言 — 从零开始为一种新语言创建规则

这节课是 Rules 专题的收尾。学完后,你将完整掌握 ECC 的规则体系。


二、语言规则的继承机制

2.1 继承声明

每个语言规则文件的第一行(正文部分)必须以引用块声明继承关系:

> This file extends [common/coding-style.md](../common/coding-style.md) with Go specific content.

这行话的含义:

  • 本文件继承common/coding-style.md 的所有规则
  • 本文件只包含新增覆盖的部分
  • 没有提及的通用规则仍然有效

2.2 paths 字段

语言规则通常有一个 paths frontmatter 字段,指定适用范围:

# rules/golang/coding-style.md
---
paths:
  - "**/*.go"
  - "**/go.mod"
  - "**/go.sum"
---
# rules/typescript/coding-style.md
---
paths:
  - "**/*.ts"
  - "**/*.tsx"
  - "**/*.js"
  - "**/*.jsx"
---

这告诉 Claude:只在处理匹配这些 glob 模式的文件时应用语言特定规则。处理其他文件时,仅通用规则生效。

2.3 继承 vs 覆盖的判断

情况行为示例
语言规则新增了通用规则没有的内容追加Go 新增 "Accept interfaces, return structs"
语言规则重新定义了同一主题覆盖Go 指针接收器覆盖通用不可变性
语言规则未提及某通用规则继承未提及 "函数 < 50 行" 则沿用

三、覆盖案例分析

3.1 Go 的指针接收器 — 覆盖不可变性

通用规则common/coding-style.md):

Immutability — CRITICAL: ALWAYS create new objects, NEVER mutate existing ones.

Go 规则rules/golang/coding-style.md):

// Go 惯用法:接受接口,返回结构体
// 使用指针接收器修改 struct 是 Go 的标准做法

func (u *User) UpdateEmail(email string) {
    u.Email = email  // 直接修改,这在 Go 中是惯用的
}

Go 的设计哲学中,指针接收器的可变性是语言核心特性。强制不可变会导致非惯用的代码。因此语言规则覆盖了通用规则。

通用规则中也标注了这种可能性:

Language note: This rule may be overridden by language-specific rules for languages where this pattern is not idiomatic.

3.2 Rust 的所有权系统 — 增强不可变性

通用规则:不可变性是 CRITICAL。

Rust 规则rules/rust/coding-style.md):

// Rust 变量默认不可变 — 拥抱这一点
// 仅在需要修改时使用 let mut
let name = String::from("hello");     // 不可变
let mut counter = 0;                   // 显式声明可变

// 使用 Cow 实现按需分配
use std::borrow::Cow;
fn normalize(input: &str) -> Cow<'_, str> {
    if input.contains(' ') {
        Cow::Owned(input.replace(' ', "_"))
    } else {
        Cow::Borrowed(input)
    }
}

Rust 不是覆盖不可变性,而是增强它 — Rust 的所有权系统在编译期强制执行不可变性,比通用规则的要求更严格。语言规则提供了 Rust 特有的实现方式(let vs let mutCow)。

3.3 Python 的 PEP 8 — 覆盖格式规范

通用规则:通用的命名规范(camelCase/PascalCase/UPPER_SNAKE_CASE)。

Python 规则rules/python/coding-style.md):

# PEP 8 命名规范(覆盖通用规则)
# 函数和变量:snake_case(不是 camelCase)
def get_user_by_id(user_id: int) -> User:
    pass

# 类:PascalCase(与通用规则一致)
class UserRepository:
    pass

# 常量:UPPER_SNAKE_CASE(与通用规则一致)
MAX_RETRY_COUNT = 3

# 不可变数据结构
@dataclass(frozen=True)
class User:
    name: str
    email: str

Python 的 snake_case 函数命名覆盖了通用规则中的 camelCase 建议。但类命名和常量命名与通用规则保持一致。

3.4 TypeScript 的类型系统 — 新增内容

TypeScript 规则rules/typescript/coding-style.md)新增了通用规则中没有的内容:

// 导出函数必须有显式类型
interface User {
  firstName: string;
  lastName: string;
}

export function formatUser(user: User): string {
  return `${user.firstName} ${user.lastName}`;
}

// 本地变量可以使用类型推断
const count = items.length;  // TypeScript 自动推断为 number

这是新增(通用规则没有类型系统的要求),不是覆盖。


四、14 种语言的规则结构

4.1 支持的语言列表

ECC 当前支持 14 个语言/平台规则目录:

序号目录语言/平台文件数
1golang/Go5
2typescript/TypeScript / JavaScript5
3python/Python5
4rust/Rust5
5swift/Swift5
6java/Java5
7kotlin/Kotlin5
8cpp/C++5
9csharp/C#5
10dart/Dart / Flutter5
11php/PHP5
12perl/Perl5
13web/Web 前端(HTML/CSS)5
14zh/中文规则翻译

4.2 每种语言的 5 个标准文件

每种语言目录下通常有 5 个文件,对应通用规则中的 5 个核心领域:

文件对应通用规则内容
coding-style.mdcommon/coding-style.md格式化工具、惯用法、不可变实现
testing.mdcommon/testing.md测试框架、覆盖率工具、测试组织
patterns.mdcommon/patterns.md语言特有的设计模式
hooks.mdcommon/hooks.mdPostToolUse 钩子(格式化、lint)
security.mdcommon/security.md密钥管理、安全扫描工具

4.3 语言规则覆盖全景

通用规则GoTypeScriptPythonRust
不可变性指针接收器可变Readonly/as constfrozen dataclass默认不可变(增强)
格式化工具gofmtPrettier/ESLintBlack/Ruffrustfmt
测试框架go testJest/Vitestpytestcargo test
Lint 工具golangci-lintESLintRuff/Flake8clippy
命名风格camelCase/PascalCasecamelCase/PascalCasesnake_casesnake_case
错误处理多值返回 errtry-catch + Resulttry-exceptResult/Option

五、安装注意事项

5.1 目录结构必须保持

这是一个极易犯错的安装问题。再次强调:

# 错误!扁平化复制会导致同名文件覆盖
cp rules/common/* ~/.claude/rules/
cp rules/golang/* ~/.claude/rules/
# 结果:golang/coding-style.md 覆盖了 common/coding-style.md
# 通用规则丢失!

# 正确!保持目录层级
cp -r rules/common ~/.claude/rules/common
cp -r rules/golang ~/.claude/rules/golang

5.2 语言规则的相对引用

语言规则文件通过相对路径引用通用规则:

> This file extends [common/coding-style.md](../common/coding-style.md)

如果扁平化复制,这个 ../common/ 路径就会失效。这是保持目录结构的另一个技术原因。

5.3 多语言项目

如果你的项目同时使用多种语言(如 TypeScript 后端 + Python ML),安装多个语言规则:

cp -r rules/common ~/.claude/rules/common
cp -r rules/typescript ~/.claude/rules/typescript
cp -r rules/python ~/.claude/rules/python

Claude 会根据当前处理的文件路径(通过 paths 字段匹配)自动选择对应的语言规则。


六、为新语言添加规则

6.1 完整步骤

以添加 Ruby 规则为例:

第一步:创建目录

mkdir rules/ruby

第二步:创建 5 个标准文件

每个文件以 extends 声明开头:

# rules/ruby/coding-style.md
---
paths:
  - "**/*.rb"
  - "**/Gemfile"
  - "**/Rakefile"
---
# Ruby Coding Style

> This file extends [common/coding-style.md](../common/coding-style.md) with Ruby specific content.

## Formatting
- **RuboCop** for enforcement
- 2-space indent (Ruby community standard)

## Immutability
- Use `freeze` on string constants
- Prefer `Struct` with keyword_init for value objects

## Error Handling
- Use specific exception classes (not bare `rescue`)
- Always rescue from specific exceptions

第三步:每个文件覆盖/新增语言特有内容

文件Ruby 特有内容
coding-style.mdRuboCop、2-space 缩进、freeze、Symbol
testing.mdRSpec、SimpleCov、factory_bot
patterns.mdMixin、Concern、Gem 结构
hooks.mdPostToolUse 运行 rubocop -A
security.mdBrakeman 扫描、strong_parameters

第四步:引用相关 Skill(如果存在)

## Reference
- Skill: `ruby-patterns` — Ruby idioms and design patterns
- Skill: `rails-testing` — Rails-specific testing strategies

6.2 草拟模板

以下是新语言规则文件的最小模板:

---
paths:
  - "**/*.<ext>"
---
# <Language> <Topic>

> This file extends [common/<topic>.md](../common/<topic>.md) with <Language> specific content.

## <Section 1: Language-specific override or addition>

## <Section 2: Tools and frameworks>

## Reference
- Skill: `<related-skill>` — description

七、本课练习

练习 1:对比分析(15 分钟)

打开 rules/common/coding-style.mdrules/golang/coding-style.md,找出至少 3 个覆盖点:

# 并排对比(或分两个编辑器窗口打开)
cat rules/common/coding-style.md
cat rules/golang/coding-style.md

回答:

  1. Go 对不可变性原则做了什么调整?
  2. Go 新增了哪些通用规则中没有的内容?
  3. 哪些通用规则 Go 没有提及(即直接继承)?

练习 2:对比 Python 和 Rust 的不可变性实现(10 分钟)

cat rules/python/coding-style.md
cat rules/rust/coding-style.md

比较两种语言如何实现通用规则的"不可变性"要求:

  • Python 用什么机制?(提示:frozen=True
  • Rust 用什么机制?(提示:let vs let mut
  • 哪种语言的不可变性更"强"?为什么?

练习 3:为 Ruby 草拟 coding-style.md(20 分钟)

这是本课最重要的练习。

基于你对 Ruby 的了解(如果不熟悉 Ruby,可以选择你熟悉的语言),草拟一个 rules/ruby/coding-style.md 文件。

要求:

  • extends 声明开头
  • 包含 paths frontmatter
  • 至少包含 3 个段落:Formatting、Immutability、Error Handling
  • 每个段落要说明与通用规则的关系(继承/覆盖/新增)

练习 4(选做):浏览所有 14 种语言的 coding-style.md

用 10 分钟快速扫过所有语言的 coding-style.md,填写以下表格:

语言格式化工具不可变性实现命名风格
Go
TypeScript
Python
Rust
Swift
Java
...

八、本课小结

你应该记住的内容
继承声明> This file extends common/xxx.md 开头
paths 字段限定规则适用的文件路径(glob 模式)
覆盖原则语言 > 通用,项目 > 语言 > 通用
标准文件数每种语言 5 个:coding-style / testing / patterns / hooks / security
安装陷阱不能扁平化复制,必须保持 common/ 和语言目录分开
新语言步骤创建目录 → 5 个文件 → extends 声明 → 覆盖/新增内容 → 引用 Skill

九、下节预告

第 6 课:Agents(上)— 文件格式与 Frontmatter

下节课我们将深入 Agent 组件,学习 YAML frontmatter 的四个字段设计要点、description 的精确度如何影响触发效果、tools 的最小权限原则,以及 model 的选择策略。

预习建议:打开 10 个不同的 Agent 文件,对比它们的 frontmatter 差异,特别注意 toolsmodel 的组合模式。