1. 问题
功能开发完成后,因为某种原因不进行发布。通常采用两种策略:采用功能分支的方式进行开发,延迟合并和发布。另一种是方式是,代码进行合并和发布,通过功能开关来控制该功能是否被用户感知。
2. 功能分支
功能分支的使用:通过对每个功能创建一个feature分支,功能完成后合并到release分支或master分支。
2.1 功能分支的好处
- 开发阶段,同时进行各功能的开发,相互独立,彼此不受影响
- 大功能或远期功能开发,不受版本发布的影响
2.2 功能分支的问题
- 分支时间长:合并时容易产生冲突
- 分支多:合并分支时,容易遗漏
- 违背持续集成:分支不能持续的进行合并,不能尽早发现问题。即便feature分支进行了测试和功能验证,不能保障release分支没有问题。仍需要对release分支进行回归测试。
- 其他场景:修改公共功能或进行重构时,会涉及其他分支中大量引用。合并时会产生大量的编译错误,也可能需要多个分支进行方法调用的修改。团队可能会为了避免产生未知的错误,而减少重构。而重构在软件开发过程中是必不可少的,不做重构,技术债就会越积越多。
3. 功能开关
功能开关主要用于主干开发,通过布尔类型的配置项控制软件功能是否对外部可见。这要求某个功能的各个相关模块共同读取该功能的控制开关。
3.1 功能开关的使用
一个功能可能会涉及多个方面,如前端页面、后端功能、数据库等。对于该功能的每个方面可能都需要进行控制。
3.1.1 功能控制开关
系统需要在配置文件中提供相关功能的配置项,其他相关模块读取该配置项。以Springboot项目为例,在application.properties文件中:
featureX.isActivated=true
3.1.2 前端页面
通过JSP、Vue、React等技术开发的前端,可通过页面标签的可见性来控制该功能是否可用
<h1 v-if="featureXIsActivated">Vue is awesome!</h1>
3.1.2 后端功能
- 在程序的入口控制功能的调用,如controller中,判断配置项的值来控制返回值。
......
protected ModelAndView handle(HttpServletRequest request, HttpServletResponse
response, Object command, BindException bindingResult) throws Exception {
if(!applicationConfig.getMessageAsBoolean("featureXIsActivated")) {
return new ModelAndView("404.jsp");
}
//normal logic
}
......
- 在程序逻辑中控制某一新功能
3.1.3 数据库
- 重点设计数据库的兼容性
3.2 功能开关的好处
- 修改配置即完成了功能发布
- 必要时,可以控制功能降级
- 紧急情况下,可快速限制某一功能的访问
3.3 功能开关注意事项
- 功能独立控制,不相互依赖
4. 总结
功能开关能够减少分支合并,但功能开关也会增加管理的成本。对于已经明确上线的功能,后续要去除配置项及判断语句,以减少维护开关的成本。