Feature Toggle 实践总结

上官瑾文 2019-11-24

url TDD 敏捷 Trunk-Based-Development GitFlow FeatureToggle

Git Flow 模型

Git Flow

很多团队的开发大都是这种 Git Flow 模型,在这稍作解释,具体实践可以参考这些地址 https://yq.aliyun.com/articles/68655https://datasift.github.io/gitflow/IntroducingGitFlow.html

  • 主要分支

    • master 分支上的代码随时可以部署到生产环境
    • develop 作为每日构建的集成分支,到达稳定转台时可以发布并 merge 回 master
  • 支持性分支

    • feature 分支每个新特性都在独立的 feature 分支上进行开发,并在开发结束后 merge 到 develop
    • release 分支为每次发布准备的 release candidate, 在这个分支上只进行 bug fix, 并在完成后 merge 回 master 和 develop
    • hotfix 分支用户快速修复,在修复完成后 merge 回 master 和 develop

那么,这么做有什么坏处呢?

  • 规则太复杂,只要规则复杂,人就一定会在规则上出问题
  • 在开发过程中,因代码隔离,只在特定的分支上有有效的反馈,但是在全量代码上并没有有效的反馈
  • ...

接下来,看看下面的场景

场景

场景一

项目时间紧、功能多,每次上线完要开始新的迭代,如果线上出现 bug, 且 release 分支就是 master 分支,我们不可能将我们新开发的功能推到生产环境。那么怎么保证将线上的bug
修复了,还能将我们的代码隐藏起来,只在特定的情况下我们的新功能才能使用呢?

场景二

我们为客户开发软件,软件中有个修改用户信息的功能,作为客户 TA 不想让一般人修改信息,但总有那么些人要修改,作为开发我们就需要设置一个功能开关,
在特定条件下这个修改用户信息的功能才能被看到。

场景N

...

根据上面的描述,项目组肯定用的不是 Git Flow 模型,那么我们先来了解一下 Trunk-Based Development。然后再看看什么是 Feature Toggle, Feature Toggle 能否解决上面描述的场景呢?

Trunk-Based Development

项目组业务是基于主干开发的(Trunk-Based Development), 意思就是所有项目组成员在一个分支 master 上进行开发,同时;利用 CI/CD 确保 master 上的代码
随时都是生产可用的,发布时,从 master 上检出 release 分支进行发布。

Feature Toggle

The basic idea is to have a configuration file that defines a bunch of toggles for various features you have pending. The running application then uses these toggles in order to decide whether or not to show the new feature.
-- Martin Fowler

简言之,Feature Toggle 就是通过在不更改或者修改少量代码的情况下修改系统行为:

  • 控制功能特性发布
  • 权限策略
  • 测试策略
  • 控制突发事件
  • ...

举个

在已有网页的 url 中加入特定的标识,刷新浏览器,在页面的特定部分出现某一个功能。

如下,在 url 中加入 ?switch=1, 刷新页面就会出现下图中的更改账户类型

之前

before

之后

after

优点

以下优点是基于主干开发的 Feature Toggle 的优点

  • 因基于主干的开发,避免了分支合并代码冲突的问题
  • 每次提交都在主干,迭代速度明显有优势
  • 新功能的整个过程都持续集成
  • 对生产环境基本没有影响
  • ...

缺点

  • 未完成的功能可能会部署到线上,如果配置有误可能将未完成的功能开启,对公司早上损失
  • 主干上担心提交代码影响其他功能,影响开发进度

实践

需求

在项目的聊天功能中,我们采用的是 Trunk-Based Development, 现在有个常用话术 的功能,但是这个功能在下个迭代上线,同时我在这个迭代已经将下次上线的功能做完了[那是‍️可能的],现在有时间可以做常用话术这个功能了,为了做完可以让 QA 测试,又不能影响已有的功能,那么我就需要 Feature Toggle 这一神器了。

Coding

测试

// featureToggle.test.js
import { featureToggle } from './featureToggle'

describe('Test for featureToggle', () => {
  afterEach(() => {
    Object.defineProperty(window.location, 'href', {
      writable: true,
      value: '',
    })
  })

  it('test should run', () => {
    const universal = 42
    expect(universal).toBe(42)
  })

  const testCase = [
    { url: 'http://localhost:8008/#/', expect: false, result: featureToggle('test'), },
    { url: 'http://localhost:8008/#/test?', expect: false, result: featureToggle('test'), },
    { url: 'http://localhost:8008/#/test?features=&', expect: false, result: featureToggle('test'), },
    { url: 'http://localhost:8008/#/test?features=test&', expect: true, result: featureToggle('test'), },
    { url: 'http://localhost:8008/#/test?features=test,123', expect: true, result: featureToggle('test'), },
    { url: 'http://localhost:8008/#/test?features=test,123', expect: true, result: featureToggle('123'), },
  ]
  testCase.forEach((item => {
    Object.defineProperty(window.location, 'href', {
      writable: true,
      value: item.url,
    })
    it(`use ${item.url} should return ${item.expect}`, () => {
      expect(item.expect).toBe(item.result)
    })
  }))
})

实现

// faetureToggle.js
export const featureToggle = (feature) => {
  const featureString = window.location.href.match(/.*features=(.*)/)
  if (!featureString) {
    return false
  }
  const features = featureString[1].split(',')
  const result = features.includes(feature)
  return result
}

效果

因项目是内部项目不能截图,效果和上面举个是一样的

参考

登录 后评论
下一篇
云栖号
7941人浏览
2020-03-04
相关推荐
常用的CSS框架
838人浏览
2018-03-04 10:34:00
从技术雷达看持续交付
2555人浏览
2016-06-15 17:18:37
Eclipse 3.3 M1 新特性一览
848人浏览
2017-12-26 15:29:00
0
0
0
514