本节摘要:机制与选型解决"怎么生成",本节解决"生成之后怎么办"。一条成熟的生成管线要回答四个问题:谁来触发生成、产物进不进版本库、评审怎么评、源头变了怎么同步。本节按管线流程逐环拆解,给出两种主流入库策略的取舍和一册可直接抄用的检查清单。
没有管线的代码生成是手工作坊:某位工程师本机跑一次生成器、把产物拷进仓库,从此没人说得清产物对不对、要不要重生成。三个月后接口改了,生成的编解码函数还是老版本,排查两天才发现根因。管线的意义就是把"一次性动作"变成可重复、可验证、可追溯的流程。

管线的第一环不是生成器,是源头描述——接口定义文件、带注解的类型声明、字段清单。三条铁律:源头必须入库且唯一(同一份描述散落两处,产物必然打架);源头变更必须走与代码同级的评审(它决定生成多少行代码,影响面比普通代码大);源头要带版本或校验和,产物里回写这个标记,让每一份产物都能回答"我来自哪个版本的源头"。
生成器本身也是资产:版本必须锁定(工具升级可能悄悄改变产物格式),升级生成器要单独立变更、重生成全部产物、把差异整体过评审。混用两个版本的生成器产出的产物,是代码库里最难排查的一类污渍。
触发方式从轻到重:手动命令(脚本化,但不强制)、构建钩子(构建前自动跑,本地不易忘)、持续集成强制(在无差别的干净环境里执行,是"再生成一致"这条不变量的唯一权威验证点)。成熟项目通常三者并用,以持续集成为准绳。
产物处置有两个流派,取舍值得展开。入库派把生成产物作为普通文件提交进版本库。好处:评审可见(评审者能直接审阅生成结果)、构建不依赖生成器环境(克隆即编译)、产物变更历史可追溯。坏处:仓库体积膨胀、源头与产物可能失同步。构建期派不提交产物,构建时现场生成。好处:产物永远新鲜、仓库干净。坏处:所有人都要配好生成器环境、构建时间变长、评审时看不到最终代码。经验法则是:面向人的库选入库派,面向机器的中间产物选构建期派——前者需要审计与可读,后者只求新鲜。
整条管线只押注一条不变量:干净环境重新生成,产物与库中完全一致。围绕它建立三道验收。其一,一致性检查:持续集成里重生成一次,与提交产物做差异比对,不一致即失败——这一道直接消灭"忘了重生成"这一最高频事故。其二,产物测试:生成代码也是代码,序列化函数要有往返测试(编码再解码必须还原),宏展开产物要有最小用例。其三,人工修改检测:在产物头部写入"本文件由生成器产出,请勿手工编辑"的标记注释,评审工具扫描手工改动即告警——发现有人手改产物时,正确的动作是回灌修改到模板,而不是放行。
⚠️ 常见坑:源头与产物分开两次提交。中途拉代码的人会拿到"新源头配旧产物"的组合,问题要到运行期才炸。成对提交、成对评审,是管线纪律里最值得死守的一条。
这套管线听起来是构建系统的活,与元编程的关系其实是一体的:生成器是宏思想在仓库尺度的投影,源头描述是输入语法树,一致性检查是"展开可重现"的工程化表达。理解了这一点,第六章讨论元编程的维护性时,你会认出很多同款问题。
把四个环节落到日常操作,核心是一条"验证式再生成"命令——无论团队用什么构建系统,骨架都是同一副:
generate: # 环节二:可重复触发 run-generator --from interface.yaml --out generated/ verify: # 环节四:验收 run-generator --from interface.yaml --out tmp-check/ diff tmp-check/ generated/ # 再生成一致性比对,不一致即失败 check-header generated/ # 扫描"请勿手工编辑"标记被改动 test generated/ # 生成产物的往返测试与最小用例
操作→结果:任何人本地跑一次就能验证产物新鲜度,持续集成里跑的是同一条命令——本地与流水线共用一个真相源。解读:注意脚本里没有"聪明"的东西,它的全部价值在于把不变量变成一条谁都能跑的命令。管线工程化的常见误区是追求复杂:一整套自研平台不如一条会红会绿的验证命令。变式:多人协作时把这条命令挂到代码合并的必过检查上,"忘了重生成"从事故降级为一次失败的合并请求。
再补一条演化期的经验:管线的各个环节不必一次建成,但建设顺序有讲究。先立"再生成一致性"检查(一小时的活,消灭最高频事故),再补产物标记与手工修改检测(半天),然后是金样例测试(随生成器演化逐步积累),最后才考虑触发自动化与平台化。反过来先建平台后补检查是常见弯路——平台解决"方便",检查解决"正确",正确永远优先于方便。判断管线成熟度也因此有个简单标尺:不看自动化程度,看"一个新同事第一次改源头描述,能不能不看文档就走完全流程不踩坑"。
最后提醒一个容易被当成管线问题的非管线问题:生成产物引发的连锁重编译。入库派的大型项目里,一个被广泛引用的产物重新生成,会拖动几十个下游模块重编——这不是生成器的错,是依赖设计问题。缓解思路是"产物切小、接口稳定":把大产物拆成按模块的小产物,引用方依赖稳定接口而非具体产物文件,重生成的影响面自然收敛。管线与架构在这里握手:好的依赖结构是管线效率的地基。
至此,"让代码写代码"的技术侧全部讲完。下一章换一副眼镜:这些能力落到工程里,性能、调试、安全三本账各自怎么记。