1. 先想清楚优化目标别把推力使在错的方向上做代码生成器的优化最怕的不是技术复杂度而是把资源全部砸在错误的目标上。我见过不少团队上来就调生成模型的参数、加显卡、扩展上下文窗口结果生成速度上去了代码质量反而下降一次代码评审打回来几十个文件最后人工返工的时间比原来手写还长。我的建议是动手优化前先做一次数据驱动的体检。把生成器在生产环境里的表现拆成几个可量化的维度一次生成的平均耗时、代码评审的通过率、生成结果的修改率即开发者拿到后需要改动多少才能合入、以及单次生成的成本。这四个数字基本能反映一个生成器的真实健康度。我遇到过的一个真实场景是这样的项目里有一个内部代码生成器根据接口定义生成服务端 CRUD 代码。初始版本能跑通但平均修改率惊人地高——开发者拿到生成代码后平均要改动 40% 才能过审。大家当时的直觉是模型太笨于是加了更大的模型、更长的上下文结果修改率只降了两个百分点成本却翻了三倍。后来我们把生成代码按问题类型归类才发现最大的痛点根本不是智能程度而是模板里的命名规则不一致、导入语句缺失、以及非空判断的遗漏。这些是模板工程层面的硬伤不是模型能力的问题。所以优化策略的第一步永远是明确方向。我习惯按这个顺序来排优先级正确性优先生成的代码必须能过编译、过基础 lint这是底线可读性次之命名、结构、注释要接近团队资深开发者的手写水准性能再次在保证前两者的前提下尽量缩短生成耗时、降低 token 消耗可维护性最后模板和生成逻辑本身要容易演进。这个顺序看起来像废话但真到落地时很多人会反过来。性能优化是最好做的效果也最直观所以最容易吸引人的注意力正确性和可读性则需要慢慢打磨没法用一两个指标呈现。可恰恰是这两个看不见的维度决定了生成器上线后到底是个帮手还是另一个麻烦。1.1 用代码审查数据反推优化重点优化方向不该靠猜而要靠证据。最省事的方法是盯住代码评审阶段的反馈。每次开发者在评审生成代码时提出的问题收集起来归类你会很快看到问题分布。比如这一批里有 30% 的问题是空指针风险有 20% 的问题是命名不符合规范有 15% 的问题是缺少日志。这个数据比我见过的任何 benchmark 都诚实。因为它反映的是真实业务环境下的使用体验而不是一个精心构造的测试集。拿到这些数据之后再决定模板怎么调、生成策略怎么改就有依据了。1.2 为什么质量优化要先于性能优化我先说一个反直觉的结论在一套代码生成系统里性能优化通常应该排在质量优化之后。原因很简单——生成得再快如果结果需要大改那开发者花费的总时间并没有减少只是把等待的时间换成了修改的时间。甚至更糟因为生成代码的修改往往比手写还痛苦你总要花很多精力去理解和纠正机器生成的屎山。反过来如果把模板、上下文、约束这些质量因素先打磨到位哪怕生成慢一两秒开发者拿到就能直接用整体效率反而高得多。我在实际项目中验证过这个逻辑把修改率从 40% 压到 15% 之后团队周迭代速度提升了将近一倍而这个过程中生成器本身的吞吐几乎没有变化。2. 模板设计是优化策略的起点从能生成到生成得对代码生成器最容易被低估的部分就是模板。很多人以为模板就是拼字符串把变量插进去就行结果生成的代码要么混乱不堪要么风格完全不统一。模板设计的好坏直接决定了生成代码的底线质量。我这里的模板是广义的——不光指文本模板还包括生成规则、校验规则、命名规则等一切决定输出形态的东西。优化模板的目的是让生成结果从能跑变成能过审。2.1 语义化模板用占位符表达意图而不是拼接字符串我自己早期吃过拼接字符串的亏。当时图省事直接用了语言原生的字符串模板把一段代码拼出来。第一次跑通的时候还觉得挺爽但业务规则一复杂就崩了条件分支满天飞缩进乱七八糟稍微改一个需求就得在生成逻辑里翻半天。后来我把模板全部改成了语义化结构。核心思路是模板里只表达这一段代码的骨架长什么样具体的标识符、类型、注释全部通过独立的配置去填充。举个例子一个实体类的模板库里存的是结构化定义包含字段名、字段类型、校验规则、关联关系生成器做的事是把这些定义映射到语义模板的各个槽位里而不是在一大段代码字符串里做替换。这样做有几点好处字段新增时模板不需要改动业务规则如非空、唯一、默认值集中配置不会散落在各段拼接逻辑里生成逻辑可以和模板独立测试模板的改动有版本记录可追溯。2.2 分层模板结构层、风格层、领域层好的模板体系应该分层就像操作系统分内核态和用户态一样。我把模板分成三层结构模板定义代码的基本骨架比如类结构、方法结构、模块组织。这一层几乎不随业务变化是模板里最稳定的部分风格模板定义命名风格、注释风格、格式化偏好。比如驼峰命名还是下划线命名私有方法是否要写文档注释。这一层由团队规范驱动领域模板定义特定业务场景的生成规则。比如电商订单的状态机该怎么生成权限校验应该放在控制器的哪一层。这一层变化最频繁也是最需要版本管理的一层。每层各司其职。优化时你只需要改动对应层不会牵一发动全身。我之前有一次只改了风格模板里日志统一走 slf4j这一条配置所有模块的生成结果全部生效一行业务模板代码都没动。2.3 约束注入把边界条件写进模板生成代码最怕的是只生成了正常路径而没有考虑边界情况。最简单的约束注入策略是提前内置一组公共校验规则生成时自动注入到代码里。常见的约束类型包括空值校验所有从接口收到的参数进入业务逻辑之前先做非空判断长度与格式校验字符串字段按数据库长度限制统一校验避免入库报错权限校验涉及修改操作的接口统一检查当前会话是否有对应权限幂等处理写操作统一带幂等键防止重复提交。把这些规则做成可复用的模板片段按配置注入。比如一个字段配置了非空 长度不超过 32生成的字段校验代码自动包含两层判断不需要在生成逻辑里手写 if 嵌套。值得强调的是约束注入不该只靠生成器自觉还要在产物里留痕。也就是说生成的代码要能一眼看出这一步是自动生成的约束逻辑这样开发者审查时不会误删关键校验后面出现问题也能快速定位是模板问题还是改动问题。3. 生成性能的优化缓存、增量与并发调度质量优化有了着落再来看性能。纯粹追求生成速度没有意义但等一个接口几十秒确实是会让人崩溃的事情。性能优化的思路不是让单次生成更快而是让它看起来很快且总成本很低。3.1 静态分析驱动的增量生成代码生成器最常见的浪费是每次改动一点点就要全量重新生成。比如后端接口层只加了一个字段结果整个 CRUD 文件全部重新生成一遍其他没变的部分也跟着刷新。这不光是计算资源的浪费更大的风险是开发者手改过的代码被覆盖掉。我采用的做法是先用静态分析工具扫描项目确定本次改动的依赖范围然后只对受影响的部分做增量生成。具体来说维护一个依赖图描述接口定义 - 实体类 - 数据访问层 - 服务层之间的映射每次生成前对比定义的变更摘要基于内容哈希只重新生成变更链路涉及的文件未受影响的文件直接跳过哪怕生成器版本升级了只要定义没变就沿用旧产物。增量生成在实践中的收益非常明显。原本一个模块从改动到生成完毕要 20 秒现在平均只要 3 秒左右。开发者几乎感觉不到生成过程体验上接近改了定义马上就在代码里生效。3.2 三级缓存策略模板、中间结果、最终产物缓存是性能优化里最直接的手段但很多人只会做一个简单的输出缓存命中率低收益也不够大。我给生成器设计了三级缓存第一级是模板缓存。模板解析后的结构比如语法树、渲染脚本只解析一次后续直接复用第二级是中间结果缓存。从定义到填充完成但未格式化的生成中间态按定义的哈希值缓存第三级是最终产物缓存。按文件路径 定义哈希 模板版本 生成器版本的组合键缓存命中时直接返回文件内容连格式化都不需要重跑。缓存设计上有个细节要特别注意缓存键必须包含生成器的版本号和模板的版本号否则升级之后读到旧缓存会带来非常难排查的诡异问题。我见过太多因为漏掉版本号而导致的为什么改了代码不生效事故。3.3 并行生成与资源限制增量生成能解决大部分场景但在首次初始化或者大规模重构时还是会碰到一次要生成几百个文件的情况。这时候就需要并发调度。并行生成最核心的一个原则是控制粒度不要无限并发。并发数太高会导致数据库连接池被打满、目标目录的 IO 冲突甚至让本地开发机的 CPU 飙到打不了字。我自己用的经验值本地开发环境并发数压在 8 以内CI 环境压在 16 以内超过这个数收益就不明显了。另外并行之间不能有共享的可变状态。比如多个线程同时写同一个缓存文件、同时刷新同一个依赖图这些问题出现时不会立刻报错而是在运行半个小时后随机炸一下特别难定位。所以并行生成时我把所有共享资源都做了隔离或者改成不可变数据宁可多复制一份对象也不要埋下并发隐患。4. 上下文窗口与提示词工程的优化用最小的 token 撬动最多的信息如果你是靠大模型做代码生成那优化重点集中在提示词工程和上下文管理上。这里的收益空间特别大而且几乎所有调整都不需要改生成核心只需要改一层包装器。4.1 上下文裁剪让模型聚焦在必要信息上大模型生成代码时上下文塞得越多并不代表越聪明反而会让模型注意力分散甚至可能被无关代码带偏。我做过一个对比实验同样的任务一个提示词把整个项目的相关代码全部粘贴进去另一个只保留接口定义的字段列表和三条示例结果后者的准确率更高token 消耗还少了 70%。我的裁剪策略是按需求粒度来组织的需求是什么、要生成什么文件、遵循什么规范输入数据结构的完整定义字段名、类型、约束2 到 3 个风格相近的示例片段不是完整文件明确标注不要做什么的负面约束。负面约束往往被忽略但它非常有效。比如不要生成单元测试不要添加日志提前说明不仅能省 token还能避免生成结果超出预期省下后续删除的功夫。4.2 少样本示例的动态选择少样本提示里的示例直接影响生成风格。固定写死几个示例的做法对复杂项目不太够用不同模块的风格差异会比较大。我现在的做法是维护一个示例片段库里面按模块类型、语言风格、复杂度打上标签每次生成时根据当前目标任务的特征做检索挑选最接近的 3 到 5 个示例动态拼进提示词。这套方案的雏形非常简单开始时就是用关键词匹配后来进阶到用向量相似度检索。动态选择的效果是生成结果在风格一致性上有明显提升特别是面对不同业务团队维护的、风格差异较大的代码库时优势格外明显。4.3 用结构化输出协议约束生成结果大模型直接输出纯文本解析时总有一些边缘情况——多了一个反引号、漏了一个大括号、把注释写在类名前面。为了减少这类问题我建议生成器定义自己的一套结构化输出协议。协议的核心规定包括代码块必须用统一标记包裹且标记内不允许再出现同类的嵌套标记元信息如文件路径、依赖清单统一放在代码块之前的约定区域生成结果里不允许出现解释性文本任何说明都放到规定的注释区对严格要求的场景比如生成 JSON 配置禁止在代码块内输出任何非 JSON 内容。这听起来像在约束模型实际上也是在帮助模型。明确的协议比模糊的请生成标准代码要更容易遵守因为边界清楚了模型也就不容易自由发挥。实测中这类规范化提示词能把输出解析失败率从 15% 左右压到 2% 以内。4.4 失败重试与回退策略再好的提示词也不是每次都能一次成功。我推荐保留一个重试 回退的机制第一次失败时先做简单的结构化修复补括号、清多余空行、修正缩进修复不了带上失败信息和部分输出重新调用模型生成一次并在提示词里说明上次结果存在以下问题请修正两次仍失败就自动切到保守生成路径只输出确定性模板渲染的代码不用大模型做智能生成。这套机制的价值在于它保证生成器在异常情况下依然有可用输出不会让整个流程卡死。实际跑下来大约 95% 的失败在第一次重试后就恢复正常了剩下的 5% 走保守路径也能产出能用的骨架代码。5. 产物质量优化让机器的代码通过人的审查模板和上下文把代码生成出来了但这只是上半场。下半场是产物的收尾加工——确保生成代码符合团队规范、没有冗余引用、可以顺利合入代码库。5.1 强制格式化与静态检查生成代码的第一个问题是格式不规范。模型生成的代码有时候缩进是对的但空行数、运算符两侧空格、字符串引号风格可能不符合团队规范。靠提示词让模型自己保证格式不如在生成链路里加一道固定的收尾工序。我的做法是生成结束后自动对每个文件执行语言对应的格式化器和静态检查工具例如 Java 场景里的 google-java-format 加 Checkstyle前端场景里的 Prettier 加 ESLint。如果检查出问题能自动修复的直接修复不能自动修复的比如命名不规范、API 误用就要上报到日志里而不是直接放过。这道工序看起来像是在欺负生成结果但它有一个很重要的副作用强制格式化后再去和旧版本 diff你会发现代码变更范围变清晰了代码审查的人能更快地确认哪些地方是生成器生成的、哪些是开发者手改的。5.2 死代码与未使用依赖的清理模型生成代码时有个坏毛病就是喜欢留下一些看起来有用但实际没用的东西。比如生成一个服务类里面自动 import 了五个类真正用到的只有两个又比如生成了一个工具方法但从头到尾没人调用它。这类死代码一旦多起来生成的模块会迅速变得臃肿。清理手段分两步第一步是自动清理冗余 import。这个相对简单IDE 或静态检查工具都能做。第二步有点麻烦需要结合调用链分析找出整个生成模块内都没有被引用的函数和类然后移除或标记为待确认。我建议不要立刻删除而是统一移到一个_deprecated区或者打上此方法疑似未使用后续人工确认的注释。这样既满足清理目标又不会因为误删让开发者恼火。5.3 让生成代码具备可测试性代码生成器产出的代码经常是能跑但是没有测试。如果想真正可用生成器还应该在产物里附带一批有用的测试模板。并不是说要自动生成全部业务测试那个当前不现实。更合理的做法是生成每个服务类时同时生成对应的测试框架包含以下几类自动生成的用例空参数和非法参数的校验测试数据访问层 mock 后的服务层逻辑分支测试基础 CRUD 的成功路径测试。这些测试模板跑起来后能显著提高生成代码合入仓库的置信度。开发者拿到手后只需要补充业务逻辑相关的用例不需要从零搭测试框架。实际上我在最近的几次优化里把生成代码附带测试模板列为比减少生成耗时更优先的改进项因为前者对团队的长期价值明显更大。5.4 生成器自身的可观测性代码生成器也像线上服务一样需要有日志和指标。否则出了问题你连是什么导致这次生成质量变差都说不清。我至少会记录这些指标每次生成的耗时分布、成功率和失败率模板渲染阶段的报错次数、上下文裁剪后的 token 消耗收尾工序里自动修复了多少问题、修复不了多少问题生成产物的平均修改率这个可以从以后代码提交的 diff 里统计出来。有了这些数据优化就不再是拍脑袋了。比如当空值校验遗漏成为生成代码的主要修改原因时你直接去约束注入层打补丁当格式化工具平均每文件改动 30 行时你就知道需要先调模板的初始格式。这个反馈闭环是代码生成器持续优化最重要的基础设施。6. 实测数据与踩坑复盘优化后的效果和值得警惕的副作用上面说的这套策略我在一个内部服务端代码生成器上完整落地过一轮。学到的经验不少这里挑几个比较有代表性的点分享出来。6.1 一组有代表性的对比数据优化前和优化后的关键指标对比如下指标优化前优化后生成代码评审修改率约 40%约 12%单模块全量生成耗时约 20 秒约 3 秒增量模式输出解析失败率约 15%约 2%平均每个文件冗余 import约 4 个小于 1 个生成后必须手写补的测试骨架100%约 20%这里面最让我意外的倒不是修改率降下来了而是解析失败率。之前总以为模型不听话是常态但把结构化输出协议写清楚之后模型其实非常配合。说明很多问题表达的抽象程度不够而不是模型本身能力不足。6.2 踩过的三个坑值得记下来第一个坑是过度缓存。有一版我加了强劲的缓存机制结果回滚了生成器版本之后缓存键没有及时带上版本号导致团队里一半人拿到的还是旧逻辑生成的代码。排查了很久最后只能强制清缓存才恢复。这个坑彻底教会了我缓存键必须包含所有可能导致输出变化的变量。第二个坑是自动修复过了头。格式化工具默认开启了一堆自动修复规则比如把单引号双引号统一、删掉表达式里看起来多余的括号。结果导致生成的代码 diff 里混入了大量与业务改动无关的格式变更代码评审的噪音大到让人崩溃。后来我学乖了自动修复只开规则明确、对语义零影响的项其他都改为提示方式不做自动改动。第三个坑是并行生成时的磁盘冲突。多个文件同时写入同一个目录偶发出现文件被占用或者目录结构未生成的错误。单独跑每个文件都是好的并发就会随机炸。最后靠把所有写盘操作都收敛到单一写入线程用队列串行写文件才稳定下来。性能损失不多但彻底解决了偶发问题。6.3 后续还可以扩展的方向优化到这里生成器已经足够作为团队的日常开发工具了。不过还有一些我觉得值得继续投入的方向列出来供参考把代码评审的反馈自动回流到模板和示例库里形成持续学习的闭环做跨项目级别的风格适配让同一套生成器在不同团队里输出不同风格互不干扰增加更细粒度的增量更新能力比如只更新实体类里新增字段对应的 getter / setter而不动整个文件接入更多语言和框架的约束模板把已经验证过的约束注入模式复制到新场景下。6.4 我在实际项目操作中的体会最后说点真心话。代码生成器的优化没有一个终点它更像是养一个植物你得持续浇水、修剪、观察长势。最关键的还是前面那条先把目标定对用数据说话。在我的实践里让生成代码通过代码评审永远比让生成速度快 5 秒更值得优先投入。如果只让我留下一句话的话那就是优化策略的核心不是让生成器变聪明而是让它产出的东西更容易被团队接纳和使用。做到了这一点生成速度、成本、稳定性这些指标都会自然而然变好。