功能标志与技术债务
如果你开始使用特性标志(Feature Flags),通常会最终得到一个高度可配置的系统,能够在运行时改变其行为——通常是通过许多分散在多个配置源中的标志。这些标志之间往往有依赖关系,因此启用或禁用标志可能会对系统的稳定性带来很大的风险。你通过避免并行分支成功避免了“合并地狱”,但最终陷入了“特性标志地狱”,系统中有数百个标志,没人知道它们的用途。
为了避免这种情况,你应该遵循以下最佳实践:
-
度量:即使特性标志在代码中提供了很多价值,它们本质上也是一种技术债务。你应该像度量代码覆盖率或其他代码相关的指标一样度量特性标志。衡量特性标志的数量、存在的时间(持续时间)、它们在每个环境中的评估情况(例如,在生产环境中100%为真的标志可能意味着该标志可以移除),以及标志的使用频率(调用次数)。
-
集中管理:在一个集中位置管理所有标志,尤其是在你使用不同方法来管理标志的情况下。每个标志都应该有一个负责人和描述,并且要记录特性标志之间的依赖关系。
-
流程集成:将特性标志的管理集成到你的流程中。例如,如果你使用Scrum,可以将特性标志的审查纳入回顾会议中。确保所有与标志相关的人员定期检查所有标志,检查哪些标志可以从系统中移除。
-
命名规范:为你使用的所有类型的标志制定命名规范。你可以使用`tmp-`作为临时标志的前缀,`perm-`作为永久标志的前缀。不要命名过于复杂,但标志的名称应该能够立即表明它是什么类型的标志,以及它在代码库中将存在多长时间。
有些团队喜欢使用一种名为“清理分支”的技术,而其他团队则不太喜欢。这种技术的思路是:当你创建标志并编写代码时,你最清楚当标志被移除时,代码应如何呈现。所以,你会在代码旁边创建一个清理分支和拉取请求,并保持拉取请求打开,直到标志被移除。这个技术在命名规范做得好的情况下最有效。
举个例子,假设你有一个新的特性对话框的标志。带标志的代码看起来是这样的:
function showRegisterDialog(){
if( featureIsEnabled("tmp-new-register-user-dialog") ){
return showNewRegisterDialog();
}else{
return showOldRegisterDialog();
}
}
这段代码在`features/new-register-dialog`分支中开发,你创建了一个拉取请求来合并代码。你已经知道当标志被移除后,代码的最终状态将只使用新对话框,因此你会创建一个新的分支(例如`cleanup/new-register-dialog`)并添加最终版本的代码:
function showRegisterDialog(){
return showNewRegisterDialog();
}
然后,你可以创建一个拉取请求,并保持它打开,直到特性完全发布并且你想要清理代码时。
正如我所说,这种技术并不适合所有团队。在复杂的环境中,维护清理分支可能会非常麻烦,但你可以尝试一下。
没有清理且没有积极维护的特性标志是技术债务,但其优点大于缺点。如果从一开始就小心处理,你可以避免陷入特性标志地狱,充分利用它们在发布和运营应用程序时提供的灵活性。