| name | coding-standards-common |
| description | Use when writing, reviewing, or modifying source code in any language (Java / TypeScript / JavaScript / Dart / Python / Kotlin / Go / Vue / React 等). 跨语言通用编码铁律 7 条 + 注释三档,语言专属 skill (java-coding-standards / korepos-backend-service 等) 在此基础上叠加。MUST 自动触发,不需用户显式要求。 |
通用编码规范(跨语言)
适用于一切源码语言。语言专属规则(如 java-coding-standards 的阿里黄山版独占条款、dart-coding-standards 的 Effective Dart·dartdoc 独占条款、korepos-backend-service 的 Flutter backend 规则)在此基础上叠加,不重复。
触发链路:coding-standards-common(通用) → {language}-coding-standards(语言专属)。任何源码 Edit/Write 前先满足本 skill 的 7 条铁律,再走语言专属。
1. 命名表意
- 名字解释「意图」而非「类型」或「实现细节」
- 禁拼音+英文混用,禁纯拼音命名
- 常量全大写加下划线(语言惯例允许时),如
MAX_RETRY_COUNT
- 类/类型名
UpperCamelCase;方法/变量名 lowerCamelCase(语言惯例允许时)
- 布尔字段不要以
is_ / get_ 前缀(JSON / POJO 反序列化兼容性问题)
- 接口实现类一律
XxxImpl;数据/传输/展示对象用 XxxDO/DTO/VO,禁混用
- Service / DAO / Repository 方法前缀:获取单个
get,获取列表 list,统计 count,插入 save/insert,删除 remove/delete,修改 update
2. 函数原子
- 单一职责:一个函数只在一个抽象层级上做一件事
- 函数体硬阈值 80 行;超出按业务步骤拆
_xxxStep 私有方法,主方法只做编排 + 事务 + 日志
- 参数 ≤4;再多就收成 Request / Options / Config 对象
- 嵌套 ≤3 层;再深用 early return、抽方法或卫语句
- 一个文件一个公开类型(语言惯例允许时)
- 构造函数禁写业务逻辑;getter/setter 不写副作用
2.5 业务场景分流拆分(高内聚 / 低耦合的函数粒度落点)
§2 的 80 行硬阈值从代码量约束;本条从业务语义约束。两者叠加:先按业务场景拆,再看每个分支是否还需要按代码量拆。1-100 期扩展既有 service 时最常违反——加新业务类型时阻力最小的方式是"在已有方法里 +一个 else if",但这会让两种业务定位的逻辑黏死,未来变更相互波及、无法独立测试。
- 函数内按业务类型 / 枚举值 if-else / switch 分流 ≥2 个分支时,禁止在同一 public 方法体内堆叠所有分支的处理逻辑
- 分支共享同一业务定位(同一状态机 / 同一校验 / 同一下游 / 同一补偿)→ 阶梯 1:抽
_handleTypeA() / _handleTypeB() 私有方法,主方法只做分流派发
- 分支差异本质是不同业务定位(不同业务实体 / 独立状态机 / 不同 PRD 模块 / 不同团队)→ 阶梯 2:升级到 service 级拆分,详见
architecture-ddd-lite-fullstack 「函数级业务场景分流」节
- 判定锚点是「业务定位」而非「代码相似度」——长得像但业务定位不同就要拆;业务定位相同即使代码差异较大也归阶梯 1
- 共享逻辑沉到原子能力层(
common/services/ / common/backend_infra/services/ 等),不要塞进同一 public 方法用 if-else 区分
3. 层次分明 / 单向依赖
- 上层调下层,下层不反调上层;UI / Controller / Endpoint / Page 禁直接执行 SQL / HTTP / 第三方 SDK
- 同层禁互相 import;Service 之间不互调,跨场景复用沉到
orchestrator 或原子能力层
- 跨 feature / 跨模块复用走
common/ 或等价的原子能力目录,禁复制粘贴
- 业务层禁直接依赖框架细节;领域模型禁带技术框架注解(JPA / Spring / Drift 等)
- 数据对象禁混层使用:DO 留持久层、DTO 走传输边界、VO 给展示层
4. 零魔法值
- 任何有业务含义的数字 / 字符串 / 协议码 → 必须命名常量、枚举或 const
- 例外仅:
0 / 1 / -1、true / false、空串 ""、空集合、单元测试断言字面量
- 与 DB 字段值 / 协议码 / 状态机绑定的数字 → 强制枚举,禁裸数字字面量(如
state == 3、item_type=1)
- 阈值类常量(如
MAX_RETRY = 3)用 const 并写一行 WHY 注释说明阈值依据
- 浮点比较禁
== / equals;金额类用 BigDecimal 或差值 ≤ 容差(POS 场景常用 ±0.005)
5. 注释(三档铁律 —— 全员都要写,但都要短)
立场:类、方法、核心代码块都必须写注释,但每档都有「简要」上限。优先讲 WHY、当前职责、约束;不允许把变更历史、设计史、实现步骤流水写进源码。
注释放置原则(总纲):注释主要落在声明位置——类 / 字段 / 方法头。函数体内除 §5.3 六类核心块外不写注释;逻辑的可读性靠拆函数 + 命名表达,不靠行内注释堆砌。函数体里冒出大段行内注释,基本等于"该拆的函数没拆""命名没起好"——先改代码结构,而不是补注释。每一档都遵循"一句话讲清"的最短表达,多余的删。
5.0.0 写注释前 3 秒自检(动手前先过一遍)
反复违规的 AI 行为:私有方法 / 内部代码块上方堆 5-12 行 dartdoc / 行内 WHY 注释解释"前端契约演变 / 旧实现 vs 新实现 / 上下游字段口径 / 不走本分支也不受影响"。先用这 4 问自检,有 1 项答错就回去删:
- 可见性问题:这是不是公开接口(public 方法 / 对外 API)?
- 是 → 允许 1-2 行 doc 描述能力 + 关键参数 / 返回语义
- 否(private / 内部 helper)→ 函数名 + 1 行职责就够,禁止写参数契约 / 上下游字段口径 / 跨场景影响
- 行数问题:这条注释超过本档硬阈值了吗?(类 ≤3 / 方法 ≤2 + 参数 doc tag / 行内 ≤1)
- 超出 → 多出来的部分必属于 commit body / bug doc / design doc 之一,就地删,不上提
- 历史问题:这条注释里有没有"曾要求 / 现已统一 / 旧契约 / 之前 / 后来 / v.X / 早期版本"这类词?
- 有 → 100% 是变更史,全删,git log + commit body 已经记录
- 场景问题:这条注释是不是在讲"另一个分支怎么走 / UI 入口不走本分支 / 旁路场景不受影响"?
- 是 → 删。当前代码块只描述自己;旁路场景的影响面归反向索引 / design doc
5.0 注释语言 = 当前会话沟通语言(沟通语言一票否决,无存量豁免)
- 沟通语言一票否决:本次会话用户用什么语言沟通,新增 / 修改注释一律用同一语言(含 doc comment、行内注释、TODO)。中文沟通 → 中文注释;英文沟通 → 英文注释。这条没有任何"存量文件已是另一种语言所以跟着写"的退路。
- 不沿袭存量:即使被改的文件原本是另一种语言注释,新增 / 修改的注释也按沟通语言写,不要为了"保持文件统一"放弃这条规则。短期内同一文件出现中英混杂可接受,后续重构再统一,不要为了避免混杂而违背沟通语言。
- 用户明确要求特定语言("用英文写"/"按公司规范全英文"等)时,按用户要求执行;该选择在当前会话内保持,不必每次反复确认。这是唯一能覆盖沟通语言默认的合法路径。
- 例外字面量:专有名词、API / 类 / 字段 / 错误码字面量、外部协议术语保留原文,不算混用。
- 该规则只约束源码注释;commit message 语言由
git-commit-standards 控制(默认中文 body),设计 / bug / 知识图谱文档语言由对应 doc skill 控制。
判断准绳:不要把"英文是行业默认"当成默认,不要把"中文更亲切"当成默认,也不要把"原文件是英文"当成默认。沟通语言就是默认——读你代码的下一个人多半就是这次会话里的协作者。
5.1 类 / 文件级 — 必须,1–3 行
写清:
- 这个类做什么(业务职责,不是实现细节)
- 属于哪一层 / 哪个模块
- 与其他关键类的协作关系(被谁调用、依赖谁)
只保留「当前职责 + 最容易误改的不变量」。算法主干 / 数据源选型 / 复合场景推演 / 长篇契约说明不进类注释——移到 design doc / 业务文档。把类头写成一篇几十行的小设计文档是最常见的超标形态,1-3 行讲不下的,说明它属于文档而不是源码。
5.1.5 字段 / 成员级 — 可选,一行简短
写清(只在不自解释时写):
- 业务含义 / 单位 / 取值约束(如
// 金额,单位分 / // 状态机当前态,见 OrderState)
- 不写类型(类型代码已声明)、不写重复字段名的废话(
amount: 金额 = 废话,删)
- 自解释字段(
userId / createTime / name)省略注释,不要为凑注释而注释
- 一行讲不清的复杂字段,说明它属于哪个枚举 / 哪张表 / 哪段业务,细节归 design doc,不在字段上铺开
- 类头与字段注释不重复:类头讲整体业务口径 / 不变量,字段只补非自解释的点;同一信息不要在类头和字段各写一遍(类头已说明
payAmount / tipAmount 口径,字段就别再逐个复述)
5.2 方法 / 函数级 — 必须,1–2 行说明 + 参数 / 返回 / 异常
写清:
- 业务意图(禁止重复方法名)
- 每个非平凡参数的含义和约束(空值规则、取值范围)
- 返回值的业务含义(失败语义:抛异常 vs 返回 null vs 返回错误码)
- 可能抛出的异常及触发条件
- 语言有 doc comment 语法(Javadoc / TSDoc / Dart doc / docstring)的优先用 doc comment
5.2.1 职责边界注释 — 推荐,仅限原子能力 / 领域服务
只对「原子能力方法」和「领域服务 / focused service 的公开类」可写「职责 / 不负责」契约清单——不负责 那部分正是 §5.1 要保留的「最容易误改的边界」,对人和 AI(知识图谱沉淀)都有效:
/// 原子能力:登记退款终态
///
/// 职责:1. 更新退款状态 2. 写退款流水 3. 发本地事件
/// 不负责:1. 调支付渠道 2. 校验退款金额 3. 更新订单支付状态
Future<void> registerRefundSuccess(RefundOrder order) async {}
硬约束(否则退化成 §5.1 禁止的「类注释 = 小设计文档」):
- 每条 ≤1 行、是契约边界,不是算法主干 / 数据源选型 / 场景推演 / 迁移史(这些仍进 design doc)
- 职责 + 不负责合计控制在 ~8 条以内,讲不下说明该拆类 / 该写文档
- 普通 DTO / Widget / 工具类 / Controller 不用这种形式,守 §5.1 的 1-3 行
5.3 核心代码块 — 必须,1 行
以下场景必加行内注释:
- 业务规则判断(如
// 评分阈值:>=70 视为通过)
- 非显而易见的技术决策(如
// 用 ConcurrentHashMap 因多线程并发)
- 魔法数字 / 阈值含义(如
// 最大修正次数,超过不再重试)
- 容错 / 降级 / 重试逻辑(如
// 向量库不可用时降级为纯 LLM 分析)
- 并发 / 锁 / 事务边界
- TODO / FIXME 必须带原因和负责人(如
// TODO(zhangkai): 等 v1.22 接入新协议后删除)
5.4 禁止(注释红线 —— 全语言、全场景单一来源)
本节是注释禁令的唯一规则源。bug 修复 / 联调 / 删冗余 / 重构 / 新功能 / 任何源码 Edit/Write 都遵循同一份红线,bugfix-coding-style 不再重复定义,只承担"bug 修复期推荐写法 + 旧标记顺手清理"的应用层指引。
- 注释掉的旧代码 → 直接删,VCS 自有历史
- 变更日志 / 变更标记:
[BUGFIX] / [DEPRECATED] / [ADDED] / [REWRITTEN] / [MODIFIED] / [FIX] / [FIXED] / 日期标记 / 版本号标记 / PR 号 / Issue 号 / Ticket 号 / 内部流水号引用(如 // 详见 v6 调整流水 2026-04-25 条目)→ 进 git commit body / bug doc / design doc
- 段落式 section divider(
// ===== [ADDED 2026-04-28] 校验链路 + 分摊编排私有方法 ===== / // --- xxx --- 风格的分节标题带日期)→ 全删,文件结构用方法分组 + 类层级表达,不用注释画分割线
- 段落式设计史 / 实现步骤复盘 / 实现步骤流水 / 未来版本计划("旧实现忽略 A/B/C、新实现第 1/2/3 步、v1.1 计划") → 进 design doc / bug doc
- 重复函数名的废话(
getUser: 获取用户)
- TODO / FIXME 格式
TODO(负责人): 原因:负责人必填(问责到人,不是甩锅)、原因必填、禁止带日期。反例:无原因 / 无负责人的裸 // TODO(噪声);带日期的 // TODO(zhangkai 2026-04): ...(日期归 VCS / 任务系统)。能开任务的优先开任务,而不是把 TODO 长期留在源码
- 业务规则的长篇科普(超过 3 行) → 进 design doc 或 bug doc
- 私有方法 / 内部 helper 的多段 dartdoc(超过 2 行讲"前端契约 / 上下游字段 / 旧契约 vs 新契约 / 跨方法不变式")→ 私有方法不是公开接口,函数名 + 1 行职责就够;契约描述只属于公开接口
- 行内 WHY 注释超过 1 行(代码块上方堆 3-5 行讲"另一分支怎么算 / UI 入口不走本分支 / 与某表字段口径对齐")→ 压成 1 行,或彻底删掉让 commit body / bug doc 自承载
- 宿主语言里内嵌的 SQL 字符串内部不写
-- 注释(drift customSelect / JDBC / MyBatis 拼接 SQL / 任何字符串字面量里的 SQL):SQL 经常被复制调试、拼接、压缩、日志输出,内嵌 -- 是噪声,还让 DAO 读起来像"SQL 文档"。口径说明放到 customSelect(或等价调用)之前的宿主语言注释(Dart ///·// / Java //),且遵守行内 WHY ≤1 行。注:check-comment-density.js 会先剥离字符串字面量再判定,抓不到 SQL 串里的 --,这条靠本规则 + 评审 + comment-cleanup
- 扩展 / 维护指南("新增 X 类型时第 1/2/3 步怎么做" / "以后接入 Y 时改这里")→ 属于维护文档 / design doc,不是当前代码契约,源码里删或迁移
- 方法头逐条复述代码已直观表达的逻辑(把分支条件、状态码映射、fallback 一条条写一遍,而下面代码本身就很直观)→ 代码自解释的别用注释翻译一遍;压成 1-2 行讲算法意图 + 兜底规则即可
注释密度是信号,不是达标项:单文件注释占比畸高(如 >40%)、或连续注释块超过 6 行,基本等于"该拆的函数没拆 / 该迁的背景没迁"。降密度靠拆函数 + 命名自解释 + 把复杂背景迁到 design doc,不靠继续堆注释。check-comment-density.js 只机械抓连续块,占比畸高靠本条 + 评审判断。
5.4.1 字面反例与原因对照(具体字符串 + 为什么禁)
列项是抽象规则,字面表是 AI 训练数据里最常学到的反模式具象样本。看到字面就该回避,不要让"规则我懂但具体代码我不确定"成为豁免理由。
| 反例字面 | 原因 |
|---|
// [BUGFIX 2026-04-30] 旧实现用 transaction_no 反查... | 变更日期 / 原因属于 git log,不属于代码 |
// [DEPRECATED 2026-04-25] 这段以前是 X,现在改成 Y | 完全属于 commit message |
// [ADDED 2026-04-25] 对齐云端 RefundServiceImpl#handleX(L1063-1070) | 对齐依据应放方法 doc comment 的"对齐云端"段(不带日期),或写进 design / bug 文档 |
// [REWRITTEN 2026-04-28] 按 xxx 重写... 后接旧实现问题 / 新实现步骤 / 未来版本计划 | 变更流水 + 设计文档摘要,应写进 commit body / 设计文档;源码只保留当前行为和必要 WHY |
大段被 // 注释的旧代码 | 增加噪声、容易腐烂、git 已经留底 |
// ===== [ADDED 2026-04-28] 校验链路 + 分摊编排私有方法 ===== 风格分节标题 | section divider 注释 = 文件结构没拆好;用方法分组 + 类层级表达,不用画注释分割线,更不要在分割线里夹日期 / 版本 |
// 详见 v6 调整流水 2026-04-25 条目 | 引用易失效;让读者跳到外部文档才能理解的代码不合格 |
// PR #1234 / Issue #56 / Linear ticket KP-789 | 同上 |
// TODO(zhangkai 2026-04): 这里以后再优化 | 日期是噪声(归 VCS / 任务系统),且无具体原因。正确:// TODO(zhangkai): 等 v1.22 协议接入后删——负责人 + 原因、不带日期;更应开任务 |
// 查询用户 / // 判断为空 / // 遍历集合 / // 设置状态(紧贴下一行同义代码) | 复述代码 what,零信息量。删——靠命名自解释,要写就写 why(// 异步退款需等回调,此处先返回) |
| 函数头连续十几行说明"旧实现忽略 A/B/C、新实现第 1/2/3 步、理论上一定完成、v1.1 计划" | 函数头注释过载。当前职责写在 doc comment,步骤说明拆到对应代码块附近 |
私有方法 _validateXxx 上方写 12 行 dartdoc 讲"前端契约 / 早期版本曾要求 X / 现已统一为 Y / 再加会双计" | 私有方法不是公开接口,契约演变史 ≠ 当前职责。1-2 行讲"当前校验什么"即可;契约迁移属于 commit body / bug doc。函数名 _validateMethodsAmountSum 已自解释,旧契约描述全删 |
| 代码块上方堆 5 行行内注释讲"cancel 桥接路径 / 联台按 scaleRatio 缩 / 否则与 income_refund_service_fee_amount 口径不一致 / UI 部分退款入口不走本分支不受影响" | §5.3 行内注释硬阈值 1 行。"另一分支怎么走 / 不走本分支也不受影响" = 旁路场景影响面叙事,归反向索引 / design doc。压成 1 行 WHY 或彻底删 |
resolveOrderBusinessState 方法头把分支条件、状态码映射、fallback 完整复述一遍(下面代码本身直观) | 复述代码 = 噪声。压成 1-2 行讲算法意图:"按云端算法据 payType/orderState/pickUpState 推导业务状态,未知值兜底待下单",不逐条翻译代码 |
RefundTipPolicyService 类头 60+ 行写关键契约 / 算法主干 / 数据源选型 / 复合场景推演 | 类注释写成了小设计文档(§5.1)。类头只留当前职责 + 最易误改的不变量,算法主干 / 选型 / 场景推演进 design doc |
CancelRefundPlanner 注释"新增订单类型时第 1/2/3 步怎么做" | 扩展 / 维护指南 ≠ 当前代码契约,进维护文档 / design doc,源码删 |
RefundTxSnapshot 类头已解释 payAmount/tipAmount,字段上再逐个解释一遍 | 类头与字段重复(§5.1.5)。类头讲整体口径,字段只补非自解释点,同一信息不写两遍 |
| 函数体内连续多段讲"反结 / 金额维度 / 失败信号 / retry 冲正"(占函数大半篇幅) | 函数体大段流水账违反 §5 放置原则 + §5.3。有价值的 WHY 压成少量核心规则行内注释,复杂背景移 design doc / 业务文档 |
customSelect 的 SQL 字符串里堆 7 行 -- tip_amount 在联合支付时.../-- 复制写到 orders 上.../-- 与 bill.tip_amount 同口径...(refund_products_dao.dart) | 内嵌 SQL 不写 -- 注释。压成 1 行放到 customSelect 之前的 Dart 注释:// 联单 tip 是整桌快照,取 MAX 避免兄弟桌重复累加,SQL 串本身保持干净 |
判定准绳:删掉这条注释,下一个改这段代码的人会不会犯错?会则保留(短句),不会则删。
机械兜底(v1.29 起;v1.35 起默认硬阻断):hooks/check-comment-density.js(PreToolUse Write/Edit/MultiEdit,默认 block)扫本次新增内容的注释,命中变更标记 / 日期 / 工单号 / 带元信息分节线 / 版本流水措辞等客观红线即 exit 2 硬阻断;long-block(连续注释块超阈值)是启发式软规则,只提示不阻断——避免误伤公开 API 的长 dartdoc。hook 只抓客观无歧义项,prose 式实现史 / 私有方法契约史仍靠本节规则 + 评审判断,不能因"hook 没报"就放行。TEAM_STANDARDS_COMMENT_HOOK=warn 降级为仅提示、=off 关闭。
5.5 修改代码时同步清理过期注释 / 历史版本说明 / 废话注释
立场:改了逻辑就要校对周边注释。过期注释比没有注释更糟——它会主动误导下一个读你代码的人;历史版本说明 / 废话注释会污染文件、消耗后续阅读者的注意力。改到哪,清到哪。
A. 必须清理 —— 过期注释(与当前实现不一致)
- 修改方法 / 代码块时,必读上下文已有的 doc comment 与行内注释,核对是否仍与当前实现一致
- 不一致时就地修正或删除,不要保留"看起来还在"但实际已失效的注释
- 典型过期场景:
- 注释描述的参数 / 返回值 / 异常已经改名、改语义、被删
- 注释描述的分支 / 条件 / 状态值已经不存在
- 注释引用的旧类名 / 旧方法名 / 旧字段名 / 旧错误码
- 注释写的"临时方案 / 待重构 / TODO"对应的代码已经重构完
- 注释里的业务规则(阈值、状态机、协议码)与当前实现不符
B. 必须移除 —— 历史版本说明 / 变更痕迹(VCS 已经记录)
发现以下任意形态,改到该方法 / 代码块时一并删干净,不留痕:
- 变更日志型:
// [BUGFIX] xxx、// [DEPRECATED]、// [ADDED v1.2]、// 2024-03-15 修改、// PR#1234 调整
- 历史叙事型:
// 原本用 HashMap,后改为 ConcurrentHashMap、// 这里之前有个 bug,现在修了、// v1.0 写法,v2.0 重构
- 版本兼容说明:
// 兼容老版本 xxx(老版本已下线)、// 为了 v1.x 用户保留(已不需要)
- 临时备忘型:
// 暂时这么写、// 待优化(没有 owner / 没有触发条件,本质是垃圾 TODO)
- 注释掉的旧代码:整段被
// / /* */ 包起来的死代码,直接删
- 段落式设计史 / 实现步骤流水:函数头堆 5+ 行讲"我是怎么一步步实现的"
这些信息属于 git commit body / design doc / bug doc,不属于源码。源码只回答"现在是什么、为什么这么写"。
C. 必须删除 —— 废话注释(零信息量)
- 重复函数名 / 字段名:
// getUser: 获取用户、/** 用户ID */ Long userId(字段名已自解释)
- 重复代码字面量:
i++; // i 加 1、return null; // 返回 null
- 占位空注释:
// xxx、// TODO(无内容)、/** */ 空 doc 块
- 翻译式注释:把变量 / 方法名直译一遍中文,不补充任何 WHY
D. 操作边界
- 只清理被本次改动覆盖到的方法 / 代码块;不要顺手扫全文件做无关注释清理(避免 PR 噪声、避免冲突)
- 同一文件里如果有大量历史垃圾注释需要一次性清,单独开 PR做"注释清理",commit 里不夹带逻辑修改
- 删除注释不需要单独 commit,跟着本次代码改动一起提交即可;commit message 里不要逐条罗列删了哪些注释,一句"清理过期 / 历史注释"足够
- 与
bugfix-coding-style skill 完全对齐:bug 修复 PR 里禁止新增任何"修了什么 bug / 之前怎么错的"的源码注释,要写进 commit message 或 bug doc
5.6 简要原则
类 1–3 行,方法 1–2 行,代码块 1 行。写不下就说明你想塞实现细节,那部分应该进文档而不是源码。
6. 异常不静默
catch 必须处理或显式往上抛,禁空 catch
- 日志必须含现场参数 + 完整堆栈;禁只打
e.getMessage()(等价于把堆栈丢了)
- 禁
try-catch 做流程控制
finally 中禁 return(会吞掉 try 的返回值或异常)
- 有事务的
catch 块必须手动回滚事务
- 对外接口用错误码 / Result 包装表达业务结果;RPC / 远程接口须捕获所有异常
7. 删冗余 / DRY:先分「知识」还是「逻辑」
DRY 的本意(《程序员修炼之道》)是知识只有唯一、权威的表示,不是"代码不能重复"。把 DRY 误读成"消灭一切重复代码"会导致过早抽象;把它误读成"两处才算重复、约定也容忍复制"又会让约定/常量在多处复写、改一处漏一处。先分类,再决定:
- 知识 / 约定 / 常量 / 契约 / 配置(枚举值含义、状态映射、协议码、URL、阈值、字段口径、默认值)→ 单一事实来源(SSOT),从第一次出现就只存一处,其余引用它,连复制一次都不该。这才是 DRY 的本意。
- 落地:命名常量 / 枚举(见 §4)、配置项、把含义标注在定义处(如枚举常量的描述注解)而不是在每个调用处 / prompt / 注解里各抄一遍。
- 逻辑 / 代码结构(算法、流程、分支处理)→ rule of 3:两处容忍,三处才抽公共方法,避免过早抽象 / 过度设计;三个相似分支再抽抽象(YAGNI)。
- 动手前先问一句:我要写的这块,是不是某处已有的知识 / 常量 / 约定的复制?是 → 先建 / 找到单一来源再引用,别复制;若重复的是逻辑 → 数到三再抽。
- dead code 直接删,不留注释占位。
- 重构与删除分开提交,避免一次 PR 既改行为又改结构。
- 重复使用 ≥2 feature 的业务计算 → 沉淀到原子能力层(
common/services/ / common/backend_infra/services/ 等)。
7.5 外部 API / 不熟悉的库:先核验,不臆造
- 用不熟悉的第三方库 / 框架 API 前,先核验签名再写——查官方文档 / 读源码 / IDE 跳转 /
javap 反编译,不凭记忆或直觉拼方法名、类名、import。
- 反例:凭印象写一个根本不存在的常量 / 方法 / import(编译期才暴露,白费往返;更糟的是"看起来对"的错误签名,跑起来才崩)。
- 拿不准的 API,先写最小验证(一次调用 / 一个
javap / 一行 REPL)确认存在,再大面积使用。
7.6 复用项目公共能力优先(编码前先查 coding-profile 的能力清单)
§7 讲「知识不重复表达」,本条讲「能力不重复造」。写功能前先分析项目已有的公共能力(公共组件 / 基类 / 工具类 / 注解 / 公共方法 / 公共封装),能复用就复用——这是最基本的前端 / 后端要求。
- 禁止重复造轮子,禁止用语言 / 浏览器原生实现替代已有公共封装。典型反例:前端放着公共确认弹框不用却写原生
confirm() / alert();后端放着 BaseService 的分页 / 统一返回不用却手撸。
- 判定顺序:要写某能力 → 先查项目有没有现成的 → 有则复用 → 没有再写,且若属可复用能力,沉淀到公共 / 原子能力层(见 §7)。
- 能力清单来源(单一):项目公共能力清单见该项目 coding-profile(
project-coding-profiles 插件)下的 common-capabilities.md(前后端公共能力 + 优先复用 Top 清单 + 编码前自检)。编码前必读;无该文件的项目,先在其 profile 补一份。
- 强制与清单落在项目侧,纪律落在这里:具体「有什么」(清单)和「机械拦截」(如
check-frontend-controls.js 拦原生弹框)由各项目 coding-profile 承担;本条只定义跨项目通用纪律「先查、优先复用、禁造轮子 / 禁原生替代」。
7.7 新增全局注册名:先查重,别假设唯一(AI 易错)
§7.5 讲「外部 API 先核验」,§7.6 讲「公共能力先复用」,本条讲「全局注册名先查重」——三条同属「动手前先查项目里已有的」。AI 反复违规行为:新建一个带框架注册语义的标识符时,只盯着自己这个类 / 文件,默认这个名字全局唯一,撞名了往往到启动期 / 运行期才暴露,白费往返。
- 下手前先在注册范围内查重名,凡是会进某个全局命名空间的标识符都适用:
- Spring bean 名(
@Component 系默认 = 简单类名,详见 java-coding-standards §11)
- HTTP 路由 /
@RequestMapping 路径、消息队列 topic / 事件名
- DI token、
@Qualifier 名、配置项 key、feature flag key
- DB 表名 / 迁移文件名(migration name)、定时任务名、i18n / 文案 key
- 判定顺序:要新增一个注册名 → 先在其注册范围内搜同名 → 无冲突再写;可能冲突 → 显式赋唯一限定名(模块前缀 / 命名空间),别靠「应该不会撞吧」蒙混。
- 别假设唯一:多模块共用 base package、同一 component-scan / 同一路由表 / 同一事件总线时,「不同包 / 不同模块」不等于「名字不同」。撞名常常成对出现(一个撞了,配套的兄弟类大概率也撞),一并核对。
- 框架专属的默认命名规则与机械细节落在语言 skill(如
java-coding-standards §11 的 Spring bean 命名);本条只定义跨语言通用纪律「新增全局名先查重」。
与语言专属 skill 的关系
| 通用 skill(本文件) | 语言 / 框架专属 skill |
|---|
| 命名表意 / 函数原子 / 层次分明 / 零魔法值 / 注释三档 / 异常不静默 / 删冗余 | java-coding-standards: 包装类比较、SimpleDateFormat、SLF4J 占位符、HashMap 容量、BigDecimal 比较、JDK8+ DateTimeFormatter、SQL 列名规范、索引规则等 Java / 数据库独占条款 |
| 同上 | korepos-backend-service: backend 目录结构、BackendInfra 边界、一接口一 service、Service 禁裸 SQL、跨 feature 业务原子能力、长方法拆 step、DB 字段值枚举绑定等 Flutter backend 独占条款 |
| 同上 | bugfix-coding-style: 禁源码内变更日志 / 函数头不堆复盘 / 复杂逻辑就近 WHY(本 skill §5.4 与之完全对齐) |
| 同上 | arch-lint: Flutter 5 类架构违规自动检测 |
触发顺序:任何源码 Edit/Write 前,先满足本 skill 的 7 条铁律 → 再走语言/框架专属 skill 的独占条款 → 最后由 coding-violation-log 在用户纠错时登记差异。
自检清单
写代码前 / 提交前过一遍以下 7 项,有 ❌ 必须改: