ub-mode 用于在编写或修改代码时贯彻严谨的软件工程原则。它参考了 @poteto 的部分规则(poteto-mode / pstack),将其精简,并配合确定性的质量门禁工具使用。
- 先让它运行起来------懒惰协议、基础思考、测试驱动开发(TDD)、从第一性原理重新设计、质疑前提、先减后加、以结果为导向的执行、打造杠杆工具,以及架构原则(领域建模、边界纪律、类型系统纪律、让操作具备幂等性、共享状态先拆分再串行化)。
- 再把它做好 ------修复根因、将工作组织成可验证的单元、测试行为而非实现、Crapper(
CRAP < 10)、Dryer(重复相似度< 0.8)、Mutator(覆盖未覆盖的代码、杀死存活的变异体、删除死代码),以及重构。 - 实践规范------记录日志、保护上下文窗口、清理工作树,以及按顺序进行小批量提交与推送。
配套工具仓库
ub-mode 依靠确定性的质量门禁工具,约束代码结构、复杂度、重复程度和测试充分性。
- unclebob/crapper------计算每个函数的圈复杂度、测试覆盖率和 CRAP 分数,使每个函数都保持在 CRAP 阈值以下(
< 10)。 - unclebob/dryer------检测结构性代码重复,消除相似度
>= 0.8的重复代码块。 - unclebob/mutator------运行变异测试,暴露未覆盖的代码,以及测试未能检出的存活变异体。
- unclebob/uml-viewer------可视化组件、覆盖率、CRAP 分数、变异测试分数和依赖方向。
技能规则(SKILL.md)
description: 编写或修改代码时遵循精简版原则。
ub-mode
每次接到变更要求,都遵循以下步骤。
- 先让它运行起来。
- 再把它做好。
- 遵循实践规范。
先让它运行起来
懒惰协议
- 同一项决策只在一个地方定义。
- 交付能够解决问题的最小改动。
基础思考
- 先把数据结构设计正确,再编写逻辑。
- 优先做能帮助后续每个阶段的事情。
- 如果另一个并发写入者会造成影响,就不要共享状态。
测试驱动开发(TDD)
- 修改代码之前,先编写会失败的测试。
- 确认测试因预期的原因而失败。
- 以最小改动让测试通过。
- 如果无法切实编写一个有效的测试,就不要写一个糟糕的测试。
从第一性原理重新设计
- 不要把新需求硬接到旧设计上。
- 问自己:如果这个需求一开始就存在,会怎样设计?想清楚,再分块交付。
质疑前提
- 如果两次修复基于同一个前提,并且都未通过同一道门禁,就用一句话写出这个前提,并统计哪些参与方承载了这种不均衡。
- 撤销这项分配。
- 完成上述工作之前,不要开始下一次修复。
先减后加
- 添加任何东西之前,先减少复杂度。
以结果为导向的执行
- 以能够验证的最终状态为目标。
- 如果事先规划过,并且能够撤销,就允许中途出现暂时失效的状态。
打造杠杆工具
- 如果工作无法一眼看明白,就构建一个能完成它的工具。
- 先手工完成一个单元,再让工具的结果与该单元一致。
架构
领域建模
- 用结构表达领域,让非法状态无法出现。
- 维护领域术语表。
- 如果代码已经清晰且局部化,就保持原样。
边界纪律
- 在数据进入系统的位置校验数据。
- 在系统内部,信任这些数据。
- 将业务逻辑保留在纯函数中。
类型系统纪律
- 让非法值无法被表达出来。
- 外部数据只在进入系统时解析一次。
- 不要压制编译器的诊断。
让操作具备幂等性
- 无论运行两次,还是在执行到一半崩溃后重新运行,最终都到达相同状态。
共享状态先拆分再串行化
- 如果各方不需要同一个可变对象,就让各方分别拥有自己的对象。
- 如果确实需要共享,就只允许一个写入者,并通过结构强制保证这一点。
再把它做好
修复根因
- 复现问题。
- 修复根因。
- 仅仅掩盖崩溃的防护措施,算不上修复。
将工作组织成可验证的单元
- 完成一个小单元,并验证它。
- 然后再开始下一个。
测试行为,而非实现
- 测试应按照使用者的方式调用代码,并检查使用者实际能看到的结果。
- 如果所有函数都不返回任何内容时,测试仍然会通过,就丢弃这个测试。
Crapper
- 使用 crapper 工具。
- 通过拆分函数、补充测试或两者结合,让每个函数的 CRAP 分数低于 10。
- 考虑是否值得减少 case 分支。
Dryer
- 使用 dryer 工具,消除相似度达到或超过 0.8 的重复代码。
Mutator
- 使用 mutator 工具。
- 覆盖未覆盖的代码。
- 杀死存活的变异体。
- 删除死代码。
重构
- 固定代码现有的行为。
- 改变结构。
- 证明行为没有发生变化。
实践规范
记录日志
- 记录采取的每项行动及其原因。
- 完成工作时报告这份日志。
保护上下文窗口
- 保持主线程精简。
- 将大块内容转移到别处,只保留摘要。
清理工作树
- 找出可以安全删除的内容,确认仍在使用的内容不依赖它们,再回收磁盘空间。
- 尚未提交的工作,等待作出处理决定。
提交与推送
- 经常提交,保持提交小而有序。
- 每次提交都说明原因、变更内容,以及如何证明变更有效。
- 最后推送到主线。