项目标题里那三个字——"生成即规范",只要写过一段时间AI辅助代码的人,大概都能品出分量有多重。AI写得快,这已经是共识了,但快带来的另一个副作用是:垃圾代码也在被加速生产。一个团队如果只是把AI当成更快的打字员,前三个月会觉得效率翻倍,到第七八个月就会发现自己被"高速堆积的技术债"围住了——大量局部可用、全局混乱的代码,只有一次性的上下文理解,没有任何结构性的约束。
这一弹,我想专门聊聊怎么从提示词设计、模板约束、工具链兜底这三个层面,把"CleanCode"这条标准真正嵌进AI生成代码的过程里,而不是等生成完再靠人工Review去救。内容偏实操,适合已经在用AI写代码、但觉得"生成一时爽,维护火葬场"的团队参考。
1. 为什么AI编程会让技术债变得比人写的还难收拾
1.1 高智商、低纪律:AI写代码的本质局限
先说一个我越来越笃定的判断:AI写代码最大的问题不是"写得错",而是"写得毫无纪律"。一个经验丰富的工程师写代码时,脑子里通常带着长期形成的约束——这个函数不能超过多少行、这里不能直接塞魔法数字、这个模块的依赖方向不能反、全局状态能不用就不用。这些约束往往不需要刻意提醒,是肌肉记忆的一部分。
AI完全不一样。它没有肌肉记忆,只有概率预测。给它一段上文,它预测下一个最合理的token应该是什么。这个"最合理"源自训练数据里的统计规律,而训练数据里既有优秀代码,也有大量平庸甚至糟糕的代码。所以你会发现一个很有意思的现象:AI写一个单独的函数,常常是对的,甚至很漂亮;但让它在一个中等规模项目里连续生成十几个文件后,整体就开始走样了。有人会跨层调用不该调用的模块,有人会在每个文件里重新声明一遍相似的常量,有人会造出只在测试里出现一次的Mock对象。
打个比方,这就像一个高智商的临时工:你交代一件事,他能干得又快又漂亮,但不会主动考虑这个活和三个月后的另一次维护之间有什么关系。他不会替你维护结构的完整性,不会替你保持风格的统一,更不会主动去重构那些"先跑通再说"的部分。
这就是技术债的加速器。人类写代码积累技术债靠的是偷懒或者赶进度,AI积累技术债靠的是"没有长程记忆和全局责任感"。更麻烦的是它的产出速度:一个下午就能生成人类写一周的代码量,等于你把技术债的复利周期从季度压缩到了天。
1.2 CleanCode在AI时代的定义变化:规范要前置
传统意义上的 CleanCode,是事后约束——代码写完了,通过Code Review、重构、静态检查来逐步净化。这套逻辑在线下开发里问题不大,因为人的产出速度有限,Review 跟得上。
但AI的到来改变了这个等式。我见过不少团队,把AI生成的代码合并后,根本不Review——或者说,Review不过来。一个1000行的PR,人很难从头到尾精细地看完每一行;如果你的工具只有"事后检查"这一道防线,那防线一定会被击穿。你挡不住所有问题,只有把"规范"这个动作前置到"生成"这个动作里去,才能从源头把垃圾量降下来。
"生成即规范"说的就是这个:不是生成完了再改,而是在生成的同时就已经符合规范了。这个理念落到实操上有三个抓手:
- 把规范写进上下文:通过提示词、规则文件、项目约定,让AI在生成之前就"知道"该怎么写。
- 把规范锁进模板:用脚手架、代码骨架、目录结构把可变性约束住,让AI只能在你划定的空间内发挥。
- 把规范焊进工具链:用Lint、静态检查、格式工具在合并前自动拦截,而不是全指望人眼。
这三件事做好了,AI生成代码的技术债总量就能显著下降。下面我按这三条线逐步拆开讲。
2. 把规范写进上下文:提示词不只是"帮我写个XX"
2.1 "四个信封"提示词设计法:给AI立规矩
很多人的AI编程提示词是这么写的:"帮我写一个用户注册接口,包含邮箱验证。"然后AI哗啦哗啦生成一堆代码。你一看,逻辑大方向没问题,但细节乱糟糟:常量到处散着、错误处理风格不统一、也没有注释。
这里面缺的不是能力,是约束。想让AI产出规范代码,提示词必须有结构、有边界、有正例和反例。我自己摸索了一套方法,叫"四个信封"——不管项目任务多复杂,提示词里至少放四类内容:
- 角色与目标:告诉AI你希望它扮演什么角色(比如"资深后端工程师"),以及这段代码最终要达成什么目标。
- 规范白名单:明确列出必须遵守的编码规则,比如"禁止魔法数字""每个公共函数必须有docstring""所有外部输入必须经过校验"。
- 反例黑名单:给出你不想看到的写法实例,或者明确列出禁止事项,比如"不允许在业务代码里直接拼SQL""不允许使用全局可变状态"。给反例比只给正例有效得多——AI见过太多坏代码,你给它列举坏样子,它反而更清楚边界在哪里。
- 输出契约:约定代码之外的交付物,比如"除了代码,还需给出测试用例清单和变更影响说明"。
这套提示词用起来后,最大的变化不是生成代码的正确率提升了,而是风格稳定性明显上升了。同一个模型,加了规范约束和没加约束,产出的代码质量差距能达到"看起来像两个人写的"。
2.2 提示词里的CleanCode规则应该列到什么颗粒度
有人会问:"规范这么多,全写进提示词里不现实吧?"确实,一条条全列,提示词能写两千字,然后模型上下文被占满了,真正干活的余地反而小了。所以要注意编排优先级。
我的经验是:提示词里的CleanCode规则要分级。高优先级的是那些直接影响可维护性、难以事后修复的规则,比如:
- 模块依赖方向(哪一层可以调用哪一层)
- 函数长度与单一职责(拒绝上帝函数)
- 错误处理策略(何时抛异常、何时返回结果对象)
- 禁止全局状态与隐藏副作用
- 命名规则(类名、方法名、变量名的风格)
中低优先级的是那些可以由工具自动检查的,比如缩进、引号风格、空行规则,这些交给格式化工具就好,写进提示词反而是浪费token。
建议把所有规则拆分到一个.ai-rules或者CONVENTIONS.md文件里,然后在提示词中引用:"请阅读项目根目录下的 CONVENTIONS.md,严格按其编码规范生成代码,不允许违反其中的任何规则,如果发现规则与需求冲突,请在代码注释中标注出来。"这比在提示词里列十条规则要高效得多——规则文件可以常驻上下文,也可以按模块分片注入,不会挤占对话空间。
2.3 一个可直接抄走的提示词模板
这里我贴一个能用在实际项目里的精简模板,覆盖常见场景。你可以按自己的技术栈调整。
你是这个项目的资深工程师。下面的规则来自项目根目录CONVENTIONS.md,是你必须遵守的编码契约: [在此粘贴规则摘要,例如] - 所有业务逻辑只能放在service层,controller只做参数解析与响应封装 - 禁止魔法数字,所有常量集中到constants模块 - 函数一般不超过30行,超过则必须拆分 - 所有外部输入用统一的XxxValidator校验 - 禁止使用any(或Python里的裸except),特殊情况须注释说明 任务:实现用户注册功能,包含邮箱格式校验、密码加密存储、重复注册检测。 要求: 1. 按上述契约生成全部代码文件,保持现有目录结构 2. 每个文件生成后,附上一段说明:你做了哪些决策、有哪些潜在重构点 3. 同时生成一组针对核心业务逻辑的单元测试用例,测试中不要使用真实数据库 4. 生成完毕后,自查一遍是否违反上述任何一条规则,违反的请先自行修正为什么加第4条?因为实测下来,让AI"生成完自查"这个动作能把规则违反率再压低一截。模型在生成时注意力可能分散,但你明确要求它回头检查时,它会重新聚焦到规则上,自动修正不少低级问题。
3. 把规范锁进模板:生成器的核心是"约束自由"
3.1 约束即自由:脚手架比提示词更管用
提示词能解决"怎么写"的问题,但解决不了"写在哪、写成什么样"的问题。AI生成代码时,如果没有一个结构骨架,它是不会有意识地遵守你的架构设计的。你说"我们项目用三层架构",它可能真的给你在三层里各放些文件,但很可能这一处依赖反了、那一处又跨层访问了,全都取决于它在哪个上下文片段里做预测。
所以这一弹要重点讲的第二个思路是:把规范锁进模板。用好脚手架和代码骨架,让AI只能在预设的框架里填写内容,很多架构层面的技术债就直接被消灭了。
用装修来类比就特别清楚:你请一个工人来刷墙,与其告诉他"按照装修规范刷",不如直接把涂料、滚筒、分色纸都给他配齐,再把施工范围划定清楚。他发挥的自由少了,但出错的概率也小了。AI生成代码也一样,给它一个接口模板、一组工具函数、一套目录骨架,它就只能在这个空间里做填空,而不是在空白画布上随意创作。
3.2 目录结构即架构约束
实操上,我建议先把项目目录结构表达清楚,再把目录结构当作提示词的一部分。比如你想要严格的分层架构,在提示词或规则文件里明确:
src/ controller/ # 只允许放HTTP层代码,不得包含业务逻辑 service/ # 业务逻辑只允许出现在这里 repository/ # 数据访问唯一入口 domain/ # 领域模型与领域服务 constants/ # 所有常量集中管理 validators/ # 统一校验逻辑然后又明确告诉AI:"生成的代码只能落在上述目录中。controller不允许引入repository的依赖;service是业务逻辑的唯一宿主;repository只做持久化,不允许包含业务判断。"这种目录结构加上依赖规则,本质上是一种"物理约束"——AI即使"想"乱写,也会在文件路径和引用关系的维度上被强烈抑制。
实测下来,把目录结构定义清楚之后,生成的代码在架构层面的乱象会少掉一大半。原因很简单:AI在生成import语句时,如果看到目录规则里写明了依赖方向,它大概率会遵守;而如果没有这个约束,它就会自由联想——今天看到张三在service里调了repository,明天就会生成类似代码。
3.3 接口骨架与"填空式生成"
更深一层的模板约束,是给AI预置接口骨架,让它"填空"而不是"创作"。比如你写了一个 OrderService 的接口,定义了createOrder、cancelOrder、queryOrderById三个方法签名,然后在任务里给它:"请实现OrderService,接口已定义,不要新增公共方法。方法内部逻辑按领域规则处理。"
这时候AI要做的事情就非常具体了:它不是设计一个订单模块,而是实现三个已知的方法。它自由发挥的空间变小了,但代码的确定性、可维护性大幅提升了。尤其是团队多人协作时,接口先定,AI生成的实现再填进去,最后合出来的代码风格和结构高度一致,后期维护的人不需要面对一堆五花八门的自定义结构。
模板约束这块,我自己在实际项目里用的一套是:每个模块生成前,先手工写好一个module_template.md,里面包含接口定义、数据模型定义、边界说明、依赖说明。然后让AI严格照这个模板产出实现和测试。这样生成器实际上变成了"填空器",质量稳定得多。
4. 让规范贯穿到可测性与可维护性:易调测不是口号
4.1 生成代码"能跑"与"可调测"的巨大差距
"易调测"这三个字,在标题里排得靠后,但真做起来,是区分团队成熟度的关键。很多AI生成的代码,功能上是通的,但你真要去调它、测它、改它,会骂人。
举个最常见的场景:AI生成的后端接口,你调用时候返回了一个错误的JSON结构,可日志里只有一行汇总信息,你根本不知道是哪个分支产生的错误。再或者,某个服务方法内部抛了个异常,你去看堆栈,发现异常被吞掉了,只记了句"操作失败"。这种代码测试起来极为痛苦——你写了个失败的测试用例,想找原因,可代码里没有足够的定位信息。
"易调测"要解决的就是这个问题。我的方案是:把可观测性、异常可追踪性、测试友好性作为生成代码的硬性规范,而不是事后补充。
4.2 把可观测性写进生成规则
在提示词和规则文件里,我强制要求几条:
- 所有外部接口的入口和出口必须打印结构化日志,包含请求ID、参数摘要、响应状态、耗时。
- 所有自定义异常的message里必须包含定位信息,比如类名、方法名、关键参数值,方便拿堆栈就能定位。
- 禁止无脑捕获异常后只记录"error",捕获时必须记录完整异常堆栈。
- 外部依赖调用(DB、缓存、第三方API)必须设置超时和断路器,且关键路径上要有指标打点。
这些规则摆出来后,生成出来的代码就和"能跑而已"彻底拉开差距了。你在本地调试阶段,打开日志就能看到一次请求的完整生命周期,哪一步慢了、哪一步返回了什么,一眼就能扫出来——省掉大量抱着Debugger一步步跟的时间。
4.3 测试骨架:让AI为"自己写的代码"先测一遍
易测的另一个关键,是让AI生成代码的同时,把测试骨架一起生成了。我见过太多"AI生成了一堆代码,但一个测试都没有"的项目。功能确实跑起来了,但三个月后没人敢动那块代码,因为不知道改动会不会踩碎什么。
合理的做法是在生成任务里强制绑上测试要求:
- 每个"核心业务方法"必须附带单元测试用例。
- 测试要覆盖主要分支,而不仅仅是"happy path"。
- 外部依赖(数据库、消息队列、HTTP客户端)一律用接口替身(Mock/Stub/内存实现),不允许真的连外部服务。
实际跑下来,AI生成的测试当然不算完美,覆盖率也不会特别高。但有了骨架,团队后续补测试的成本低得多——比从一张白纸开始写测试至少省一半时间。而且,这些测试本身就是一种文档:它们表达了AI对所生成代码的行为预期。后续改代码的人一跑测试就知道自己改坏了哪个行为,定位效率会高很多。
我还喜欢在生成任务上追加一句:"请先写测试,再写实现。"对AI来说,这个顺序会让它更早思考"这段代码应该有哪些外部行为",产出的实现通常会更贴合接口契约,测试也更扎实。这个顺序很多人忽略,但实测下来对质量提升非常明显。
5. 用工具链兜底:把"人盯着"变成"自动拦着"
5.1 规范要变成门禁,不能只靠自觉
前两招都是把规范"前置"到生成过程,但AI永远不可能百分百遵守规则——何况模型还有随机性,同一个提示词跑两次,结果都可能有差异。所以必须加一道保险:工具链在合并代码之前自动拦截不合规的东西。
这一环节里,最值得投入精力的是三件事:
- 静态检查(Lint):把命名、格式、复杂性规则装进CI,任何违反规则的代码直接阻止合并。
- 复杂度与坏味道检测:用工具扫描圈复杂度、认知复杂度、重复代码、过长函数等指标,超标的直接挂红。
- 单测覆盖率门禁:核心模块设定覆盖率下限,低于阈值不允许合并。
很多团队可能会说:"我们也有Lint啊!"但实际执行时经常打折——本地跑不过就--force提交,或者CI只是个摆设,没人真的卡。我的建议是:把质量门禁做成"硬门禁",合并时必须全绿。AI生成时代,代码量会指数级膨胀,人工Review已经靠不住了,只有机器门禁能稳定守住基本面。
5.2 可量化的"技术债体检"指标:别靠感觉
"技术债"最怕的就是模糊。你说这代码有技术债,我说还行,吵半天没结果。要治理它,必须把"技术债"翻译成可量化的数字。
我在多个项目里沉淀了一套"技术债体检指标",放在CI里定期跑:
| 指标 | 健康阈值 | 超标的典型信号 | 修复建议 |
|---|---|---|---|
| 圈复杂度 | 单函数 ≤ 10 | 函数里的圈复杂度高于15,说明分支太多,逻辑难测试 | 拆函数,引入状态策略或表驱动 |
| 重复代码率 | ≤ 3% | 超过5%说明大量复制粘贴,一改漏几处 | 提取公共函数或模板 |
| 注释密度 | 关键公共接口 100% 注释 | 公共方法无docstring,说明"为什么"没留存 | 补注释,只写"为什么"不写"是什么" |
| 覆盖率 | 核心模块 ≥ 80% | 低于60%,改代码等于盲飞 | 先补核心链路的测试 |
| 依赖层级违规 | 0 | 有跨层import说明架构在腐烂 | 按依赖规则修复 |
| 全局可变状态 | 0 | 有global/static可变对象,并发隐患 | 改成不可变或显式上下文传递 |
有了这些数字,技术债就不再是感觉问题,而是报表上的具体数字。每次迭代跑一次体检,你就能看见"这周我们又引入了多少债、还了多少债"。这个反馈闭环非常重要——它让规范落地有了数据支撑,也让管理层能看清楚"代码质量不是玄学"。
5.3 门禁过严会逼出"绕过机制":治理要留出口
这里有一个必须提醒的坑:门禁太严格、又没有申诉出口,团队就会开始绕。常见的是改Lint配置、把检查命令从CI里删掉、或者找"绕过检查"的提交姿势。这事我踩过,教训很深。
所以我在设定门禁时留了三道口子:
- 紧急热修复可以带warning合并,但必须12小时内补修复单。
- 对规则的自定义要透明:任何人想改规则,必须提出书面理由,比如"这条规则对这个场景不适用",经评审后改,而不是顺手就关。
- 门禁拦截的问题要可追溯:每次被拦下,要有清晰的提示说"为什么被拦、应该怎么改"。如果门禁只报错不给解法,团队的挫败感会很高。
工具链的本质是"自动提醒",不是"自动惩罚"。它应该帮人减少决策负担,而不是增加心理抗拒。把门禁设计得友好,大家才愿意配合。
6. 常见坑:AI生成CleanCode时我踩过的那些雷
6.1 AI的"过度抽象"陷阱
一开始用约束规则时,我把提示词里写了"遵循DRY原则(不要重复自己)",结果AI把三处用途完全不同的代码硬抽成了一个魔改函数,参数加了六个,默认值套默认值,调用处各种传参绕圈子。表面上没有重复,实际上比重复还难读。
后来我把规矩改细了:允许小范围重复,禁止过早抽象。规则描述改为"如果同一结构的代码出现三次以上才考虑提取;两次或少于两次,宁可复制也不要强行抽象。提取时,公共函数必须有清晰的单一职责和完整测试。"这个微调之后,生成代码的可读性提升非常明显。
这个坑其实很有代表性:CleanCode的很多原则,字面上看是对的,但如果AI不理解上下文语境,会机械地执行一条规则而破坏另一条更重要的规则。规范规则要表述得"带条件",而不是绝对命令。
6.2 偏好"一次性代码":为特殊场景生成的代码污染全局
另一个常见现象:AI为一个特定需求生成的代码,里面带的特殊处理逻辑会蔓延到好几个文件里去。今天你让它实现"订单过期自动取消",它会在OrderService、OrderScheduler、OrderRepository三个类里各放几行关于"过期"的判断,既不集中也不分层。后期你要改"过期时间从24小时改成48小时",得全局搜索,还是容易漏。
对付这个问题,我在规则里加了一条:"所有时间窗口、状态机转换、支付结算等涉及业务规则的逻辑,必须集中到domain层或configuration里,业务层只允许调用,不允许四处散落规则判定。"有了这个约束后,生成代码里的规则就集中得多了,改一处就生效,维护成本大幅下降。
6.3 测试替身滥用:Mock到没意义
AI很爱生成Mock,因为Mock能立刻让测试跑通。但一个测试如果Mock掉了几乎所有依赖,那它其实什么都没测到——它只是在验证AI自己构造的剧本。有一阵子我发现团队里一堆单测,跑得飞快、全绿,但根本抓不住回归问题。后来我专门查了一下,发现大量测试都是"All friends mocked, no assertion on real behavior"。
我在规范里加了一条:"测试必须对真实行为做断言,外部依赖用轻量级替身(如内存版Repository),不鼓励对内部私有方法做Mock。禁止对纯函数Mock。"
这条加上后,测试质量有明显回升。AI生成的测试更偏向"行为验证",而不是"自导自演"。
6.4 注释成为噪声
还有一个很反直觉的坑:AI生成的注释往往是噪声。它特别喜欢写"这是一个创建订单的方法"这种毫无信息量的注释——你说的是"是什么",我看代码就知道了。我要的是"为什么":为什么要校验库存、为什么要用分布式锁、为什么这个分支在并发时会走向这里。
所以我在规则里明确:"注释只写为什么,不写是什么。公共方法和复杂分支必须有注释说明设计意图,代码本身要表达'是什么'。"有了这条约束,AI生成的注释明显收敛,也更有价值。它可以写"这里先更新库存再创建订单,是为了避免并发下超卖",而不是"更新库存"。
6.5 生成的异常处理太重或太轻
异常处理是AI代码里最两极分化的地方:要么是全部try-catch吞掉,导致错误完全不可见;要么是到处throw笼统的RuntimeException,调用方根本不知道该怎么处理。两种情况都会给调试和生产带来很大麻烦。
我的规则是:业务层只抛出明确的业务异常,技术层异常向上传递时保留堆栈;跨系统调用时必须捕获并包装为带错误码的领域异常。另外,异常信息里必须包含足够的上下文(比如用户ID、订单号),这对排查线上问题简直是救命级别的。
7. 这一弹的收尾:说说我实测下来的体会
第36弹了,想聊点没有什么排版的体会。CleanCode和AI编程结合这件事,一开始我以为是个技术问题,做久了发现更多是"工程纪律"问题。AI这个协作对象,能力强、脾气好、努力不抱怨,但它没有方向感,也不会主动维护你的架构秩序。你要么花力气把它训练成"懂规矩的团队成员",要么就只能天天给它擦屁股。
我个人实测下来最有效的时间投入,其实就是最开始那两三天:把项目规范整理成机器可读的规则文件、把模板补齐、把CI门禁建好。这笔投入会在后面每一个生成任务里不断复用,省下来的Review和返工时间远远超过最初的投入。
另外想强调一句:别指望一个"万能生成器"能解决所有项目的代码质量问题。每个团队的技术栈、架构风格、领域约束都不一样,规范必须"定制"才能落地。通用的CleanCode规则是骨架,你自己的项目和团队文化才是血肉。
要是你也在做类似的事,建议从一个小模块开始试:先写规则文件,再做模板约束,最后挂门禁。跑两个迭代看看数据,再决定要不要推广。这套方法不挑语言、不挑框架,核心思想是一致的——AI负责快,规范负责稳,人来定义什么是"对"。
最后分享一个让我觉得值回票价的小配置:把规则文件放进AI的上下文里,再在每次生成任务末尾加一句"请先自查是否违反规则,并列出你违反的地方以及如何修正"。就这么一句话,AI生成代码的规范违反率能降两到三成,强烈建议试试。