ECC coding-standards 技能:跨项目编码规范、不可变模式与代码坏味道审查的完整实战指南
ECC(Everything Claude Code)将「编码规范」沉淀为可被 Agent 直接调用的技能(Skill),让命名、不可变性、可读性、代码坏味道审查等跨项目公约数不再依赖团队口头约定,而是变成可复用、可审计的标准化流程。本文以 ECC 仓库中的 coding-standards 技能 为主体,完整解析其激活条件、作用边界、代码质量四大原则、TypeScript/JavaScript 与 React 实战规范、API 设计标准、文件组织约定、注释文档规范、性能与测试标准以及代码坏味道检测清单,并结合仓库中的规则层(coding-style.md、code-review.md)与安装清单(install-components.json)补充其落地机制。读完后,你可以在任何 TypeScript/JavaScript/React 项目中直接套用这套规范,并理解 ECC 如何在 Agent 工作流中强制执行这些约定。
一、技能定位:共享底线(shared floor),而非框架手册
coding-standards 技能的官方描述是:「跨项目的基线编码规范,覆盖命名、可读性、不可变性与代码质量审查;框架特定模式请使用更细粒度的前端或后端技能」。它被明确定位为共享底线(shared floor),而不是详细的框架手册。技能文档开头的分工指引是:
- React、状态、表单、渲染与 UI 架构 → 使用
frontend-patterns技能; - 仓储/服务分层、端点设计、校验与服务端关注点 → 使用
backend-patterns或api-design技能; - 只需要最短可复用规则层、而非完整技能走查时 → 使用 rules/common/coding-style.md。
这一定位与仓库的安装体系相互印证:在 manifests/install-components.json 中,该组件登记为 skill:coding-standards,描述为「语言无关的编码标准与最佳实践」,归属 framework-language 模块——即选择框架/语言类安装模块时,它会与前端/后端模式技能一起被选中,但各自职责不重叠。
1.1 元数据与隐式调用
该技能在 .agents/skills/coding-standards/agents/openai.yaml 中声明了 Agent 接口元数据:
interface:
display_name: "Coding Standards"
short_description: "Cross-project coding conventions and review"
brand_color: "#3B82F6"
default_prompt: "Use $coding-standards to review code against cross-project standards."
policy:
allow_implicit_invocation: true
其中 allow_implicit_invocation: true 意味着 Agent 在判断任务属于「代码质量/命名审查且没有更细粒度框架技能适用」时,可以隐式激活该技能,无需用户显式指定;default_prompt 则给出了标准触发句式:用 $coding-standards 按跨项目标准审查代码。另外,仓库根目录下还有一份带 metadata.origin: ECC 的同源技能 skills/coding-standards/SKILL.md,内容与 .agents 版本一致,体现了技能在两套目录结构中的同步分发。
二、激活时机与作用边界
2.1 何时激活(When to Activate)
技能文档列出了六类典型激活场景:
- 启动新项目或新模块时;
- 审查代码质量与可维护性时;
- 重构既有代码使其符合约定时;
- 强制统一命名、格式化或结构一致性时;
- 配置 lint、格式化或类型检查规则时;
- 向新贡献者灌输编码约定(onboarding)时。
2.2 作用边界(Scope Boundaries)
技能对自身边界给出了双向清单,这是 ECC「技能分层」设计的核心:激活于——
- 描述性命名(descriptive naming);
- 不可变性默认值(immutability defaults);
- 可读性、KISS、DRY、YAGNI 的强制执行;
- 错误处理预期与代码坏味道(code smell)审查。
不作为主要来源于——
- React 组合、hooks、渲染模式;
- 后端架构、API 设计、数据库分层;
- 任何已有更窄 ECC 技能覆盖的领域特定框架指导。
从源码结构看,这条边界规则在仓库中是被系统性遵守的:语言专属技能(如 skills/cpp-coding-standards/SKILL.md、skills/java-coding-standards/SKILL.md)与框架专属技能(skills/frontend-patterns/、skills/backend-patterns/)与 coding-standards 并列存在于技能目录中,而 rules/common/code-review.md 进一步把审查职责按语言拆分给 typescript-reviewer、python-reviewer、go-reviewer、rust-reviewer 等 Agent——coding-standards 负责「语言无关的公约数」,具体语言问题下沉到更专的审查者,避免一个技能包揽一切。
三、代码质量四原则
技能将代码质量原则压缩为四条,每条都有可直接执行的子项:
3.1 可读性优先(Readability First)
- 代码被阅读的次数远多于被编写的次数;
- 使用清晰的变量名与函数名;
- 自文档化代码优于注释;
- 保持格式化一致性。
3.2 KISS(Keep It Simple, Stupid)
- 选择能工作的最简方案;
- 避免过度设计;
- 不做过早优化;
- 易理解 > 取巧。
3.3 DRY(Don't Repeat Yourself)
- 把常见逻辑抽成函数;
- 构建可复用组件;
- 跨模块共享工具函数;
- 杜绝复制粘贴式编程。
3.4 YAGNI(You Aren't Gonna Need It)
- 不在需要之前构建功能;
- 避免投机性泛化;
- 只在必要时增加复杂度;
- 从简单开始,需要时再重构。
这四条在规则层 rules/common/coding-style.md 中对应存在更短的「规则版」表述,例如 DRY 一节额外补充了「当重复是真实的而非推测的,才引入抽象」,YAGNI 一节则补充「先简单,当压力真实到来时再重构」——技能给出完整走查,规则层给出一页可快速引用的底线,这正是文档开头「用规则层代替完整技能」建议的落点。
四、TypeScript/JavaScript 标准:六个维度的可执行约定
这是技能文档的主体部分,全部采用 PASS/FAIL 对照示例,可直接作为代码审查对照表。
4.1 变量命名
// PASS: GOOD: Descriptive names
const marketSearchQuery = 'election'
const isUserAuthenticated = true
const totalRevenue = 1000
// FAIL: BAD: Unclear names
const q = 'election'
const flag = true
const x = 1000
规则层 coding-style.md 对此有更完整的命名约定表:变量与函数用描述性 camelCase;布尔量优先 is/has/should/can 前缀;接口、类型与组件用 PascalCase;常量用 UPPER_SNAKE_CASE;自定义 hooks 用 use 前缀加 camelCase。
4.2 函数命名:动词-名词模式
// PASS: GOOD: Verb-noun pattern
async function fetchMarketData(marketId: string) { }
function calculateSimilarity(a: number[], b: number[]) { }
function isValidEmail(email: string): boolean { }
// FAIL: BAD: Unclear or noun-only
async function market(id: string) { }
function similarity(a, b) { }
function email(e) { }
4.3 不可变模式(标记为 CRITICAL)
// PASS: ALWAYS use spread operator
const updatedUser = {
...user,
name: 'New Name'
}
const updatedArray = [...items, newItem]
// FAIL: NEVER mutate directly
user.name = 'New Name' // BAD
items.push(newItem) // BAD
这是整个技能中标记最重的条款。规则层给出了其伪代码形式与理由:update(original, field, value) 应返回带变更的新副本,而非原地修改;理由是「不可变数据消除隐藏副作用、让调试更容易、并支持安全的并发」。值得注意的是,技能允许有充分理由的例外——在注释章节的示例中,items.push(newItem) 在「刻意为了大数组性能使用 mutation」的注释下被判定为 PASS,说明该规范追求的是「例外必须显式声明」,而不是教条。
4.4 错误处理
// PASS: GOOD: Comprehensive error handling
async function fetchData(url: string) {
try {
const response = await fetch(url)
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
return await response.json()
} catch (error) {
console.error('Fetch failed:', error)
throw new Error('Failed to fetch data')
}
}
// FAIL: BAD: No error handling
async function fetchData(url) {
const response = await fetch(url)
return response.json()
}
规则层对错误处理的要求与之同构:每一层显式处理错误、UI 侧给出用户友好信息、服务端记录详细上下文、绝不静默吞错。
4.5 异步/并发最佳实践
// PASS: GOOD: Parallel execution when possible
const [users, markets, stats] = await Promise.all([
fetchUsers(),
fetchMarkets(),
fetchStats()
])
// FAIL: BAD: Sequential when unnecessary
const users = await fetchUsers()
const markets = await fetchMarkets()
const stats = await fetchStats()
无依赖关系的多个请求应并行执行,而非串行等待。
4.6 类型安全
// PASS: GOOD: Proper types
interface Market {
id: string
name: string
status: 'active' | 'resolved' | 'closed'
created_at: Date
}
function getMarket(id: string): Promise<Market> {
// Implementation
}
// FAIL: BAD: Using 'any'
function getMarket(id: any): Promise<any> {
// Implementation
}
偏好联合字面量类型('active' | 'resolved' | 'closed')收窄取值域,杜绝 any。
五、React 最佳实践
技能为 React 场景给出了四组可直接套用的模式(注意:边界章节已声明 React 深层模式应交给 frontend-patterns,这里只保留与「命名/结构/不可变」相关的公约数部分)。
5.1 组件结构
// PASS: GOOD: Functional component with types
interface ButtonProps {
children: React.ReactNode
onClick: () => void
disabled?: boolean
variant?: 'primary' | 'secondary'
}
export function Button({
children,
onClick,
disabled = false,
variant = 'primary'
}: ButtonProps) {
return (
<button
onClick={onClick}
disabled={disabled}
className={`btn btn-${variant}`}
>
{children}
</button>
)
}
// FAIL: BAD: No types, unclear structure
export function Button(props) {
return <button onClick={props.onClick}>{props.children}</button>
}
要点:Props 显式建模为接口、可选属性声明 ?、解构参数带默认值。
5.2 自定义 Hooks
// PASS: GOOD: Reusable custom hook
export function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value)
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value)
}, delay)
return () => clearTimeout(handler)
}, [value, delay])
return debouncedValue
}
// Usage
const debouncedQuery = useDebounce(searchQuery, 500)
注意该示例同时体现了两条规范:泛型 <T> 保持类型安全,清理函数 clearTimeout 避免副作用泄漏。
5.3 状态更新
// PASS: GOOD: Proper state updates
const [count, setCount] = useState(0)
// Functional update for state based on previous state
setCount(prev => prev + 1)
// FAIL: BAD: Direct state reference
setCount(count + 1) // Can be stale in async scenarios
基于前值的函数式更新 setCount(prev => prev + 1) 是默认写法,直接引用闭包中的旧状态在异步场景下会读到陈旧值——这与不可变原则一脉相承。
5.4 条件渲染
// PASS: GOOD: Clear conditional rendering
{isLoading && <Spinner />}
{error && <ErrorMessage error={error} />}
{data && <DataDisplay data={data} />}
// FAIL: BAD: Ternary hell
{isLoading ? <Spinner /> : error ? <ErrorMessage error={error} /> : data ? <DataDisplay data={data} /> : null}
短路与逻辑(&&)优于嵌套三元的「ternary hell」。
六、API 设计标准
6.1 REST 约定
GET /api/markets # List all markets
GET /api/markets/:id # Get specific market
POST /api/markets # Create new market
PUT /api/markets/:id # Update market (full)
PATCH /api/markets/:id # Update market (partial)
DELETE /api/markets/:id # Delete market
# Query parameters for filtering
GET /api/markets?status=active&limit=10&offset=0
资源用名词复数、方法语义明确(PUT 全量更新 / PATCH 部分更新)、过滤与分页走查询参数。
6.2 统一响应格式
// PASS: GOOD: Consistent response structure
interface ApiResponse<T> {
success: boolean
data?: T
error?: string
meta?: {
total: number
page: number
limit: number
}
}
// Success response
return NextResponse.json({
success: true,
data: markets,
meta: { total: 100, page: 1, limit: 10 }
})
// Error response
return NextResponse.json({
success: false,
error: 'Invalid request'
}, { status: 400 })
成功与失败共享同一个 ApiResponse<T> 泛型外壳,客户端可以按单一契约解析。
6.3 输入校验(Zod schema)
import { z } from 'zod'
// PASS: GOOD: Schema validation
const CreateMarketSchema = z.object({
name: z.string().min(1).max(200),
description: z.string().min(1).max(2000),
endDate: z.string().datetime(),
categories: z.array(z.string()).min(1)
})
export async function POST(request: Request) {
const body = await request.json()
try {
const validated = CreateMarketSchema.parse(body)
// Proceed with validated data
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json({
success: false,
error: 'Validation failed',
details: error.errors
}, { status: 400 })
}
}
}
这正对应规则层的「输入校验」条款:在系统边界校验、优先 schema 化、快速失败并给出清晰错误信息、永不信任外部数据。校验失败时返回 details: error.errors,把 schema 级错误明细透出给调用方。
七、文件组织与命名
7.1 项目结构
src/
├── app/ # Next.js App Router
│ ├── api/ # API routes
│ ├── markets/ # Market pages
│ └── (auth)/ # Auth pages (route groups)
├── components/ # React components
│ ├── ui/ # Generic UI components
│ ├── forms/ # Form components
│ └── layouts/ # Layout components
├── hooks/ # Custom React hooks
├── lib/ # Utilities and configs
│ ├── api/ # API clients
│ ├── utils/ # Helper functions
│ └── constants/ # Constants
├── types/ # TypeScript types
└── styles/ # Global styles
7.2 文件命名
components/Button.tsx # PascalCase for components
hooks/useAuth.ts # camelCase with 'use' prefix
lib/formatDate.ts # camelCase for utilities
types/market.types.ts # camelCase with .types suffix
规则层 coding-style.md 在此之上给出了量化约束:多小文件 > 少大文件,单文件 200–400 行为常态、800 行为软性维护上限(测试、生成代码、vendored 文件可例外),按功能/领域而非按类型组织目录。这套量化阈值与 rules/common/code-review.md 的审查清单完全对齐——「函数 < 50 行」「源文件处于 800 行软上限内或附例外理由」都直接写入了合并前的检查项。
八、注释与文档
8.1 何时写注释:解释 WHY,而非 WHAT
// PASS: GOOD: Explain WHY, not WHAT
// Use exponential backoff to avoid overwhelming the API during outages
const delay = Math.min(1000 * Math.pow(2, retryCount), 30000)
// Deliberately using mutation here for performance with large arrays
items.push(newItem)
// FAIL: BAD: Stating the obvious
// Increment counter by 1
count++
// Set name to user's name
name = user.name
该示例同时示范了两类合格注释:解释算法选择动机(指数退避防止故障期压垮 API),以及为「规范例外」留痕(大数组场景刻意使用 mutation)。
8.2 公共 API 的 JSDoc
/**
* Searches markets using semantic similarity.
*
* @param query - Natural language search query
* @param limit - Maximum number of results (default: 10)
* @returns Array of markets sorted by similarity score
* @throws {Error} If OpenAI API fails or Redis unavailable
*
* @example
* ```typescript
* const results = await searchMarkets('election', 5)
* console.log(results[0].name) // "Trump vs Biden"
* ```
*/
export async function searchMarkets(
query: string,
limit: number = 10
): Promise<Market[]> {
// Implementation
}
完整 JSDoc 应覆盖 @param(含默认值)、@returns、@throws 与 @example,让公共 API 的行为契约自解释。
九、性能最佳实践
9.1 记忆化
import { useMemo, useCallback } from 'react'
// PASS: GOOD: Memoize expensive computations
// Copy before sorting - Array.prototype.sort mutates in place
const sortedMarkets = useMemo(() => {
return [...markets].sort((a, b) => b.volume - a.volume)
}, [markets])
// PASS: GOOD: Memoize callbacks
const handleSearch = useCallback((query: string) => {
setSearchQuery(query)
}, [])
注意 [...markets].sort(...) 的写法:Array.prototype.sort 原地修改数组,先拷贝再排序——性能技巧与不可变原则在这里合流,注释行正是 8.1 节「解释 WHY」范式的实例。
9.2 懒加载
import { lazy, Suspense } from 'react'
// PASS: GOOD: Lazy load heavy components
const HeavyChart = lazy(() => import('./HeavyChart'))
export function Dashboard() {
return (
<Suspense fallback={<Spinner />}>
<HeavyChart />
</Suspense>
)
}
9.3 数据库查询
// PASS: GOOD: Select only needed columns
const { data } = await supabase
.from('markets')
.select('id, name, status')
.limit(10)
// FAIL: BAD: Select everything
const { data } = await supabase
.from('markets')
.select('*')
只取需要的列、显式限制条数。审查规则 code-review.md 的性能清单进一步补充了 N+1 查询、缺分页、无界查询、缺缓存四类问题——这些是「查询侧性能」的系统化检查项,与本文的列选择约束互为补充。
十、测试标准
10.1 AAA 结构(Arrange / Act / Assert)
test('calculates similarity correctly', () => {
// Arrange
const vector1 = [1, 0, 0]
const vector2 = [0, 1, 0]
// Act
const similarity = calculateCosineSimilarity(vector1, vector2)
// Assert
expect(similarity).toBe(0)
})
10.2 测试命名
// PASS: GOOD: Descriptive test names
test('returns empty array when no markets match query', () => { })
test('throws error when OpenAI API is missing', () => { })
test('falls back to substring search when Redis unavailable', () => { })
// FAIL: BAD: Vague test names
test('works', () => { })
test('test search', () => { })
测试名即规格说明:主语 + 条件 + 期望结果(如「Redis 不可用时回退到子串搜索」),works、test search 这类名字被明确判为坏味道。
十一、代码坏味道检测清单
技能将高频反模式固化为三类可检测清单,这也是「审查」场景下该技能最直接的交付物:
11.1 长函数(> 50 行)
// FAIL: BAD: Function > 50 lines
function processMarketData() {
// 100 lines of code
}
// PASS: GOOD: Split into smaller functions
function processMarketData() {
const validated = validateData()
const transformed = transformData(validated)
return saveData(transformed)
}
11.2 深层嵌套(> 4~5 层):用早返回压平
// FAIL: BAD: 5+ levels of nesting
if (user) {
if (user.isAdmin) {
if (market) {
if (market.isActive) {
if (hasPermission) {
// Do something
}
}
}
}
}
// PASS: GOOD: Early returns
if (!user) return
if (!user.isAdmin) return
if (!market) return
if (!market.isActive) return
if (!hasPermission) return
// Do something
11.3 魔法数字:用命名常量替代
// FAIL: BAD: Unexplained numbers
if (retryCount > 3) { }
setTimeout(callback, 500)
// PASS: GOOD: Named constants
const MAX_RETRIES = 3
const DEBOUNCE_DELAY_MS = 500
if (retryCount > MAX_RETRIES) { }
setTimeout(callback, DEBOUNCE_DELAY_MS)
常量命名遵循 UPPER_SNAKE_CASE(如 DEBOUNCE_DELAY_MS 带单位后缀),与 4.1 节命名约定呼应。
十二、与 ECC 仓库工程实践的印证
技能不是孤立的文档:它给出的每条阈值,都能在 ECC 仓库自身的工程配置中找到执行机制。
- 审查清单闭环。rules/common/code-review.md 把技能中的软性约定变成硬性合并门槛:函数 < 50 行、文件 < 800 行(或附例外理由)、嵌套 ≤ 4 层、错误显式处理、无硬编码密钥、无遗留
console.log、新功能有测试且覆盖率 ≥ 80%。它还定义了严重度分级——CRITICAL(安全漏洞/数据丢失风险)必须 BLOCK、HIGH 应 WARN、MEDIUM(含「超过 800 行软上限且无说明」)为 INFO、LOW 为 NOTE——这意味着「文件过长」这类坏味道会被正式记入审查意见。 - Lint 兜底。仓库根部的 eslint.config.js 将
no-unused-vars、no-undef设为error、eqeqeq设为warn,并对_前缀参数提供豁免——「未使用变量」「未定义引用」「非严格相等」这类可由工具机器判定的问题交给 ESLint,技能与规则层则聚焦于机器难以判定的命名语义、结构坏味道与设计取舍,形成「机器检查 + 规范审查」的双层防线。 - 规则层与技能层分离。rules/common/coding-style.md 末尾附有一份「代码质量检查清单」(函数 < 50 行、文件 < 800 行、嵌套 ≤ 4 层、无 mutation、无硬编码值等勾选项),与技能全文形成「短规则层 + 完整技能走查」的两级分发,Agent 可按上下文深度选择加载哪一级。
十三、落地建议:如何在你的项目中使用这套技能
结合技能文档与 ECC 的安装体系,推荐以下使用路径:
- 按模块安装:该技能归属
framework-language安装模块(见 manifests/install-components.json),在 ECC 的选择性安装流程中选择框架/语言模块即可获得 coding-standards 及配套的语言/框架技能。 - 审查场景显式调用:对 Agent 使用元数据中的默认提示「Use $coding-standards to review code against cross-project standards」,或依赖其
allow_implicit_invocation: true在代码质量审查任务中自动激活。 - 分层使用:日常快速对齐用规则层 rules/common/coding-style.md 的一页清单;完整走查、新人 onboarding、新项目初始化时加载完整技能;React 细节交给
frontend-patterns,服务端细节交给backend-patterns/api-design。 - 审查前自检:合并前对照 code-review.md 的清单逐项勾选,按 CRITICAL/HIGH/MEDIUM/LOW 分级处理审查意见。
总结:ECC 的 coding-standards 技能把「命名要描述性、默认不可变、KISS/DRY/YAGNI、错误显式处理、坏味道可枚举」这些跨语言公约数,组织成一份带 PASS/FAIL 对照示例、量化阈值(函数 50 行、文件 800 行、嵌套 4 层)和明确作用边界的可执行规范,并通过规则层短清单、审查清单与 ESLint 配置三层机制保证其在 Agent 工作流中被持续执行。正如文档末尾所强调的:代码质量不可谈判(Code quality is not negotiable)——清晰、可维护的代码才能支撑快速开发与有信心的重构。
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 StartedRust0623
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