给 Protobuf 接口加合并闸口前,我会先用 Buf 把 lint 和 breaking check 分层
很多团队的 .proto 文件最开始只有生成代码这一个入口,等到服务数量增加、客户端增多、多人同时改 schema 时,真正先出问题的往往是合并顺序和兼容性边界。今天有人改字段编号,明天有人删枚举值,代码生成仍然能过,消费者却会在更晚的环境里踩坑。Buf 适合放在这个阶段,因为它把 lint、breaking change 检查和 CI 接入整理成一套明确的工程接口,团队可以先把 schema 约束写清,再把合并闸口收紧。

原创示意图:从 proto 变更到 lint、breaking check,再到 Pull Request 合并闸口的链路。 来源:Codex image generation
先把本地反馈做快
我会先要求每个 schema 仓库在本地稳定跑通 buf lint。Buf 的 lint 文档把默认规则集、配置入口和 lint.use 这类定制方式写得很清楚,意思也很直接:先让团队对目录结构、命名和 package 约定形成一致反馈。这个阶段不要急着把所有历史问题一次清空,更适合先在活跃目录里修新问题,再把旧问题列进迁移清单。只要本地命令足够快,开发者才愿意在提交前先看一眼结果。
breaking check 要盯住对比基线
真正决定能否合并的,通常是兼容性,而不是格式漂亮与否。Buf 的 breaking 文档给了最关键的命令形态,buf breaking --against '.git#branch=main' 这种写法可以把当前改动和主干基线直接对比。对团队来说,重点不只是会跑命令,更是先定好 against 谁。我的默认做法是让功能分支始终对比主干,发布分支对比上一个稳定 tag,避免把短期分支之间的临时状态误当成长期契约。这样一来,字段删除、类型变化和服务接口漂移都会在合并前暴露出来。
CI 里要分清硬闸口和例外入口
当本地规则稳定后,再把检查接进 Pull Request。Buf 的 GitHub Actions 文档给了几个很实用的边界。第一,Action 示例支持显式 pin 版本,适合把 CLI 行为固定住。第二,breaking job 可以结合 PR label 做条件跳过,文档里直接展示了 buf skip breaking 这种标签判断。第三,Action 也支持 paths 过滤,只在 schema 目录变更时触发。我的落地顺序通常是:先让 lint 成为所有 schema 改动的必跑项;breaking check 作为默认必跑项;确实需要例外时,通过受控 label 和变更说明留痕。这样既能保护主干,也能保留紧急修复的收口路径。
用 workspace 边界控制仓库复杂度
如果仓库里同时放了多个模块、生成配置和第三方依赖,我会顺手把模块边界整理进 Buf 的 modules 与 workspaces 结构。官方文档把 workspace 说得很清楚,它的价值是让多个模块在同一个仓库里有稳定边界,同时保留统一命令入口。这样团队在拆分公共 schema、业务模块和实验目录时,不必把所有内容都混在一层跑检查。目录边界越清楚,CI 的路径过滤、生成脚本和 breaking 对比也越容易解释。
我会怎样推进第一周试点
第一天只做四件事:补 buf.yaml,让 buf lint 在本地通过;挑一条主干分支作为 against 基线;在 GitHub Actions 里接入 lint 与 breaking;给紧急例外准备受控 label。第二天开始记录失败原因,区分历史债务、真实兼容性风险和配置误报。等一周后,如果开发者已经能根据报错快速定位问题,再考虑扩大规则集或把更多目录纳入检查。团队若还有多语言代码生成链路,我还会把同一套检查结果同步给客户端仓库负责人,避免兼容性判断只停留在服务端。Buf 最有价值的地方,在于它让 schema 治理从口头约定变成可复跑、可审计、可解释的合并闸口。
主要来源
Buf breaking change documentation
Buf GitHub Actions documentation