3077 字
约 10 分钟
14
S16-A21 · 规范的可执行化:写在文档里的规矩怎么变成机器能拦的
2024-12-31
2026-11-09

编码规范文档写了四十页,没人看过第二页。团队通常把它解释成态度问题,但更常见的原因是:它没有任何机制会检查。文档只对愿意遵守的人生效,机制才对所有人生效。可执行化的目标不是把四十页都变成门禁,而是把其中能被判断的那部分,挪到会真正拦住人的位置上。


1. 三类规范与判断方法

判断一条规范能不能自动化,只需要问一个问题:它能不能写成确定性断言——给定一份代码,结果是明确的通过或失败。

类别 判断方法 典型落点
能自动化的 能写成确定性断言,不依赖上下文判断 格式化器、静态检查
能半自动化的 能检出可疑点,但需要人确认 静态检查报警 + 评审确认
只能靠人的 涉及设计判断、取舍、命名意图 评审必答问题与决策记录
// 能自动化的规范: 写成配置,而不是文档里的句子
style:
  formatter: FORMATTER_CONFIG        // google-java-format / prettier / black
  import_order: enforced
  max_file_length: 800
lint:
  fail_on: [UNUSED_IMPORT, EMPTY_CATCH, SQL_IN_LOOP]

配置里的规则不需要写成散文,因为它自己会执行;散文里的规则如果没有落点,就只是建议。

三类之间的边界并不稳定:随着检查工具变强,原来"只能靠人"的条目会逐年后移。因此规范的划分不该写死在文档里,而应在每次新增检查能力时重新过一遍——哪些条目现在已经能被断言了。留在"只能靠人"里的条目越多,规范对日常产出的影响就越小。

规范里最不容易被判定的是命名与结构偏好。它们看起来也能写成断言("类名必须是名词"),但断言只能校验形式,校验不了是否准确;这类条目更该出现在评审的必答问题里,而不是门禁里。


2. "必须"与"建议"混写的降级

一份文档里同时写着"必须"和"建议",读者会按最低标准执行。强弱约束混排,等于把强约束降级。

措辞 机制上读到的含义 实际效果
必须 / 禁止 有阻断机制吗?没有就是建议 与"建议"同级
建议 / 推荐 无阻断,靠自觉 会被忽略
应尽量 程度副词,无法判定 反复成为争议点
不允许出现 可写成断言 能进门禁

"不允许出现"这一行是整张表的落点:一条规范若不能改写成这种句式,它就很难进门禁。改写本身也是一次筛选——很多争议条目在尝试改写成断言时会自动消失,因为它们其实只是偏好。

规范条目应当自带理由字段——写不出理由的条目,通常是在规定风格偏好,而不是在防止具体损失:

// 规范条目模板
规则: 禁止在事务方法内发起远程调用
理由: 长事务会持有连接并放大锁等待,历史上造成过连接池耗尽
判据: 静态检查识别注解与调用点(半自动)
例外: 幂等且已加超时的同步查询,需在评审中说明

3. 工具化的四种形态与拦截时机

形态 拦截时机 能自动修 代价
格式化器 保存或提交前 能 低,几乎没有争议
静态检查 提交前与流水线 部分能 中,规则越严误报越多
提交钩子 本地提交 通常不能 低,但可被绕过
流水线门禁 合并前 能 高,排队与重跑成本

"能自动修"与"只能报警"决定了它该放在哪一环:能自动修的放最前面,因为它不打断人;只能报警的放到流水线,因为本地报警会被忽略。

这里有一个具体的差别值得记住:能自动修的规则,冲突根本不该进入讨论——机器改完了,人连看都不用看。只能报警的规则一旦进到本地提交环节,就会变成"提交失败但不知道怎么办",人于是找绕过方式。把报警放流水线,至少还留了一次在评审里解释的机会。

// 提交钩子: 本地第一道,只放能自动修的检查
FORMAT_CMD --check $(CHANGED_FILES) || { echo "格式未通过,先运行 FORMAT_CMD"; exit 1; }
// 流水线门禁: 只放本地做不了的检查(全量扫描、依赖、覆盖率)
gate:
  - name: lint-blocking
    fail: true
  - name: complexity-check
    fail: false            // 先观察,观察期满再改为 true
  - name: full-scan
    fail: true

4. 新增规范、存量代码与例外通道

存量代码是绕不过去的:任何一条新规则在生效的第一天,都会大量命中现有代码。上表四个阶段的顺序不能改,因为它的目标只有一个——让规则在没有人对抗的情况下被接受。观察期的存在不是为了收集数据,而是为了让"这条规则会拦下多少东西"变成可讨论的事实,而不是立场。

新增一条规范的正确流程是先观察再阻断:直接上阻断会因为存量代码立刻失败,接着整条门禁被关掉。

阶段 动作 升级条件
观察 只报警不阻断,统计命中量 命中量收敛到可修复范围
清理 新代码强约束 + 存量豁免 豁免条目有到期时间
阻断 门禁改为失败即阻止 例外通道已存在
收敛 定期清理过期豁免 豁免数走到零附近
// 豁免清单: 每条必须有理由与到期日
ALLOW: src/legacy/LEGACY_MODULE      // 理由: 计划在退役中;到期: DATE_1
ALLOW: src/old/BIG_FILE              // 理由: 拆分排期中;到期: DATE_2
// 到期未处理 → 门禁恢复对它的约束,不允许静默续期

门禁必须留例外通道。没有例外通道的规则会被两种方式绕过:临时关掉整条门禁,或者把违规代码挪到规则覆盖不到的地方。例外要有记录、有期限、有审批人。

例外通道还要能回答一个具体问题:这条豁免到期时,谁会收到通知?如果通知发给豁免申请者,而他早已不在项目里,豁免就会静默过期或静默续期。把到期处理挂到模块 owner 上,和台账里的做法一致——责任跟着代码走。

规范数量与遵守率之间是反向关系:规则越多,人对每条规则的记忆越弱,豁免越多,整体可信度越低。可执行化不是"把四十页都工具化",而是先挑能被判断的那几页,把它们真正做成会拦住人的机制。


5. 排错与边界

5.1 排错

现象 机制 处理
门禁被人为关闭 阻断面过大且无绕过路径 缩小阻断集,补例外通道
阻塞率低但违反率不降 规则只报警不阻断 观察期结束后必须升级为阻断
格式争议特别多 把风格偏好写成了"必须" 交给格式化器,评审不再讨论该项
豁免清单不断增长 豁免没有到期时间 每条豁免加到期日,到期恢复约束
文档越来越厚但没人读 可执行部分与建议混写 拆成两份:门禁清单与参考文档

可执行化还有一个容易被忽略的副产品:它把"风格问题"从评审里搬走了。评审时间是最稀缺的资源,把它花在缩进、导入顺序这类无争议项上,等于用最贵的人力做最便宜的检查。

排错表里最容易被误读的是"门禁被人为关闭"。关闭通常不是恶意,而是某次发布的阻断面大到无法在窗口内修完,于是有人在流水线配置里把 fail 改成 false,之后再没人改回来。判断标准很简单:只要出现过一次"为了发布而临时关闭",就说明阻断集划得太大,或者例外通道缺失。

5.2 边界

场景 结论
团队只有一个人 用格式化器与编辑器配置即可,不必建门禁
代码量极小 检查成本高于收益,靠评审更省
规范主要涉及设计判断 工具化收益低,转成评审必答问题
已有 CI 但无本地检查 优先补本地能自动修的检查,缩短反馈时间

边界表里"代码量极小"值得补充一句:代码量小时,检查的收益主要来自"防止未来的自己犯错",而不是当前的错误率。如果这份代码一年只改两次,收益确实低于维护成本;但如果是每天都要改的小项目,同一个判断就反过来了。

边界里最容易判断错的是"团队只有一个人"。单人团队其实并不需要门禁,因为它要防的是协作噪声,而单人不存在协作;但它仍然需要格式化器,理由是换编辑器、换机器时的一致性。工具化在这里的收益不在约束别人,而在替自己省记忆。

度量四个数:可执行化比例(规则总数中进了门禁的比例)、阻断命中率、豁免条目数、规范文档篇幅变化。最后一个值得注意——文档变薄通常说明规则真的搬走了。

阻断命中率不是越高越好。持续偏高说明规则与现有写法长期冲突,团队会把它当噪声;持续接近零则要分清是"大家都守规矩"还是"规则其实没生效"——用一次故意构造的违规提交去验证门禁,比看统计更可靠。

// 门禁自检: 用一次人工违规验证它真的会拦
git checkout -b gate-selftest
// 在受检目录放一处已知违规,然后本地跑门禁命令
GATE_CMD $(CHANGED_FILES); echo "exit=$?"
// 期望 exit 非 0;为 0 说明规则未生效或已被静默跳过
// 规范审计(每季度过一遍)
可执行化比例: 进门禁的规则数 / 规则总数
阻断命中率: 被门禁拦下的次数 / 提交次数
豁免条目数: 清单行数,与上一季度对比趋势
文档篇幅: 字数变化,与可执行化比例反向对照

6. 自测

  1. 判断一条规范能否自动化,只需要问哪一个问题?
  2. 为什么"必须"与"建议"写在同一份文档里会让强约束降级?
  3. "能自动修"与"只能报警"分别该放在哪一环,理由是什么?
  4. 新增一条规范为什么不能直接上阻断?
  5. 豁免清单为什么必须带到期时间?
  6. 规范文档篇幅变薄,可能说明了什么?
S16-A21 · 规范的可执行化:写在文档里的规矩怎么变成机器能拦的
https://www.clxhxhhr.top/posts/6346/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。