以前我们团队严格执行标准的 GitFlow 分支管理规范:每次迭代都要拉出 feature/*、release/*、hotfix/* 分支,还要维护万年不变的 dev、test、prod 三大长期分支。结果每天早上开会,大家光是讨论“这个需求合并到哪个分支了”、“为什么测试环境的代码跟生产对不上”、“谁又把冲突代码强推上去了”就能扯皮一个小时。最痛苦的是每周五的发布日,合并分支引起的冲突多到爆炸,简直是研发效能的终极杀手。
GitFlow 最大的缺陷在于它把“代码集成”和“业务发布”强行绑定在了一起。因为大家觉得只有把代码合入 main 或者 release 分支,才代表这个功能上线了。这就导致有些长周期的开发分支迟迟不能合回主干,拖得越久冲突越严重,最后合并的时候只能求神拜佛。
为了彻底摆脱这种折腾,我们团队在上半年痛下决心,全面转向了主干开发(Trunk-Based Development)。所有人每天都直接提交代码到 main 分支(或通过生命周期极短的 PR 快速合入),取消所有的 dev 和 test 分支。而对于还没开发完或者不想立刻对用户公开的功能,我们统一通过代码里的 Feature Flag(功能开关) 来进行发布拦截。
下面是我们团队目前在 TypeScript 项目中落地的一套超轻量、支持动态评估的 Feature Flag 系统实现:
import { createHash } from 'crypto';
export interface UserContext { userId: string; email: string; role: 'admin' | 'editor' | 'viewer' | 'guest'; group?: string;}
export interface FeatureRule { enabled: boolean; allowedRoles?: Array<UserContext['role']>; percentage?: number; // 灰度发布比例:0 - 100 whiteList?: string[]; // 用户白名单 (userIds)}
export class FeatureFlagManager { private flags: Map<string, FeatureRule> = new Map();
constructor(initialFlags: Record<string, FeatureRule>) { for (const [key, rule] of Object.entries(initialFlags)) { this.flags.set(key, rule); } }
// 核心评估算法:判断某个用户是否对该功能可见 public isEnabled(flagName: string, context: UserContext): boolean { const rule = this.flags.get(flagName); if (!rule) { return false; // 默认关闭未定义的安全配置 }
if (!rule.enabled) { return false; }
// 1. 白名单检查 if (rule.whiteList && rule.whiteList.includes(context.userId)) { return true; }
// 2. 角色限制检查 if (rule.allowedRoles && !rule.allowedRoles.includes(context.role)) { return false; }
// 3. 灰度比例检查 (基于哈希算法分流,保证同一用户的评估结果具有幂等性) if (typeof rule.percentage === 'number' && rule.percentage > 0) { const hashInput = `${flagName}-${context.userId}`; const hashVal = createHash('md5').update(hashInput).digest('hex'); // 取 md5 的前 8 位转为整数,并对 100 取模 const userScore = parseInt(hashVal.substring(0, 8), 16) % 100; return userScore < rule.percentage; }
return true; }}
// 模拟配置定义(生产环境通常从 Redis 或 Consul 动态拉取)const localFeatureFlags: Record<string, FeatureRule> = { 'new-payment-gateway': { enabled: true, allowedRoles: ['admin', 'editor'], whiteList: ['user-10023', 'user-10024'], percentage: 10 // 只有 10% 的用户能访问到新网关 }, 'beta-dashboard-v2': { enabled: true, percentage: 50 // 对 50% 用户开放 }};
// 演示具体使用场景const manager = new FeatureFlagManager(localFeatureFlags);
const testUser: UserContext = { userId: 'user-99999', email: 'test@company.com', role: 'viewer'};
if (manager.isEnabled('new-payment-gateway', testUser)) { console.log('🚀 执行新版支付网关逻辑');} else { console.log('📦 执行老版支付网关逻辑 (Fallback)');}通过这套逻辑,我们把还未测试通过的“新支付网关”代码直接发布到了生产环境,但由于开关限制,只有白名单用户或部分灰度用户能走到对应逻辑。这就达到了“随时合并、随时发布、按需上线”的效果。
引入主干开发之后,我们团队分支冲突的概率下降了 90% 以上。以前发布大版本需要全员戒备、封网三天,现在随时随地都能将最新的 main 分支构建并推送到生产环境,因为未完成的功能已经被 Feature Flag 完美屏蔽掉了。
不过主干开发对单测覆盖率和 CI 自动化门禁的要求非常高。如果大家没有信心建立完善的自动化流水线,主干开发可能会成为线上 Bug 的放大器。
你们团队目前在用哪种分支管理策略?有没有尝试过 Feature Flag 的落地,在落地过程中又遇到了哪些阻碍和踩坑点?