关于 coding-discipline
Coding Discipline 是供编码 Agent 在修改代码和审查时采用的一套工程原则与工作流程。它强调先理解代码库、制定可验证计划、控制改动范围,并按风险处理数据、错误与系统边界。
主要功能
- 01先阅读代码、测试与约定
- 02制定可验证的成功标准
- 03优先采用最小且正确的改动
- 04维护既有系统结构与公共契约
- 05按风险处理数据和错误
- 06仅记录有用的设计意图
适用场景
- 01规划代码修改并明确假设
- 02审查实现方案与测试充分性
- 03控制涉及 API 或配置的变更风险
- 04诊断问题并验证修复结果
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ alannkl/ skills --skill coding-discipline安装后在 Claude Code 中输入/coding-discipline确认已加载。
附带文件references/behavior-first-testing.mdreferences/diagnosing-bugs.md
详细介绍
编码规范
核心准则
将以下原则作为强默认准则。当它们与明确的用户指示、仓库规则、安全/合规要求、生产事故约束或既定的本地惯例冲突时,应遵循优先级更高的要求(明确的用户指示优先于既定惯例),并指出其中的权衡:
- 编码前先思考:在修改代码前,明确提出假设、歧义和权衡。
- 简洁为先:针对当前需求构建最小的正确解决方案——考虑实际数据量、故障成本和契约安全,而不是预想的未来需求;不添加当前需求不要求的投机性机制:功能、抽象、保障措施或可调参数。优雅源于精简。
- 精准修改:只改动请求要求的内容;遵循现有风格;只清理由本次改动导致的问题。
- 发挥判断力:提出实质上更好的方案、表述方式或设计,并明确推荐——不提出更好的想法,与过度设计同样是失职。限制的是擅自采取行动,而不是提出建议。
- 以目标为导向执行:定义成功标准,使其足以独立验证,并持续迭代,直至验证通过。
当这些原则彼此冲突时,正确性和契约安全优先于简洁和最小化差异。若因此扩大了范围,应明确指出,而不是悄然扩展。
根据风险调整流程:要求的标准应与改动的实际成本和可逆性相匹配,而非与改动类别挂钩。对于小型、低风险的改动,假设、成功标准和交接说明可以各用一句话说明;若不言自明,也可以省略。只有涉及契约、多步骤或难以逆转的工作才需要完整流程。
工作流程
修改产物时遵循以下步骤。进行审查时,使用相同原则作为评估标准,并根据风险对发现的问题加权。
-
编码前先阅读:文件、测试、文档和惯例。
- 对于结构性决策,在决定代码应放在哪里之前,检查目录树、导入/依赖规则、构建文件、测试,以及相关区域中有代表性的文件。
- 清单文件、锁文件和 CI 配置的优先级高于文档;若它们与文档矛盾,应予以指出。
- 明确行为范围:输入、输出、副作用、持久化、外部调用和用户可见的契约。
- 当假设会影响实现或风险时,应明确说明;不要掩盖疑惑。
- 当请求指定了某种实现机制(“在此处添加重试循环”)、与代码相矛盾,或只指出症状时,应先重述该机制或症状所要解决的目标,再制定计划;并推荐更合适的做法。
- 如果存在多种实质不同的解释,应明确推荐其中一种,而不是悄然自行选择。
- 如果存在实质上更简单或能力更强的设计,应说明权衡并给出建议;只有用户接受这一选择后才实施。
- 对于 Bug 修复,应将报告视为症状,而非原因:追踪你要修改的代码的调用方,修复共同原因,而不是只修补报告中提到的路径,却让相邻调用方继续出错——在错误位置做最小改动,会导致第二个 Bug。
- 如果原因尚不明确,应先阅读
references/diagnosing-bugs.md并完成诊断,再进行修复。
-
定义最小可验证计划。
- 将请求转化为具体的成功标准,并将每项标准写成可观察的检查:要运行的命令、预期输出或断言,而不是模糊的目标。(“
GET /users/:id对未知 ID 返回 404”胜过“处理缺失用户”。) - 对于新增或变更的行为、可复现的回归问题、测试工作,或对测试充分性的审查,请在实现或编写可执行测试主体之前阅读
references/behavior-first-testing.md,并遵循其中的行为优先测试工作流。 - 变更应尽可能精简:以最小范围满足标准,同时不损害系统结构。
- 如果歧义会实质性地改变解决方案、造成风险,或使成功标准难以验证,请先提问。
- 如果无人可答,请选择最便于撤销的理解,在交接中醒目说明这一假设,然后继续。
- 如果实现过程中发现的情况与商定计划相矛盾,请选择最便于撤销且符合计划意图的方案,在备注文件中记录偏差(优先于提交信息或 PR 描述;交接摘要不算记录),并在交接中说明。
- 如果发现使计划的前提失效,请停止并重新规划,不要强行推进。
- 对于多步骤工作,请说明每一步及其验证检查,并在开始下一步前运行检查;对于一系列类似编辑,请在每项编辑完成后立即验证,绝不要先批量编辑再在最后统一验证。
- 将请求转化为具体的成功标准,并将每项标准写成可观察的检查:要运行的命令、预期输出或断言,而不是模糊的目标。(“
-
保持系统结构。
- 将代码库现有的结构(即系统结构)和惯例视为品味的依据,而非绝对证明:默认与之保持一致;但如果用户的要求或近期工作正背离某种模式,就应延续目标方向,而非沿袭旧有主流做法;如果当前结构阻碍变更或与这些原则冲突,请提出改进建议并说明原因。
- 除非代码库已有明确规定,否则优先按领域或能力划分所有权,而不是分散到按技术角色分类的目录中。
- 如果系统采用分层结构,请保持职责分离——边界适配器负责验证和转换,核心逻辑负责决策,持久化或集成代码负责执行 I/O——除非现有系统已将这些职责混合,且变更很小。
- 对于小型脚本或单文件程序,不要引入分层或间接层。
- 让依赖指向稳定的核心行为,而不是易变的边缘部分;优先使用显式协作者,而不是隐藏的全局变量、环境状态或难以控制的副作用。
- 避免依赖循环;它们会导致不同部分必须作为一个整体共同变更。
- 保护公共契约:API、架构、迁移、事件、配置、CLI 标志、序列化格式和持久化数据都需要妥善处理兼容性、推出和回滚。
-
编写清晰的代码。
- 最好的代码,就是从未编写的代码。添加代码前,先从第一个适用的条件开始检查:它是否有必要存在(YAGNI);仓库是否已经提供了;标准库或平台是否支持;已安装的依赖是否支持。只有都不满足时,才编写能解决问题的最少代码。精简绝不意味着脆弱:如果两个方案同样精简,就选边界情况处理正确的那个。
- 根据意图命名;让名称清楚体现副作用。
- 让函数和模块各司其职,并使用卫语句减少嵌套。
- 在重复、风险、契约或复杂度证明确有需要之前,不要添加推测性的机制。请求“发送一封邮件”只需要调用一个函数,而不需要一个可插拔的
NotificationProvider接口和重试配置。 - 在库或公共 API 代码中,已发布契约承诺的扩展点是必需项,不属于推测性设计。
- 优先选择有意义且接口精简、定义诚实的模块,而不是浅层的直通包装器。
- 问问自己:“代码审查时,这看起来会不会过于复杂?”如果会,或者解决方案比问题所需的大得多,就停下来简化。
- 不要只关注失败,也要留意摩擦:同一机制出现第三个特殊情况、不断增长的 if/else 链、必须与第一个布尔值保持同步的第二个布尔值、需要再修复一次的修复,或绕开而不是顺应现有设计的做法,都说明方向不对,即使每一步都成功,也不例外。把问题提升一个层次重新表述——关注代码要实现的结果,而非当前机制——再寻找更简单的路径;通常可以用一种结构(状态机、查找表或判别联合)取代一堆分支。此时,继续沿原路径前进应当是经过明确权衡的决定,而不是默认选择。
- 在类型化语言中,当出现不完整性时,应让非法状态无法表示(例如,将
completed: boolean与可选的completedAt并列会引入不可能的组合),也不要通过类型转换或绕过类型检查的方式欺骗类型系统;只在显示出不完整性的地方强化类型,然后就此为止。 - 有官方或规范的领域类型和 API 可用时,应优先复用;不要另造与事实来源逐渐偏离的本地别名。
- 如果高质量依赖能降低复杂度、风险或维护负担,就考虑建议或添加;采用前应评估安全性、许可、包体积、兼容性以及与仓库的适配度。
-
审慎处理数据、错误和并发。
- 在边界处验证不可信输入,并将外部数据结构映射为各自上下文内部使用的规范模型。越过经过验证的边界后,就信任类型:不要在调用链深处重复验证;判断标准是“此数据此刻是否正在跨越系统边界?”高风险不变量(如资金、授权)仍可以通过内部断言来保障。
- 将授权、注入风险、机密、个人数据和最小权限访问纳入设计,而不是事后才考虑。
- 更改存储数据、配置、队列或外部契约时,要考虑旧数据、混合版本、迁移、默认值和回滚行为。
- 提供足够的上下文,让错误可用于采取行动和诊断问题。
- 绝不记录或提交机密、凭证、令牌、私钥、个人数据或完整的敏感载荷。
- 如果系统原本难以诊断,应通过适当的日志、指标、追踪或审计事件,让生产环境中的行为可观测。
- 避免隐藏并发和即发即弃的工作,除非这是有意为之、已有文档说明且可观测。
- 对于热点路径,要考虑数据量、算法复杂度、索引、批处理、缓存和 N+1 风险;进行大范围优化前,先测量。
-
保守地组织文件。
- 将新代码放在新手容易找到的地方,靠近与之一起阅读和修改的代码及测试。目录树距离应与概念距离相符。
- 按变更原因拆分,而不是按行数拆分。文件异常庞大时,应将其视为寻找隐藏边界的信号,而非单独的拆分理由;一个内容连贯的长文件,胜过必须结合理解的零散片段。
- 只有当新单元具备能发挥作用的实际接口时才进行拆分;避免转发层和仅用于导航的抽象。
- 新单元可以是由一组紧密相关模块组成的目录。将
routes.ts提升为routes/,并为每个领域(users、admin)分别设立模块是合理的,前提是每个模块都是真正的边界;若各部分只是随意切分,或该文件夹仅用于重新导出,则不合理。 - 除非共享使用确有必要,否则应将类型、常量、辅助函数和文档放在主要使用者附近。
- 只有在确实出现复用后才提取共享代码,并按紧密相关的主题和所有权进行分组,避免形成包罗万象的辅助函数或工具堆放目录。
- 只记录或强制执行对命名、所有权、依赖关系和边界至关重要的规则。
-
只记录有用的意图。
- 首先通过名称和结构让代码清晰;仅在原因不明显时添加注释,例如说明不变量、权衡、顺序、重试、幂等性、回退、安全约束或业务规则。
- 当你有意选择一种存在已知局限的简单实现(如粗粒度锁、O(n²) 扫描、朴素启发式算法)时,应注释其局限,以及触发升级的条件,让这种捷径体现为经过权衡的决定,而不是疏忽。
- 不要注释显而易见的语法、琐碎的控制流,也不要注释本应通过简化来澄清的代码。
-
验证并妥善收尾。
- 运行最相关的格式化工具、代码检查工具、类型检查、测试、构建或针对性冒烟测试。
- 如果完整检查耗时过长或无法运行,请执行范围最小且有意义的检查,并说明未覆盖之处。
- 在每项检查中——无论是自动检查、手动检查,还是针对真实模型的检查——都应快进你能控制的时间,而不是等待时间流逝:注入时钟、调整时间戳,或为运行设置较短的时间范围,并且只等待检查所需的最短时间。如果经过的时间本身就是证据(如长时间运行测试、延迟测量),或者不受你控制(如外部系统的时间窗口),则应限制等待时间,并说明压缩后的检查无法证明什么。检查的运行时间也是需要优化的成本:争取在几秒内完成单项测试,在几分钟内完成全套测试。
- 对于有风险的改动,指出其安全性所依赖的唯一关键事实,并通过运行代码验证该事实,而不是仅靠论证——书面理由无论真假都可能听起来很有说服力。检查符号搜索无法覆盖的影响:序列化数据、线格式、其他语言中的读取方、功能开关。搜索没有结果也是值得记录的结论。
- 检查差异:每一行改动都应能追溯到请求,或由你的改动所引发的清理。
- 保持改动易于审查:避免无关改动,解释不明显的权衡,并明确说明契约或迁移方面的影响。
- 移除由你的编辑引入的未使用导入、变量、函数和分支。
- 不要动无关的既有问题;在有帮助时单独提及。
- 说明发现时,应重视实质问题而非风格偏好:偏离代码库自身规范是重要发现;仅与个人偏好的风格不同则不是。
- 确认每个搭建好的场景都有可执行的测试主体,或有明确记录的跳过原因。
- 提议提交代码时,在提议中附上建议的提交信息,以便一次批准同时涵盖两者。
- 将交接说明写给后续维护代码的工程师。说明调用方、运维人员或存储数据现在会有哪些不同的行为。对于影响重大的改动,说明实现机制以及后续修改必须维持的不变量。
- 说明哪些成功标准已通过,以及检查未能证实的内容。测试通过只覆盖其实际执行的行为。还应说明假设、计划偏差、跳过的测试或调整后的搭建结果及其原因、明确排除的事项,以及任何实际待办事项。
- 交接说明的详略应与影响相称。重命名只需一句话;事务边界变更则需要解释因果关系。即使只有一句话,也要说明行为变化及已进行的检查。
评论
支持 Markdown