3065 字
约 10 分钟
16
S27-A04 · AI 开发规范怎么落地:从文档到门禁

规范文档写完发群里,点赞一片,三周后没人再提。半年后复盘发现:提交信息还是随手写,生成代码照样手工改,测试目录越来越像杂物间。问题不在规范内容,在于它从来没有执行入口。

这篇文章只谈一件事:哪些规范值得写、哪些只能靠别的手段,以及检查手段本身什么时候会失效。


1. 规范失效的三个原因

原因 典型表现 根因
写成愿望 "代码应当清晰、可维护" 无法判定,谁都可以说自己做到了
没有执行入口 只在 wiki 里,PR 时不出现 记住规范的成本由个人承担
没人检查 违规合并后无反馈 违反的代价为零,遵守的收益为零

三条里最容易修的是第二条。把规范搬到 PR 模板和 CI 里,等于把"记住"换成"被提醒",这一步的收益比写更细的规范大得多。


2. 该规定什么,不该规定什么

判据只有一条:能不能被一台不睡觉的机器判定。

值得写进规范 不值得写进规范
提交信息格式(type(scope): subject) "提交信息要有意义"
目录职责(adapters/ 只放外部集成) "代码结构要合理"
生成物必须带标记且不得手工改 "生成代码要看一遍"
新依赖需在清单里登记 "少引入依赖"
敏感文件禁入仓库(.env、密钥、样本) "注意安全"
PR 必填项(影响面、回滚方式) "充分自测"
命名后缀约定(*DTO、*Adapter) "命名要准确"

右列不是没价值,而是价值无法通过文字传递——它们靠评审、靠结对、靠让人看到一次好代码。写进规范只增加字数,不改变行为。


3. 能自动化的检查项

最小可用的门禁只需要五条,跑在 pre-commit 与 CI 两层:

#!/usr/bin/env bash
# scripts/guard.sh — 本地与 CI 共用,返回非零即阻断
set -euo pipefail

# 1) 提交信息格式
msg="$(git log -1 --pretty=%s)"
[[ "$msg" =~ ^(feat|fix|refactor|docs|test|chore|perf)(\([a-z0-9-]+\))?:\ .+ ]] \
  || { echo "提交信息不符合 type(scope): subject"; exit 1; }

# 2) 敏感文件禁入
if git diff --cached --name-only | grep -qE '(^|/)\.env($|\.)|\.pem$|\.p12$|(^|/)secrets/'; then
  echo "检测到敏感文件,已阻断"; exit 1
fi

# 3) 生成物必须带标记
for f in $(git diff --cached --name-only -- '*.java' '*.ts' '*.py'); do
  grep -qE '@generated|Code generated by|DO NOT EDIT' "$f" \
    || echo "warning: $f 未见生成物标记(如为手写请忽略)"
done

# 4) 依赖引入需登记
git diff --cached --name-only | grep -qE 'pom.xml|package.json|requirements.txt' \
  && { git diff --cached | grep -qE '^\+' && echo "请同步更新 docs/deps.md"; }

CI 侧只做一件事:把同一份脚本再跑一遍。

name: guard
on: [pull_request]
jobs:
  guard:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 2 }
      - run: bash scripts/guard.sh
      - run: bash scripts/check-generated.sh   # 重新生成后 diff 必须为空

第二条 check-generated.sh 是关键补充:生成物只在"重新生成后 diff 为空"时才算可合并,否则说明有人手工改过。

# check-generated.sh 核心断言
pnpm run codegen             # 或 mvn generate-sources
git diff --exit-code -- 'generated/**' \
  || { echo "生成物被手工修改,请改模板或源定义"; exit 1; }

4. 不能自动化的部分:写成可判定的问句

架构判断、命名语义、抽象是否多余,这些交给评审。但评审清单的写法决定它有没有用:

不可用的写法 可判定的写法
"设计是否合理?" "新增的这层转发,去掉后调用方要改几处?"
"命名是否清晰?" "这个 Manager 类里有没有两个以上不同生命周期的职责?"
"有没有考虑异常?" "外部调用失败时,这个方法的返回值和副作用分别是什么?"
"测试是否充分?" "新增分支里,哪一条没有被任何用例覆盖?"

放进 PR 模板的形式:

## 评审必答(每项一行,写不出就说明还没想清)
- 影响面:调用这个接口的服务有哪些?失败时它们的表现?
- 回滚:出问题后回到上一版本需要几步?有无数据迁移?
- 例外:本次是否有违反规范之处?若有,例外编号是?
- 不作为:这次刻意没做什么?留给谁、什么时候做?

四问的共同点是答案可以被别人反驳。形容词不行,"几步""哪些""有没有"可以。


5. 机器辅助的边界

检查越细,绕过越自然。这是可预期的博弈,不是道德问题:

检查项 常见的绕过方式 更稳的落点
方法行数 ≤ 50 拆成一串只有一个调用的小方法 检查认知复杂度上限,不查行数
圈复杂度 ≤ 10 用策略表把分支藏进 map 查分支覆盖,不查分数
注释率 ≥ 20% 灌无信息注释 只要求公共 API 有契约说明
TODO 数量 = 0 改写成 FIXME 或挪进 issue 要求 TODO 带责任人与日期
依赖数不增 塞进内部公共库再引入 查依赖树的传递引入

一条经验:能被一行 if 绕过的规则,最终都会被绕过,而且绕过后更难读。 宁可少两条规则,也别加一条可以便宜绕过的规则。


6. 生成代码的标记与责任归属

标记的作用不是"免责",是让下一个人知道该改哪里:

// @generated by openapi-generator 7.x from api/order.yaml
// DO NOT EDIT. 修改请改 api/order.yaml 后重新生成。
package com.example.order.api;

配套三条约定:

约定 落地方式 违反后果
生成目录只读 CODEOWNERS 指向生成器维护者 评审直接打回
生成物可重复 CI 重新生成后 git diff --exit-code 门禁红灯
责任归属 谁提 PR 谁负责,即使代码是模型写的 出问题找 PR 作者

第三条要写清楚,因为它的反面很常见:"这是 AI 生成的,我不知道"。责任随提交转移,与生成方式无关。


7. 例外流程

没有例外流程的规范,会被"悄悄违反"替代。

<!-- docs/exceptions/EXC-0007.md -->
- 规则:新依赖需登记在 docs/deps.md
- 原因:临时接入内部日志 SDK,其坐标未公开
- 影响:依赖清单与实际不一致,供应链扫描会漏
- 到期:2025-06-30
- 批准:@owner-a

四个字段缺一不可。到期日是最重要的一栏——例外没有期限,就等于规则被删除,只是没人宣布。

到期的例外要有人看:一个每周跑一次的脚本,扫 docs/exceptions/*.md 里的日期,过期的发到值班频道即可。

grep -H '^- 到期:' docs/exceptions/*.md \
  | awk -F':' '$2 < "'"$(date +%F)"'" { print $1 }'

8. 版本与演进

项目 建议 说明
归属人 一位,不是委员会 多人共管等于无人负责
存放位置 与代码同仓,docs/spec/ 能跟着 PR 一起改,评审可见
回顾周期 每季度一次,30 分钟 只看"哪条规则被绕过最多"
变更方式 提 PR 改规范,附一条触发它的真实案例 没案例的规则不加
废弃 显式删除并写进变更记录 留着不执行的规则会稀释全部规则的分量

季度回顾只回答一个问题:过去三个月,哪条规则被绕过最多? 答案要么是规则写错了,要么是该换成检查。两种都值得改。


9. 培训靠三件具体的东西

手段 具体做法 起效时间
新人第一周 第一天跑一遍 guard.sh,故意让它拦一次 当天
代码模板 脚手架生成的目录、注释头、测试骨架即规范 首次提交
示例仓库 一个被评审过的完整 PR,可对着抄 第一周内

第三条最容易被忽略也最有效:新人不是照规范写代码,是照最近的合并记录写代码。示例仓库过时,规范就等于没有。


10. 度量:看违反率,别看阅读量

指标 定义 为什么用它
规则违反率 每个 PR 被拦的规则分布 直接指出哪条规则最难遵守
返工率 同一 PR 因规范问题被改几轮 衡量规范是否增加摩擦
例外存量 未到期例外数 / 过期未关数 过期未关是最真实的失效信号
首轮通过率 一次通过门禁的 PR 占比 过低说明规则太严或提示不清

不要统计文档阅读量、培训出勤、规范页数。它们只反映投入,不反映行为。


11. 排错

现象 原因 处理
门禁红灯一片,大家直接 admin merge 一次上太多规则 先上两条,稳定两周再加
规则被 # noqa / // ignore 绕过且无记录 缺少例外流程 要求绕过必须带例外编号
新代码风格接近老代码 示例仓库或模板过时 季度同步模板,选一个标杆 PR
规范文档被拆成十几页 把评审判断也写进去了 一页 checklist + 附录案例
同一 PR 反复被同一条规则打回 提示信息没说怎么改 报错里给正确示例,不只给错误

最后一条经常被当成小事:提交信息不符合规范 这句话让人去翻文档,示例:fix(order): 修正退款金额取整 让人直接改。


12. 边界

场景 建议 理由
团队少于五人 不写规范,只约定提交格式 口头沟通成本低于文档维护成本
规范超过两页 拆成一页 checklist 读不完等于不存在
已有强 CI 纪律 只补缺口项 增量价值有限,重复建设会稀释信任
短期外包项目 只保留敏感文件与依赖两条 交付后没人维护
纯研究原型 不设门禁 探索期规则会抑制试错
已有成熟团队习惯 先观察,别先规定 把既有做法写下来比引入新规则便宜

规范的本质是降低协作摩擦,不是提高代码质量上限。质量上限靠评审和人,规范负责的是"不要每次都重新争论同一件事"。


13. 自测

  1. 判断一条规则该不该写进规范,用什么单一判据?
  2. 为什么"方法行数 ≤ 50"这类规则容易被绕过,且绕过后更糟?
  3. 生成物"可合并"的判定条件是什么?
  4. 例外记录里最不能省的一栏是哪个,为什么?
  5. 季度回顾只需要回答哪一个问题?
  6. 为什么"文档阅读量"不适合作为规范落地的度量?
S27-A04 · AI 开发规范怎么落地:从文档到门禁
https://www.clxhxhhr.top/posts/6117/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。