
从第三十四弹这个编号你应该能猜到这个项目我已经折腾了很长一段时间。关于 AI 编程市面上的讨论大多围绕“能不能生成”“生成得快不快”但这套生成器从一开始就绑定了一个更硬核的目标生成即规范从源头杜绝技术债。说白了我不希望 AI 只是把代码量做大、把 Bug 从人工手写换成机器批量生产而是希望它生成的东西本身就符合 CleanCode 的标准拿到手能调、能测、能维护。这篇文章我想把背后的设计思路、实操链路以及这一路踩过的坑原原本本分享出来。1. 为什么要做“生成即规范”的代码生成器1.1 技术债是怎么在 AI 生成代码时悄悄积累的AI 编程普及之后很多团队面临的第一个问题不是“代码能不能跑”而是“代码跑起来之后还能不能改”。用大模型直接生成一段功能代码几乎都可以在几秒内完成任务但这些代码往往带着非常典型的生成痕迹变量名是 a、b、tmp、data一个函数写两百行里面既查数据库、又发消息、还处理日志异常处理要么是空 catch要么干脆把可能抛错的部分全部抛给上层注释只写“// 处理逻辑”完全不解释为什么要这么做。这些问题的本质不是大模型的智商不够而是它的优化目标与你不一致。模型默认情况下追求的是“语义上看起来合理的代码”不是“符合工程规范的代码”。当项目规模小、功能场景简单时这种脏代码的代价还不明显一旦代码量上来、多人协作、频繁迭代每一行不合规范的功能代码都会变成技术债的利息早晚要还。我在做这套生成器的前期调研时统计过一组数据未经约束的模型生成代码大约有 70% 以上需要至少一轮人工重构才能进入评审流程。重构消耗的工时甚至比从零手写还多。这让我意识到用 AI 写代码这件事真正的杠杆不在“生成速度”而在“生成质量”。如果能在生成链路上嵌入规范约束从源头压住技术债AI 编程才算真正有了工程价值。1.2 从“能用的代码”到“规范的代码”差距在哪很多人对 CleanCode 有一个误解觉得“规范代码”就是“格式好看的代码”加个格式化工具就行了。其实远不止这样。我理解中的规范代码至少要覆盖五个层面命名能不能表达意图、函数是不是单一职责、依赖方向是否正确、异常和边界有没有处理、测试是不是真的覆盖了行为。举一个很简单的例子。同样的“根据订单 ID 查询订单并将状态改为已支付”这个需求非规范生成的结果往往是这个样子的public void pay(String id) { Order o orderDao.get(id); if (o ! null) { o.status 2; orderDao.update(o); // 更新订单日志 log.info(update order status); } }这段代码能跑没有任何语法问题但它把状态变更的规则写在 Service 层、把常量 2 写在赋值语句里、把更新操作和日志操作硬耦合在一个方法中。一旦以后要增加“支付前检查库存”“支付后发送通知”这个函数就会持续膨胀最终变成没人敢动的泥球。规范生成的结果看起来麻烦得多但也可靠得多Service public class PaymentProcessor { private final OrderRepository orderRepository; private final EventPublisher eventPublisher; Transactional public PaymentResult pay(PaymentCommand command) { Order order orderRepository.findByOrderId(command.getOrderId()); order.pay(); orderRepository.save(order); eventPublisher.publish(new OrderPaidEvent(order.getId())); return PaymentResult.success(order.getId()); } }两者的差异就是“能用的代码”和“可维护的代码”之间的鸿沟。前者把逻辑塞进一个动作里后者把领域行为分配给了正确的对象。而这个差异必须在生成阶段解决不能指望事后靠人肉 Review 来救。团队里人的注意力是有限的每天 Review 十段代码就已经很疲惫如果生成器输出的每一段都是“看着能跑但细看满身毛病”那 AI 编程最终只会让团队更累而不是更轻松。2. 核心链路设计规范如何进入生成器的“大脑”2.1 提示词工程把 CleanCode 规则转译成模型听得懂的指令要让模型输出符合规范的代码不能简单地在提示词里加一句“请写出规范的代码”这等于没说。我的做法是把 CleanCode 的核心原则拆解成提示词体系分成四层来注入。第一层是角色设定告诉模型它不是一个普通程序员而是一个有多年架构经验、对代码质量和可维护性有极高要求的资深工程师。第二层是规范清单把命名、函数长度、职责边界、异常处理、测试要求等以规则列表的形式显式写入。第三层是示例驱动给模型一两个“黄金示例”让它模仿结构而不是凭空发挥。第四层是输出约束明确告诉它哪些写法是禁止的比如禁止超过三层嵌套、禁止出现一次性变量、禁止吞异常等。其中规范清单最重要也最容易写砸。我尝试过把中文版的《代码整洁之道》规则一股脑塞进提示词结果是模型上下文被占满生成的代码又长又啰嗦反而失去了实用性。后来我换了一种策略每一条规则都改成“不要做什么 应该怎么替代”的句式比如“不要使用无意义的命名应该用能表达业务含义的名字”“不要在 Service 中直接操作 JSON 对象应该建立独立的 DTO 转换层”。这个转换非常关键。大模型对负面指令和正面指示的遵循率差距很大单说“不要 XXX”容易激活模型的补全惯性导致它一边答应一边继续犯同样的错误而“应该怎么替代”则给出了明确路径让模型有路可走。2.2 生成后校验生成器必须自带“质检员”只有提示词还不够因为模型的输出天然带有随机性。无论提示词写得多么精细仍然可能“抽风”生成一段不合规范的代码。所以在生成链路中我还加了一个校验层作用相当于流水线上的质检员。校验规则分成了两类。第一类是硬性规则任何违反都会直接判定不合格并要求重新生成比如硬编码魔法值、空 catch、方法行数超过阈值、外部调用没有异常处理等。第二类是软性规则违反不会直接拒绝生成但会在返回结果时打上警告标记比如函数参数过多、圈复杂度超标、缺少必要的注释等这类规则通常需要结合上下文判断不能一刀切。硬性规则的实现可以依托现成的静态分析工具也可以自己写 AST 级别的检查脚本。我比较推荐从 AST 层下手因为正则表达式根本挡不住人类和 AI 的复杂写法。写一个简单的规则引擎读取生成的代码并解析成语法树然后遍历节点检查有没有违反规则的情况遇到问题就带着具体行号返回给生成器让它重新修正。实测下来经过一轮“生成—检查—修正”的循环之后代码合格率能提升到九成以上。2.3 分层生成先定接口再写实现早期版本失败的很大一个原因是让模型直接一次性地生成完整实现结果虽然代码风格不错但架构一团糟。Service 依赖了 Repository 的实现在类里Controller 直接操作实体对象领域逻辑纠缠在展示层。这不是提示词能解决的因为一次性输出几千米长代码时模型的注意力会分散很难把几百个对象间的关系安排得井井有条。现在我改成分层生成策略。第一步先生成接口定义和领域模型包括类名、方法签名、入参出参、异常类型第二步生成核心业务逻辑严格依赖接口编程第三步生成测试代码和边缘处理。每一层的输出都被下一层当作“事实依据”不允许跨层依赖。这个策略听起来慢其实总体效率反而更高。就好比盖房子先画好图纸标好房间布局再一堵墙一堵墙地砌远比一边砌一边改图纸要快。模型在每一层的任务边界都很清晰输出的代码自然就规整而且生成结果几乎是可预测的。3. 实操复盘从业务需求到规范代码的完整链路3.1 一个典型需求的生成示例我拿一个非常常见的业务场景来演示——库存扣减。需求是这样的用户下单后系统需要校验库存充足扣减库存数量同时记录扣减流水防止超卖。这个需求在电商项目中到处都是也是并发场景下最容易出问题的逻辑。把需求输入生成器后它首先输出的是这样一组接口定义public interface InventoryService { DeductResult deduct(String skuId, int quantity, String requestId); } public record DeductResult(boolean success, int remainStock, String reason) {}然后生成核心实现我直接截取关键段落Service public class InventoryServiceImpl implements InventoryService { private final InventoryRepository inventoryRepository; private final DeductRecordRepository recordRepository; private final IdempotencyChecker idempotencyChecker; public DeductResult deduct(String skuId, int quantity, String requestId) { if (quantity 0) { return DeductResult.failure(扣减数量必须大于0); } if (!idempotencyChecker.isNewRequest(requestId)) { return DeductResult.failure(重复请求); } Inventory inventory inventoryRepository.findBySkuId(skuId); boolean deducted inventory.tryDeduct(quantity); if (!deducted) { return DeductResult.failure(库存不足); } inventoryRepository.save(inventory); recordRepository.save(new DeductRecord(skuId, quantity, requestId)); return DeductResult.success(inventory.getRemainStock()); } }注意几个关键点幂等校验被放在了第一层库存不足时不抛异常而是返回失败结果的业务语义扣减逻辑封装在 Inventory 对象内部的 tryDeduct 方法中不在 Service 层操作库存数字整个 Service 只做协调不做计算。这就是“领域逻辑下沉、应用层薄”的典型写法。换成没有约束的普通 AI 工具大概率会生成一个把库存数字取出来做减法再存回去的版本看起来简单但超卖的风险、幂等的缺失、流水记录的遗漏全都是隐患。所谓技术债往往就是这些隐藏点在没有规范约束时被一笔带过。3.2 生成代码的调测与验证代码生成之后不能直接信要按一套默认流程验证。我的做法是先跑静态规则检查再跑单元测试最后做一次人工阅读抽查。静态规则检查主要看有没有明显的硬伤未使用的 import、缺失的边界校验、违反命名规则的字段等。单元测试这一层生成器会同步输出对应的测试用例把核心业务路径覆盖住只需要执行一遍确认全部通过。人工阅读抽查则针对关键业务点比如这个示例中的并发扣减场景我会重点看 tryDeduct 方法内部是否用了乐观锁或行级锁。这里有个非常重要的经验AI 生成代码最容易出现的 Bug 不是语法错误而是“测试设计与实现代码共享了同一个错误假设”。也就是说模型生成实现的时候把某个参数当默认值处理生成测试的时候仍然用同一个默认值推断两边自洽但和真实业务不符。破解办法是给生成器提供明确的业务规则清单把不可变更的事实写清楚比如“库存字段不允许为负数”“扣减数量必须为正整数”让测试不再跟着实现瞎猜。3.3 与主流 AI 编程工具的配合用法我理解很多人不一定用这套生成器而是用现成的 AI 编程软件、付费插件比如常见的一些 AI 编程助手。它们本身功能很强代码补全和对话能力都不错缺的恰恰是统一的项目级规范约束。所以我把这套 CleanCode 规则体系额外做成了一份“提示词模板”可以整体粘贴到支持自定义指令的 AI 工具中。模板包含十几个硬规则和软规则正好覆盖一次 AI 编程会话的输入范围。实测下来使用同一份模板之后不同工具生成代码的规范水平差距大幅缩小。有一点要提醒提示词模板不是粘贴完就能一劳永逸的。不同模型对规范的理解能力差别很大过一遍模板之后最好用一个标准需求试测看看输出的代码是否真的遵循规则。如果模型老是不听约束那就得考虑在项目配置里加更严格的重试机制让生成结果经过规范化后再复用。4. 落地过程中的常见问题与排查技巧4.1 生成代码“看着规范”但运行不了这是最让人头疼的一类问题它比语法错误隐蔽得多。典型表现代码结构完全符合 CleanCode 要求命名清晰、函数短小、职责分层但一跑测试就报空指针或者数据对不上。原因多数出在生成器的“规范压倒了业务正确性”。为了满足短函数规则模型强行把关键计算拆进了一个独立方法但方法间的参数传递逻辑没有理顺或者为了满足“不直接操作实体”的规则加了一层 DTO 转换结果字段映射漏了一个。这种问题用静态检查发现不了必须靠运行时的测试来兜底。排查思路也很固定先在生成结果与最初需求的映射关系上找差异——需求里有几个输入字段生成代码里是不是都有对应需求里有几个业务分支生成代码里是不是都有处理需求明确写了什么不可为空的约束生成代码里有没有校验。按这个逻辑逐条对照几乎都能在十分钟内定位到问题。经验证明这类问题的高发区集中在序列化/反序列化、时间格式化、金额计算精度这几个容易“看似正确处理实则偷梁换柱”的位置。4.2 提示词不稳定同一需求多次生成结果差异很大模型输出天然带有随机性如果不加控制同一个需求可能生成出三个截然不同的版本。有的人觉得这是灵活性但从工程角度看这是灾难——代码生成一旦不可复现就无法纳入正常的开发流程。我这边用的对策是约束生成温度同时引入“输出骨架固定”的技巧。在提示词里明确要求模型先输出方法签名和注释再填充实现并明确指定关键类型不允许自己发明新的类。相当于给生成过程上了两道保险一道锁死输出的轮廓一道锁死模型可用的概念范围。如果工具不支持调温度也可以在提示词里加“请严格按用户给定类型实现不要新建额外类型”这类限定语句效果会好很多。另外给生成器配置一个“规范记忆库”——把历史生成中被人工评审通过的好代码片段保存起来在后续生成时作为初选示例能大大提升稳定性。4.3 团队协作中的规范一致性控制团队用 AI 编程时经常遇到的问题不是某一次生成不合规而是不同成员用不同的工具、不同的提示词生成出来的是不同风格的代码。今天 A 提交的代码用一个命名前缀明天 B 提交的代码又用了另一个Review 变成大型找茬现场。我建议团队把规范文件当成一等公民和代码一起入库。建立 .ai-code-rules 目录或 equivalent 配置文件里面统一定义命名、分层、异常处理的细则并强制所有 AI 编程工具在生成前加载这里的规则。同时将生成代码纳入统一的 CI 检查流程凡是命中硬性规则违规的直接在流水线中拦截不让脏代码进入合并分支。这套做法最大的价值在于把“规范”从个人经验变成了团队流程的一部分。哪怕团队里每个成员用的工具不同、习惯各异只要规则统一、检查统一输出的代码质量就是收敛的。这也是“生成即规范”在团队层面真正的含义——不是靠某个人盯出来的而是靠机制兜住的。5. 关于这套生成器的几个可复用机制5.1 规范不搞一刀切软硬规则的取舍前面我提到了硬规则和软规则这里展开说一下取舍逻辑。硬规则适合那些绝对正确或绝对错误的场景比如“禁止吞异常”“禁止硬编码数据库连接密码”这些标准没有争议直接用程序判定最可靠。软规则就麻烦一点比如“函数行数不要超过 50 行”有的方法天生就需要复杂编排硬性限制反而会把逻辑切得支离破碎。我的处理方式是给软规则加一个“解释通道”——如果模型或开发者认为某段代码需要突破规则必须在提交说明中写明理由。这种“可以破例但必须交代”的模式既保留了约束力又避免了教条化。实际运行中软规则的误报率很高刚开始会惹人烦。后来我调整策略规则命中时不立即拦截而是生成一个“建议清单”让开发者在提交代码时选择忽略或采纳。一旦选择忽略这个位置会沉淀为一条例外备案下次生成遇到类似情况会自动参考。规则跟着真实场景迭代比死板的静态清单有效得多。5.2 生成器的“记忆能力”如何构建AI 编程工具生成代码时最大的遗憾是对项目历史一无所知。它不知道这个项目里已有的类命名风格、不知道哪个模块依赖了哪个模块只能靠上下文窗口里有限的代码片段去猜。所以我的生成器做了一个非常轻量的记忆模块每次生成结束后自动抽取当前项目的包名、类名、方法签名和依赖关系形成一份“项目知识摘要”。下一次生成时这份摘要会被自动优先注入提示词。这个机制你完全可以借用到任何 AI 编程工具上——新建一个 knowledge.md 文件把项目的关键结构、命名偏好、不允许触碰的模块写进去生成前让它读取。实测下来这个简单的动作能让生成代码与既有代码的结构一致性提升一大截。尤其是重构老项目时AI 终于不再把老代码的风格全部推翻重写而是顺着已有结构做增量修改。5.3 给 AI 编程新手的三条实战建议如果你是刚开始用 AI 生成代码的开发者我给三条最实用的建议。第一不要在空白项目里直接让 AI 写全部代码先让它画结构再填实现否则你会得到一个看似完整但没人看得懂的大型泥球。第二每个生成结果都要带着“这个代码我明天还要能改得动”的标准去审视如果看着就头晕那说明规范度不合格趁早重做。第三一定要保存自己项目里多次验证过的提示词片段。你自己的业务有自己的特殊边界约束只有把试验过、可靠的那段规则沉淀下来AI 工具才能真正贴合你的项目。没有经过定制打磨的通用提示词生成的永远是通用风格的代码谈不上工程级规范。我个人的体会是CleanCode 和 AI 编程天然应该组合在一起AI 负责速度和广度CleanCode 负责质量和长期价值。这套生成器做了三十四期最大的收获不是说让 AI 完全替代人而是让人从“盯着 AI 改 Bug”的泥潭里走出来把注意力放到真正需要判断力的架构和需求层面。只要规则进得了生成链路、规范经得起运行验证AI 生成代码这件事才能真正从玩具变成生产力。