Put a leash on your AI. Because it knows every API, but zero software engineering. 🦮
给你的 AI 拴上工程狗链。因为它精通所有 API,却对软件工程一无所知。
CHNW(Code How, Not What) 是一套模型级的、业务无关的工程认知约束,不依赖特定框架或编程语言。
它不教 AI 写什么业务功能(What),只规定它 怎么写代码(How)——也就是让 AI 建立起“如何写”的思维模式。
可以这样理解:就像作家有文风,程序员有编码风格,CHNW 所做的正是对 AI 的“代码文风”进行模型级别的微调,把成熟的软件工程原则翻译成大模型可执行的认知约束。它不是让 AI 更会写某个函数,而是影响它在面对任何编码任务时的思考方式。
与现有 AI 编程助手的区别
一些 AI Agent 或编程助手内部已隐含了“写出什么风格的代码”的约束,但通常很保守、克制,而且与工具本身耦合,不具有通用性。使用时,往往还需要人工反复叮嘱:“注意一下软件工程”。
CHNW 则是一组纯粹、可移植的工程思维约束,未来可以作为任意 Agent 或对话助手的默认编码规则,不依赖特定厂商,不夹杂业务需求。
本仓库的 CHNW 实现
仓库内置的是一套将经典软件工程原则(如 UNIX、KISS 等)转化为大模型可执行约束的补丁集——本质上是将人类几十年沉淀下来的工程经验,以认知约束的形式注入 AI。
它不是唯一的“通用标准”,但会随迭代逐渐稳定,并衍生出覆盖不同人群工程偏好的 CHNW 变体。
这个仓库下面内置的 chnw 实现其实是传承了我脑子里装下的编码思维,
尤其厌恶 AI 本应将规范服务于业务,却容易成为极端的教条主义😡
CHNW(Code How, Not What)项目的计划:
- 整理这个仓库,规范一点
- 在本仓库中实现通用软件工程的 CHNW ,并附上双语,可拆分为多个层级
- 完成 chnw CLI 工具,上传 npm 包
它可以解析 chnw 的 yaml(或其它格式) 格式的文件,根据不同需求,
通过开关配置,最终拼接出 prompt,可安装到各个 AI 工具中。
* 就像 Sillytavern 的预设功能一样,方便使用开关定义文风…… - 不知是否应该创建独立的社区仓库,可选为 CLI 工具服务(?)
- (待补充计划……)
变体 CHNW 实现:
- 实现为老旧项目服务的 CHNW ,项目历史经验优先。
- 我想,会有人对提交信息有洁癖,应该也需要对 AI 进行一种约束(比如要求不要一边说着参考,却使用 --oneline 遮掩多行历史)
- (待补充计划……)
如果你在日常开发中有更多精妙的、非业务相关的 AI 认知约束沉淀,非常欢迎提交 Pull Request 共同完善!
- 跨大模型的代码作风不统一:有的容易过度设计,有的喜欢走捷径留下一堆硬编码,有的在宏观职责划分上放飞。本规则在工程审美层面强行抹平差异,统一跨模型的代码质量底线。
- 被迫沦为复读机审查员导致血压上升:你无需在每次对话中像老母亲一样碎碎念 不要硬编码、把错误抛出来别吞了、职责要分离。
- 隐式约束引发的生产环境 Bug:大模型极度喜欢为错误输入添加自动修正或全局
|| 10类的 Fallback,这在复杂的生产环境中往往会掩盖底层真实漏洞。本规则强制 AI 执行“快速失败”和“显式业务约束”原则。 - 混淆规范与业务的教条主义:大模型经常被简单的需求引入另一个极端,为了死板执行 删除无用代码 的指令,会擅自抹除客观存在但当前未被调用的数据。本规则强行划定边界,明确 规范服务于业务,禁止用工程教条去阉割真实的业务事实。
你可以直接把以下内容追加到 prompt,或者 agent 的文件,例如 CLAUDE.md
(实验性!) 目前为 v2,在不断优化,不断纯粹,不断测试……
还剔除了为工具服务的部分,例如 agent 工具的 “如何编辑”
## 此处定义"代码应该怎么写",不定义"业务规则是什么"。规范服务于业务表达,而非取代业务表达
规则优先级(当两条规则冲突时)
1. 业务明确指定的约束(来自用户需求文本)> 本文件中的任何规则
2. KISS / 不增加不必要复杂度 > 显式约束 > 单一数据源
**模块职责分离**
每个文件只做一件事。模块间仅通过公开接口通信,不可跨界访问内部实现。
**KISS — 不增加不必要复杂度**
只有一个实现时不需要接口,只有一个调用者时不需要通用化。
同一模式重复出现时再考虑抽象(经验阈值:3次,可根据团队情况调整)。
避免提前抽象。不为假设性的未来需求设计。内联优于间接。
**区分“实现代码“与“数据模型”**
删除代码前先分类。代码按性质分为两类,删除规则不同:
- **实现代码**(逻辑、工具函数、helper、死代码):确定未被调用 → 可删除。
- **数据模型**(Schema、协议字段、类型定义、接口声明):即使当前无调用者,也必须保留。数据结构描述的是客观事实,独立于消费它的代码。禁止以“当前未被调用“为由删除。
判定标准:这段代码是在 DO something(执行动作),还是在 DESCRIBE something(描述事实)?描述型代码受保护,执行型代码可删除。
**SSoT — 单一数据源**
同一份数据在系统中只有一个权威来源。需要多种表示形式时从源头派生,而非复制。常量、配置值集中定义。
**不硬编码**
会变化的值(阈值、URL、参数)通过配置或参数注入,禁止写死在逻辑中。
**快速失败 & 隐式约束禁止**
错误在最早时刻、最靠近源头处暴露。禁止吞错误。
禁止为错误输入添加 fallback / 默认值 / 自动修正——输入错了就报错,让调用方修正。
校验集中在系统边界处(用户输入、外部 API、跨模块接口)。模块内部信任类型系统和调用链——不在每层重复校验。
业务约束必须显式。换一个人读代码,能否不靠注释看懂所有业务约束?不能就必须重构。
禁止:
- `Math.min(Math.max(x, 0), 100)` — 为什么是 0 和 100?约束必须命名。
- `value || 10` — 默认值 10 是业务规则却无处声明。
- `if (!count)` — 混淆 null 和 0。
**代码自解释**
代码自解释优先。注释只解释‘为什么(Why)’和‘业务上下文’,禁止解释‘做了什么(What)’——后者应通过命名和结构自明。
禁止在注释中引用会过时的上下文(任务名称、PR 编号、”为 X 功能添加“、”被 Y 调用”)——这些属于 commit message,不属于代码。
命名必须携带信息量。禁止用类型信息填充变量名(例如 `userList`、`dataObject`、`countValue`),类型系统已经说了的事不需要名字再说一遍。
禁止装饰性注释(ASCII 边框、分隔线、banner 风格块注释)。注释是工程文档,不是视觉设计。
**检查清单**
1. 职责单一且不跨界?
2. 数据是否已有单一来源?
3. 是否有硬编码?
4. 是否悄悄修正了调用方的错误输入?
5. 是否存在隐式业务约束?
6. 是否因简化而遗漏了需求中明确的要求?
本项目基于 MIT 协议开源。