结论:用分支隔离客户定制需求短期可行但长期必踩坑,因客户数超3个后merge冲突指数级上升,且硬编码环境差异导致维护成本飙升;应改用运行时特征开关与模块化定制目录实现逻辑隔离。

直接说结论:用分支隔离客户定制需求,短期能跑通,长期必踩坑——尤其当客户数超过3个、定制逻辑开始交叉时,git merge冲突率会指数级上升,feature/center-shanghai这类分支很快变成“不敢动的祖宗分支”。
为什么git checkout feature/center-beijing越用越累
不是分支本身有问题,而是它把“环境差异”硬编码进了代码流里。典型表现:
- 每次主线(
main)发版,所有定制分支都要手动git merge main或git rebase main,稍有遗漏就导致客户线上功能缺失 - 客户A要的功能B刚合入
main,但客户C只想要其中一半逻辑——你得从main里git cherry-pick部分提交,而这些提交往往依赖其他未选中的改动 -
feature/center-shanghai里混着接口地址、字段映射、校验规则等不同粒度的定制,无法按需启用或关闭
application-{center}.yml只是配置隔离,不是逻辑隔离
配置文件能解决URL、超时时间这类参数,但挡不住业务逻辑分叉。比如:
- 上海中心要求订单提交前调用本地风控接口,北京中心走通用风控服务——这没法靠
--spring.profiles.active=shanghai自动切换 - 深圳中心的审批流程多一步人工确认,代码里就得写
if ("shenzhen".equals(center)) { ... },这种硬编码会迅速污染通用模块 - 当某次
main分支重构了订单服务类结构,所有定制分支的同类逻辑都得逐个适配,没人敢保证改全了
真正可落地的替代方案:运行时特征开关 + 模块化定制目录
把分支管理的负担,从Git转移到运行时和构建阶段:
- 前端在
window.CUSTOMER_ID = 'shanghai'注入客户标识,业务代码用if (CUSTOMER_ID === 'shanghai') { ... }包裹定制逻辑,打包时通过Webpack DefinePlugin静态替换,避免运行时判断开销 - 后端把定制逻辑抽成独立模块,放在
custm/shanghai/目录下,通过SPI机制加载——main分支只维护接口定义,各中心实现自己的ShanghaiOrderValidator - CI/CD流水线根据部署目标客户,自动选择构建参数:
mvn clean package -Dcustomer=shanghai,编译时排除其他中心的定制模块,镜像里不带冗余代码
分支不是不能用,而是别让它承载本不属于它的责任。客户定制的本质是“同一套骨架,不同器官”,强行用分支当器官移植工具,最后缝合线会崩得满地都是。











