敏捷实践系列(八):单元测试及最佳实践

前言

在工作中或者在面试中,我经常碰到的开发人员就是对单元测试不重视,这一类基本上都表现出了一种“无知的自信”,总觉得自己写的代码质量很高,直到一次次虫子(Bug)把自己咬的头破血流时,才发现原来自己的代码已经到了剪不断理还乱的状态,而每一次修改一个bug,都需要走一遍“墨镜迷宫” (看上图)。还有很多人知道单元测试或者写出了单元测试,但是就是写了一个方法,上面标注了一个[Test]属性而已,甚至很多的人单元测试上面标注的是[IgnoreTest], 每次看见这些,我都深深的感到推行单元测试之路是艰难的,是遥远的,但是我依然坚信是是渴望也可及的,只要有着深深的信念,坚强的意志,无谓的勇气,一头扎进去泥巴堆里,假以时日,当大雨来临,必将带走泥巴,从此你拔剑扬眉,哦,你不用拔剑了,因为你就是剑。。。

为了让更多人能够拔剑扬眉,也为了我们公司刚入职的新人做一些培训,我精心准备了单元测试的一些知识,在此为你奉上,我尽量用简短的语句来描述,如果你不清楚我说的某一些点,那么欢迎你发邮件给我 wangdeshui@outlook.com,我可以针对集中的点发篇文章,如果你想知道我说的所有点怎么实践,那就联系我,试试加入到我们公司来。

什么是单元测试

单元测试是开发者编写的一小段代码,用于检验被测代码中的一个很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。

执行单元测试,是为了证明某段代码的行为确实和开发者所期望的一致。因此,我们所要测试的是规模很小的、非常独立的功能片段。通过对所有单独部分的行为建立起信心。然后,才能开始测试整个系统。

为什么要使用单元测试

  • 单元测试使工作完成的更轻松
  • 单元测试使你的设计更好
  • 大大减少花在调试上的时间
  • 能帮助你更好的理解代码

没有单元测试

  • 任何代码都是在假定其他代码是正确无误的情况下编写的。
  • 修改一处代码时无法得知会对其他代码产生怎样的影响。
  • 任何一处改动都需要进行功能级别的整体调试。

单元测试难以推动的原因

太花时间

很多人认为单元测试很花时间,但是想想我们在下面几点话的时间,我经常看到为了测试一个简单的API方法,我们很多人必须让前端跑起来,甚至自己写一个客户端才能调用

  • 调试上花的时间
  • 对自认为正确的代码,花了多少时间确认代码是正确的。
  • 定位Bug所耗的时间

测试不是我的工作

很多人认为测试不是自己的工作,但是想一想每次测试提出一个bug所花的时间,以及你改bug所化的时间,所以下面2点是很重要

  • 内在质量的重要性
  • 测试应该是辅助,好的软件是开发设计出来的,不是测试出来的

系统可测试性差

  • 系统耦合度很高,我们需要提高我们的团队的设计能力。

单元测试最佳实践

实践一: 三到五步

  • SetUp
  • 输入
  • 调用
  • 输出
  • TearDown

实践二: 运行快速

###为什么?
单元测试运行很频繁,是辅助开发的,在开发过程中运行,如果慢影响很大

多快较好?

  • 单个测试小于200ms
  • 单个测试套件小于10s
  • 整个测试小于10分钟

实践三:一致性

任何时候同样的输入需要同样的结果

    Date date=new Date()
    Random.next()

这样的代码都需要Mock掉,不然时间每次都不同,结果就会不一样。

实践四:原子性

** 所有的测试只有两种结果:成功和失败**
不能部分测试通过

实践五:单一职责

一个测试只验证一个行为

** 测试行为,不要测试方法 **

  • 一个方法,多个行为    —–>  多个测试
  • 一个行为,多个方法   —–   一个测试
    这里的一个行为,多个方法一般指这个方法调用private, protected, getters, setters
  • 多个Assert只有在测试同一个行为时可以接受

实践六:独立无耦合

单元测试之间无相互调用

  • 单元测试执行顺序无关
  • 不同的顺序无影响

单元测试之间不能共享状态

比如一个测试里设置了一个属性值,然后在另外一个测试里用,如果必须共享可以放到Setup里

实践七:隔离外部调用

  • 单元测试需要快速运行,且每次结果一致,所以需要隔离一切对外部的调用。
  • 不使用具体的其它真实类,就是不要new
  • 不读数据库
  • 不读网络
  • 不读外部文件
  • 适当时候可以构造一个相同的内部文件来Mock
  • 不依赖本地时间
  • 不依赖环境变量

实践八: 自描述

  • 单元测试是开发级文档
  • 单元测试是方法的描述

实践九: 单元测试逻辑

  • 单元测试必须容易读和理解的
  • 变量名,方法名,类名
  • 无条件语句,无Switch
    办法:分解if到多个测试,所有的输入都是已知的,所有的结果都是一定的(Mock)
  • 无循环语句
  • 无异常捕捉
    ** 测试预知的异常,用ExpectedException方法 **

实践十: 断言

  • 断言信息最好包含Business Information

  • 断言信息包含出错的具体信息如果失败

  • 适当时候可以封装自己的Assert
    比如:

    Assert.IsProgrammer(Jack)
    Return Jack. Cancooking() && Jack.CanCoding()

实践十一:产品代码

  • 产品代码无测试逻辑

不能有:

    If(global.IsTest){…}
  • 测试代码和产品代码要分离
  • 不要在产品代码里有任何只供测试用的代码
  • 使用依赖注入

最后,单元测试常用技术及工具

下面是.NET程序常用的单元测试需要的技术和工具,其它语言请自信比对。

  • 面向接口编程
  • 依赖注入(Castle, Unity, Ninject)
  • Moq
  • 测试工具(xUnit) 
  • .Net Nunit
  • 代码覆盖率测试工具Ncover
  • 自动运行测试辅助工具NCrunch
  

敏捷实践系列(七):为什么你都听客户的,客户却不满意

开篇

这样的场景你是不是很熟悉?客户让你做一个软件,你需要他给你写出需求,当它给你写出需求后,在你认为时间非常紧的情况下,你辛辛苦苦,加班加点,费劲九牛二虎之力,最后赶在最后时刻给客户提交了,你满怀希望等待客户给你的表扬,你万分坚信领导对你的辛苦会给予高度认可和鼓励,你觉得很快就要带一朵“小红花”时,最后你得到的是绵绵无绝期的等待,甚至是客户的不满意,这是为什么呢?这种情况在我的团队里也会出现,有时候我让改一个东西,经常得到的回复就是:”客户就是这么要求的,而且描述很清晰,不能改!”, 最后如果不改的结果就是客户不满意。

为什么我们听客户的,客户却不满意?

客户如果说要一个 “杯子”, 我们是怎么做的?

我们的很多程序员一般是这么工作的,客户说我需要一个 “杯子”,然后程序员就在网上或者生活中搜索杯子,于是程序员就得到了很多杯子,现在各种各样的茶杯,各种各样的酒杯等等,这时候程序员就按自己的喜好选中了一个杯子,聪明一点的程序员可能会衡量一下制造不同杯子的时间成本,选定以后就开始进行模仿,最后你没日没夜,加班加点终于把杯子做出来了,客户会说:怎么会做一个这样的杯子要这么久时间? 因为市场上已经有人做出来过,所以客户就会觉得照抄应该很快,其次客户会觉得这个杯子不是他想要的,如果是抄一个别人一样的杯子,那为什么不直接买一个呢?

客户如果说要一个”杯子”, 他要的不是一个”杯子”

实际上,客户说要一个“杯子”, 他要的不是一个杯子,他要的不是用来装水的,这个就像我们对一个女孩说:“我想和你一起看明早的太阳”,我们都知道他要的不是白天而是夜晚,不是吗?

所以,当客户说要一个“杯子”时,他要的不是一个“杯子”,因为“杯子”只是一个名词,显然客户要的不是一个名词,而是一个”动词”, 所以我们要想到的是这个杯子用来干什么?我们脑子里要想是不是几个老人在喝茶?一对情侣在喝咖啡?一个婴儿要用来喝奶?还是单身狗要用这个“杯子”?

可见,当客户要一个“杯子”时,他要的是一种场景,一个动态的场景,一个有人使用的场景,这个场景就是用户体验,你想想老人喝茶的杯子和单身狗要用的杯子能一样吗?

要自信的对客户说 “NO”

这个还是那句话,就是“为什么你都听客户的, 客户却不满意?” 我举个简单的例子,你生病了,你到医院去看病,你告诉医生怎么给你治疗,有的人甚至是在网上查了查资料,或者曾经有别的医生给这个病开个药方,然后你给医生说你要吃什么药,还有的人拒绝血常规检查,最后他的病要么没好,要么拖的时间很长,最后你还对医生非常不满意。你想想你的客户给你提需求的时候,是不是有的时候就和这个病人一样?

为什么医生会对你说 “NO” ,甚至不管你了呢? 因为医生比你更知道怎么治疗,如果按你说的肯定是不行,那么回到程序开发,软件设计上,做一个程序员或者高级软件开发人员,为什么不能给客户说 “NO” 呢?因为我们比客户更知道怎么开发,我们比客户对软件更专业不是吗?

协助客户改进需求

当然,我们不能简单粗暴的对客户说 “NO”, 由于我们对软件更了解或者说更专业,我们就可以给客户一些更好的方案,由于客户对技术的不了解,经常会提出一些既耗时又非常“愚蠢”的方案,那我们需要利用我们的专业知识来告诉客户什么是更好更省时的方案,有的时候需求稍加改动会让客户体验更好,而且开发更快,我相信这样的场景在不同的项目里有很多。

最后

所以,当我们拿到客户需求的时候,我们不是卷起袖子就开始编码,而是第一步想想客户要的到底是什么?他说的真的是他想要的吗? 他想要的说了吗?我们需要知道客户的项目的远景是什么?我们需要站在整体的角度,站在更 High level的角度来看需求。

举个例子,我曾经做过的一个项目,客户说要做房间预订,还要分淡旺季,还要有Offer, 还有内部员工不同价,团队当时想的很复杂,想到了想艺龙那样的,但是当我去了客户那边了解后,他们只有5个公寓而已,而且只是给集团员工别的地方来的提供住宿从而计入别的分公司成本而已,我觉得hard code出来一个版本都够用好几年,而且这样的话,原来的估计至少得半年以上吧,当你清楚客户最终需要啥的时候最多两个礼拜就弄完了。

总之,听客户没错,但是无脑的听客户的,完全听客户的就有问题了,就像如果你完全听你媳妇的,然后她错了后,她会说:“我让你那样做,你就那样做吗?你自己脑子是干什么的!”

  

敏捷实践系列(六):Team Leader 你不再只是编码, 来炖一锅石头汤吧

为什么软件项目需要 Team Leader

多年以前,当我接触敏捷时,我接触到一个概念,叫做 “自组织的团队”,当时我看了一些估计从没做过敏捷的一些凭空捏造的很多文章(事实上,这类人现在越来越多),那些文章多见名猜意,提出了自组织的团队就是团队自己组织,不需要Leader, 一开始我也是这么认为,甚至想尽一切办法向这个方向靠拢,而且成功的提交了一些项目,甚至于连我自己都相信这是自组织的结果,但是后来一想,我在团队里的所想所做不正是一个Team Leader 需要做的吗?

自组织团队的形成需要一个过程,而且目标只能无限靠近,难以完全达到

就拿一个足球团队来说,就拿 “宇宙队” 巴萨来说,也是需要一个教练,同时也需要组建团队,买卖球员,队伍文化建设,战术打法等等,那么软件项目团队来说,自组织团队同样需要组建,文化建设,代码规范,Team Rule, 团队磨合,质量意识,技术交流,客户需求范围把控,会议组织,冲突解决,开发流程等等。

自我保护,害怕承担责任是大部分人的天性

在没有一个好的敏捷文化的公司,大部分的程序员都不愿意做更多的事情,话句话说,更愿意做明确分配的事情,因为做的事情越少,自然问题越少,那么责任就越少,这是一种本能的自我保护,这也是大部分人难以成长的重要原因,所以必须要一个人来承担更多的责任,当然方法可以是把要做的事情分的更细,更明确,最终每个人做的事情更细,更多,更明确,那么每一个人要承担的责任就更多了。

###被领导惯了

很多中国的孩子,尤其是很多现在正处于黄金时代的程序员,独立意识确实要差一些,从小被父母装在一个大 “笼子”里,比如去哪里都是大人在前面牵着后面的小孩,老师严格教条的作业却只有一个标准答案,甚至在我看了写错一个字要重写一百遍一样猪一样的惩罚还至今流传着,忘了教育的本质是要把字学会而不是把字写一百遍,等等类似的东西,使我们不敢去思考,习惯被别人领导。

比如,我爱爬山,和我一起爬山的大部分人去一个没有去过的地方,都喜欢走在我的背后,因为他对未知有恐惧!习惯别人牵着走。

这个问题,我以前以为对高智商的程序员来说应该很少,后来经过10几年,我发现这和其它行业的人一样多。

所以,如果没有Leader, 大家不知道怎么干! 没有Leader组织,大家不知道干什么?

软件团队Team Leader的诞生

由于上面的原因,我们需要一个Team Leader, 但是由于太多人习惯被领导,害怕承担更多责任,我们就急需要一个Team Leader, 但是一个好的Team Leader是非常难找的,因为一个好的Team Leader要做很多事情才能把一个Team变成好的Team.

不懂技术的Team Leader在软件项目里成功的概率很小

由于软件项目来说,一个不懂技术的人可以当一个Team Leader, 但是要想当一个的Team Leader是难于登天,因为如果你不懂技术,那问题太多了,你怎么知道大家的评估时间靠谱?你如何向客户展示你的方案,你的优势?由于文人相亲,有技术的人一般会鄙视不懂技术的人瞎指挥,从技术人员喜欢鄙视技术人员这点就不难想象的出来。

请不要举马云的例子,你啥时间看到马云去直接领导一个技术团队了.

不懂管理的Team Leader 也难以成为优秀的Team Leader

因为Team Leader难找,所以在中国又一个常见的事情不断上演,那就是 “学而优则仕”,同样这个在我的团队里也大量存在,这是没有办法的事情, 因为如果他不懂管理,他还至少是一个程序员,相比不懂技术的管理人来说,如果他管理做不好,那对公司就没有什么价值了。

学而优则仕

在软件团队里,我们都知道一个Leader懂技术是多么的重要,那么我们唯一的选择就是沿用了中国多年的传统,那就是“学而优则仕”,比如上小学时,老师不都是让学霸的当班长吗? 所以很多技术还不错的人,都在团队需要的时候 “被挺身而出”,“被临危受命” 成为了Team Leader,但是这样出来的Team Leader 由于由于没有太多的管理项目的知识,没有团队管理的经验,往往也有不少问题,请继续往下看!

Team Leader 你不再只是编码,请炖一锅石头汤

由于“学而优则仕”,导致大部分软件团队Team Leader更多的专注于技术,就自然把更多的时间花在编码上,因为编码是立即可以看到的产出,而忽略了一个Team Leader要做的更重要的事情,比如团队文化建设,项目过程,质量保证,进度跟踪等等的事情。很多时候,我们缺少这些依然把项目做完了,但是实在很多加班,甚至是Team Leader卷起袖子一个定俩的情况下干完了,这样大部分情况就是客户感觉还OK, 但是难以达到满意。这还是自我保护的意识,害怕客户看不到自己实际的编码产出,实际上忽略了团队整体的目标的重要性。

关于技术团队Team Leader应该做什么,我本文就不想讲太多,有时间我会再写几篇关于Team Leader的文章,但是本文我强调的是技术团队的Team Leader不能只是编码,他要意识团队管理的重要性,哪怕这个“重要性”在别人看来什么都没有,不用害怕,因为我们只需要最终的项目成功来证明。

三言两语难以让大家明白Team Leader应该做什么,我就用一则寓言故事来告诉Team Leader 请炖一锅石头汤。

很多年前,有三个士兵,他们从战场回来既饥饿又疲倦,这时他们来到了一个小村庄。然而由于粮食遭遇欠收和连年的战争,村民们迅速的将它们的一小点粮食藏了起来,并在村子的广场中接待了士兵们,搓着双手,哀叹着他们是多么缺少食物。

士兵们平静地与村民们交谈着,第一个士兵对村庄的长老说道:“既然你们的土地收成不好,不能分给我们点吃的,那么我们将会与你们分享我们所有的:如何用石头做一道好汤的秘密。”

自然啦,村民们都十分好奇,很快他们就升起了火,架起了城里最大的一口锅,士兵们将三颗光滑的石子丢到了锅里。“这将是一锅好汤”第二个士兵说;“不过如果有一撮盐和一些欧芹那就更棒啦!”一个村民跳了起来,喊道“多幸运啊!我刚刚想起来家里还剩下些呢!”于是她跑回家,带着满满一围裙的欧芹和一根萝卜回来了。随着锅里的水渐渐煮沸,村民们的记忆力也变的越来越好,很快地,大麦,胡萝卜,牛肉还有奶油,统统被投入了这个大罐子里。

他们吃啊跳啊唱啊~直到深夜,美妙的宴会和新结交的朋友让每个人都感到焕然一新。当早上三个士兵醒来时,他们发现所有村民正站在他们面前。在他们脚边放着有一包这个村子最好的面包和奶酪。“你们把最好的礼物送给了我们:如何从石头里做汤的秘密”,一位长老说道,“这一点我们永远也不会忘记。”第三个士兵转身冲大伙说到:“这并没有什么秘密,但是有一件事是确定的:只有一起分享,我们才可能举办一次宴会。”说完,他们又踏上了路,慢慢走去了。

上面这个故事,我希望Team Leader能够明白虽然士兵并没有什么,但是他却让大家把好东西都拿出来一起做了一锅好烫,村民就是你的团队成员,一开始都把好东西藏起来不是吗?你需要做的就是拿出你的“秘密配方” 和大家一起炖上一个鲜美的石头汤吧!

  

敏捷实践系列(五):迁移已有项目到Git flow

上一篇 敏捷实践系列(四):代码管理流程 给大家详细的讲解了一下Git flow, 本周我们就把一个客户的所有项目实施了Git flow, 如果说你详细看了那篇文章,那么你就能了解为何要进行Git flow 以及如何对一个新项目按Git flow的流程操作, 但是当我们整理已有的项目时,我们发现了以下几个问题需要一些方法处理。

错乱的分支名如何解决

问题

我们遇到的一个项目,一开始生产环境用的是master分支,突然客户提出他们有一个客户有特殊需求,让打一个分支出来,团队成员就打出了一个master-quickfix 分支,但是后来这个分支上功能越来越多,慢慢的团队成员就把这个分支当做master分支用了,此时develop和 master-quickfix 分支也早已分道扬镳。然后所有的客户又需要相同的功能,这样如果想合并会master将是非常难的,因为冲突太大。 经过沟通和核实,我们发现现在的master-quickfix其实承担了master的角色,所以我们要让master-quickfix当做master.

方案

方案一 用master-quickfix 代码覆盖master

git checkout master-quickfix

merge master - ignoring master's changes
git merge -s ours master

git checkout master

# finally merge all our stuff to new master - actually it replaces the master with master-quickfix
git merge master-quickfix

# now master-quickfix can be deleted
git push origin: master-quickfix

然后我们就看到master上代码已经和原来master-quickfix上一样了,但是这个方法有一个问题就是 master-quickfix上的提交日志没有了,如果你不在乎提交日志那么就可以使用这个方法。

方案二 改分支名

git branch -m master new_master         # Rename branch locally    
git push origin :master                 # Delete the old branch    
git push --set-upstream origin new_master   # Push the new branch, set local branch to track the new remote


git branch -m master-quickfix master         # Rename branch locally    
git push origin : master-quickfix                 # Delete the old branch    
git push --set-upstream origin master   # Push the new branch, set local branch to track the new remote

使用这个方法,就可以看到master-quickfix上的分支的提交记录,过程当中会删除旧的远程master分支,以及master-quickfix分支
,所以需要你确认要删除的分支有备份及没有问题,危险系数5星, 我在做这个操作的时候向别人确认了多次。我操作的时候手都抖呀!

不同的客户不同的分支问题

问题

原来有一个项目,项目有不同的客户,导致不同的客户的代码在不同的分支上,这导致代码难以管理,功能散乱在各个分支,时间久了,开发人员都不知道每个分支上有哪些功能。

解决方案

针对这个问题,我们的想法是不同客户使用同一套代码库,还是使用Git flow, 因为不同的客户需要不同的功能,这其实就是一个SaaS系统,所以我们把系统改为SaaS系统,代码用同一套,不同的客户使用什么功能使用SaaS管理端来配置。 这样做有很多好处,比如:给A客户做的功能很容易再卖给B客户,同时很容易给不同的客户做个性化的配置,例如界面样式等等。 当然,这个需要你的强大的沟通能力,因为改为SaaS需要时间哪,时间就是金钱嘛!

如果你的客户不同意,或者你需要项目遵守一些”特殊规则或者某个国家特殊政策”,导致整套系统非常不同, 那么我的建议是你重新建一个代码库给这个特殊客户,而不要用分支来区分。

总结

由于我们团队的敏捷程度还可以,所以推行Git flow的流程除了以上几个技术上的问题以外,其它的都很顺利。 如果你想实行Git flow,那么再次提醒务必完全理解这个流程再开始。请阅读:敏捷实践系列(四):代码管理流程

  

敏捷实践系列(四):代码管理流程

本来没有这么快到流程以及技术部分,但是因为公司需要,所以就临时写了这一部分。

我们已经从SVN 切换到Git很多年了,现在几乎所有的项目都在使用Github管理。对与那些还在坚持使用SVN的,我实在想不出原因,权且称作守旧派吧。

Git的优点

Git的优点很多,但是这里只列出我认为非常突出的几点。

  1. 由于是分布式,所有本地库包含了远程库的所有内容。
  2. 优秀的分支模型,打分支以及合并分支,机器方便。
  3. 快速,在这个时间就是金钱的时代,Git由于代码都在本地,打分支和合并分支机器快速,使用个SVN的能深刻体会到这种优势。

感兴趣的,可以去看一下Git本身的设计,内在的架构体现了很多的优势,不愧是出资天才程序员Linus (Linux之父) 之手

版本管理的挑战

虽然有这么优秀的版本管理工具,但是我们面对版本管理的时候,依然有非常大得挑战,我们都知道大家工作在同一个仓库上,那么彼此的代码协作必然带来很多问题和挑战,如下:

  1. 如何开始一个Feature的开发,而不影响别的Feature?
  2. 由于很容易创建新分支,分支多了如何管理,时间久了,如何知道每个分支是干什么的?
  3. 哪些分支已经合并回了主干?
  4. 如何进行Release的管理?开始一个Release的时候如何冻结Feature, 如何在Prepare Release的时候,开发人员可以继续开发新的功能?
  5. 线上代码出Bug了,如何快速修复?而且修复的代码要包含到开发人员的分支以及下一个Release?

大部分开发人员现在使用Git就只是用三个甚至两个分支,一个是Master, 一个是Develop, 还有一个是基于Develop打得各种分支。这个在小项目规模的时候还勉强可以支撑,因为很多人做项目就只有一个Release, 但是人员一多,而且项目周期一长就会出现各种问题。

Git Flow

就像代码需要代码规范一样,代码管理同样需要一个清晰的流程和规范

Vincent Driessen 同学为了解决这个问题提出了 A Successful Git Branching Model

下面是Git Flow的流程图

上面的图你理解不了? 没关系,这不是你的错,我觉得这张图本身有点问题,这张图应该左转90度,大家应该就很用以理解了。

Git Flow常用的分支

  • Production 分支

也就是我们经常使用的Master分支,这个分支最近发布到生产环境的代码,最近发布的Release, 这个分支只能从其他分支合并,不能在这个分支直接修改

  • Develop 分支

这个分支是我们是我们的主开发分支,包含所有要发布到下一个Release的代码,这个主要合并与其他分支,比如Feature分支

  • Feature 分支

这个分支主要是用来开发一个新的功能,一旦开发完成,我们合并回Develop分支进入下一个Release

  • Release分支

当你需要一个发布一个新Release的时候,我们基于Develop分支创建一个Release分支,完成Release后,我们合并到Master和Develop分支

  • Hotfix分支

当我们在Production发现新的Bug时候,我们需要创建一个Hotfix, 完成Hotfix后,我们合并回Master和Develop分支,所以Hotfix的改动会进入下一个Release

Git Flow如何工作

初始分支

所有在Master分支上的Commit应该Tag

Feature 分支

分支名 feature/*

Feature分支做完后,必须合并回Develop分支, 合并完分支后一般会删点这个Feature分支,但是我们也可以保留

Release分支

分支名 release/*

Release分支基于Develop分支创建,打完Release分之后,我们可以在这个Release分支上测试,修改Bug等。同时,其它开发人员可以基于开发新的Feature (记住:一旦打了Release分支之后不要从Develop分支上合并新的改动到Release分支)

发布Release分支时,合并Release到Master和Develop, 同时在Master分支上打个Tag记住Release版本号,然后可以删除Release分支了。

维护分支 Hotfix

分支名 hotfix/*

hotfix分支基于Master分支创建,开发完后需要合并回Master和Develop分支,同时在Master上打一个tag

Git Flow代码示例

a. 创建develop分支

git branch develop
git push -u origin develop

b. 开始新Feature开发

git checkout -b some-feature develop

# Optionally, push branch to origin:
git push -u origin some-feature



# 做一些改动


git status
git add some-file
git commit

c. 完成Feature

git pull origin develop
git checkout develop
git merge --no-ff some-feature
git push origin develop

git branch -d some-feature

# If you pushed branch to origin:
git push origin --delete some-feature

d. 开始Relase

git checkout -b release-0.1.0 develop

# Optional: Bump version number, commit
# Prepare release, commit

e. 完成Release

git checkout master
git merge --no-ff release-0.1.0
git push

git checkout develop
git merge --no-ff release-0.1.0
git push

git branch -d release-0.1.0

# If you pushed branch to origin:
git push origin --delete release-0.1.0




git tag -a v0.1.0 master
git push --tags

f. 开始Hotfix

git checkout -b hotfix-0.1.1 master

g. 完成Hotfix

git checkout master
git merge --no-ff hotfix-0.1.1
git push




git checkout develop
git merge --no-ff hotfix-0.1.1
git push

git branch -d hotfix-0.1.1

git tag -a v0.1.1 master
git push --tags

Git flow工具

实际上,当你理解了上面的流程后,你完全不用使用工具,但是实际上我们大部分人很多命令就是记不住呀,流程就是记不住呀,肿么办呢?

总有聪明的人创造好的工具给大家用, 那就是Git flow script.

安装

  • OS X

brew install git-flow

  • Linux

apt-get install git-flow

  • Windows

wget -q -O - –no-check-certificate https://github.com/nvie/gitflow/raw/develop/contrib/gitflow-installer.sh | bash

使用

  • 初始化: git flow init

  • 开始新Feature: git flow feature start MYFEATURE

  • Publish一个Feature(也就是push到远程): git flow feature publish MYFEATURE

  • 获取Publish的Feature: git flow feature pull origin MYFEATURE

  • 完成一个Feature: git flow feature finish MYFEATURE

  • 开始一个Release: git flow release start RELEASE [BASE]

  • Publish一个Release: git flow release publish RELEASE

  • 发布Release: git flow release finish RELEASE
    别忘了git push –tags

  • 开始一个Hotfix: git flow hotfix start VERSION [BASENAME]

  • 发布一个Hotfix: git flow hotfix finish VERSION

Git Flow GUI

上面讲了这么多,我知道还有人记不住,那么又有人做出了GUI 工具,你只需要点击下一步就行,工具帮你干这些事!!!

SourceTree

当你用Git-flow初始化后,基本上你只需要点击git flow菜单选择start feature, release或者hotfix, 做完后再次选择git flow菜单,点击Done Action. 我勒个去,我实在想不到还有比这更简单的了。

目前SourceTree支持Mac, Windows, Linux.

这么好的工具请问多少钱呢? 免费!!!!

Git flow for visual studio

广大VS的福音
GitFlow for Visual Studio

  

敏捷实践系列(三):沟通

大话西游里有一段因为没有沟通的经典, 结局如何大家都知道。

唐僧:你想要啊?悟空,你要是想要的话你就说话嘛,你不说我怎么知道你想要呢,虽然你很有诚意地看着我,可是你还是要跟我说你想要的。你真的想要吗?那你就拿去吧!你不是真的想要吧?难道你真的想要吗?……

悟空:…

敏捷项目沟通尤其重要

敏捷开发多采用短迭代,要求尽快交付可以工作的软件,所以项目的需求文档就很难像原来的瀑布开发模式一样面面俱到。这就要求我们多进行沟通以确保双方的理解一致。根据以往很多项目的经验来看,失败的项目很多都是缺乏沟通,成功的项目必然是沟通做的比较好的。

敏捷经常出现的沟通问题

你以为你以为的就是你以为的

有时候,我们很多人做项目,拿到项目的需求以后,自己简单看了看,然后就开始做了,等到我们做完一个迭代以后,拿给客户看时,完全不是客户要的东西,因为我们对项目的需求的理解比较片面,就是自以为是。

不敢沟通

还有的人,尤其是新入团队的人,客户写的东西或者说的东西,自己没有完全理解,然后又不问,他总觉客户已经说过了,已经写过了,然后再去问客户,是不是不妥?如果按自己的理解或者猜测万一对了呢?, 然后就碰运气。 事实上我们的猜测都常常都是错误的,尤其是在我们对项目没有更全局的了解的情况下。

拖延

还有一类沟通的问题,就是我们自己也发现了问题,需求上的,或者客户的流程上的,但是我们总是心想等到明天沟通,明天再等明天,其实是希望这个问题自动消失,但最终的结果往往越来越严重。

不敢沟通客户的问题

在我的一些项目里,比如我们团队觉得客户那边需要提高,比如需求都很碎片化,客户的UI改来改去,希望客户能提供一个比较接近Final Design的设计,但是总觉得说出客户的问题不好意思,或者对客户的不尊重,实际上我们提出问题和自己的想法才是对客户最大的尊重。

沟通方法单一

我们很多人习惯了一种沟通方式,不管什么问题都是同一种,所有的问题都写邮件或者所有的问题都用语音,这也是很没沟通效率的方式。

敏捷项目如何沟通

平等沟通的意识

首先,我们要理解,敏捷项目里的沟通,不管是与客户还是与团队成员,大家都是平等的,沟通项目相关的问题都是平等的,尤其是把客户也当做自己团队的一部分,客户也是希望项目做好的那一个人, 只有这样,我们才能畅所欲言,无所顾忌,更不会有想法掖着藏着。

多种沟通方式结合

小平说: “白猫黑猫,抓住老鼠就是好猫”,沟通也一样,下面是常见的几种沟通方式。

  • 面对面
  • 视频
  • 语音
  • IM 文字沟通
  • 邮件沟通

这几种沟通方式的效率一般情况下由高到低,所以我们尽量选择沟通效率比较高的方式, 因为当我们能看见对方的时候,我们可以根据对方的表情就可以看出来对方是否真的理解了自己所说的。

但是实际当中我们需要多种沟通方式的有效结合,比如离岸团队面对面的机会比较少,我们可以视频和语音,但是视频语音由于他强调及时性,需要大家的时间都合适,但是视频语音也有缺点,很难有文字记录,虽然可以录音,但是视频和语音很难搜索内容,也较难在组内传播。 那么文字沟通有时候就体现出了很多的优势,有历史记录,多人可以参与等。

但是IM得文字沟通也有一些不足,比如很难通过图形沟通,对图形标注等,除此之外,很多时候很难及时给出答案等,那么邮件的优势就是不需要客户立即回复,也给客户更多的时间思考,同时格式化的文档更容易把一件事情描述清楚。

所以,我们在沟通的时候,要结合情况选择最适合,最快速能够把问题沟通清楚的方式。

一图胜千言

软件项目很多时候,图形化的沟通非常重要,我们很多项目的设计,以及架构,开发人员难以达到统一的认识,就是缺乏图形,设计图或者架构图。我们都知道,人对图形的记忆力要好于文字。

系统之间的交互,软件如何部署,项目的架构封层等,这些用图形来表示往往一页纸就可以,而且大家看后,很容易理解,很容易记住。因为只有别人对你的想法理解了,别人才能够发现你的问题,才能给出建议。我觉得软件项目,非常重要的两张图,一个是 Architecture Diagram, 一个是Use Case Diagram, 这两张图可以大大节省项目的开发沟通时间。

原型或者草图

敏捷项目流行边做边改,但是我对这个是不太认可的,客户付钱是让我们把事情做对而不是把事情改对。那么怎么样能够在做之前尽量就明白要做什么?那就是先做一个原型,我说的原型是不需要花太多时间的,如果花太多时间就得不偿失了,比如UI我们可以先在开发之前先画出Wireframe, 如果需要交互,我们可以先做交互的Prototype. 现在有很多工具比如Invision都可以建立静态图形之间的交互,这样可以大大减少返工的时间。

提出问题时尽量给出方案

很多人只问问题,所以导致客户回复的很慢,因为客户需要思考,但是有的时候客户思考后给我们的方案又不适合,所以一来二去就浪费了很多时间,那么好的方法是就是,我们给客户提的问题,想想是不是我们没有方案呢? 还是我们有多个方案不能确定是哪一个方案呢?很多时候我们其实有方案的。

我们都知道我们上学的时候都喜欢做判断题,其次是选择题,其次是填空,最不想做的就是问答题,想想我们要想得到客户的快速反馈,我们是不是想想应该给客户出比较容易做的题呢?

其实,凡是我们提的方案被客户采纳的,我们做起来也更顺手,也就更有效率。

总结

最后,沟通最主要的两点:

  1. 如果你想要,就一定要说,不说,别人怎么知道你想要呢? (敢于沟通)
  2. 要想尽一切办法, 让客户觉得:“你以为你以为的就是你以为的” (确认自己的想法)
  

敏捷实践系列(二):敏捷意识

上篇文章我提到了敏捷的心法,但是我想除了心法之外,更重要的是敏捷的意识。

为什么意识更重要?

意识:辩证唯物主义的解释是,意识是物质运动变化的场所,意识是人脑对大脑内外表象的觉察,即它可以辨识自己脑区中的表象是来自于外部感官的还是来自于想像或回忆的。

我们这里说的意识,一是你能主管的去想一件事情,另一个就是你对一件事情的发自内心的认可程度。

敏捷,光有心法是没有用的,有没有要敏捷的意识,敏捷的意愿。有没有主观的愿望去做,我们经常说的话:没有成不成,就是看你想不想做。

欲推敏捷,首先要培养敏捷的意识

我举两个例子:

一个是最近推行**”无车日”**, 无车日本来是想让大家绿色出行,减少二氧化碳的排放,但是结果是什么,结果是堵的一塌糊涂。 为什么?据说原因如下:有车的人想,本来平时不开车,但是今天无车日,路上车少,那我就开车肯定不赌。这样造成的结果是,坐公交的人觉得无车日大家都挤公交,开车的人觉得今天无车日开车应该不赌,所以开车。造成的结果是,公交也堵,开车也堵。这就是政府和民众对环保的意识不够,如果政府意识够了,那么政府是不是多提供公共交通的数量,以及提高公共交通的便捷以及服务?如果民众意识够了,那么我是不是可以走着去上班?我是不是可以骑自行车?我是不是可以跑步?若能如此,无车日才有了真正的意义。

另一个例子,就是我看到的道路旁边的自行车道栅栏,政府甚至花了不少钱来弄这个,但是效果呢? 我看到的情况是很多地方没人用,甚至造成地段的拥堵。为什么? 有些地方自行车道里面有面包车在里面堵着,还有的地方有人逆向骑过来,试想,如果你骑自行车有这样的经历,你还会骑自行车道吗?骑到中间你飞出来? 如果这个自行车道没人用,那么另一个问题又来,本来3车道,现在一个车道停车,一个车道过车,原来没有自行车道时,如果前面的车临时停车,其他的人还能利用空的地方错开车,现在就能硬等,这样岂不是更堵?结果就是,本来就赌的路,硬硬的把路切一块空着浪费了!

从上面的两个例子,我们都能感觉到,都是为了要把事情做好,但是,就是意识层面没做好工作,才导致截然相反的结果。(以上只是我自己片面的感觉,真正的事实数据请看官方数据)。

如何培养敏捷的意识?

首先,我觉得培养敏捷的意识之前,首先要知道是什么事敏捷的意识,我觉得敏捷的意识就是:

  • 高质、高效的做事情
  • 持续的学习
  • 团队精神,Good team player.
  • 责任心
  • 关心客户的价值
  • 解决问题
  • 努力改变现状,不抱怨现状。

其次,那么如何培养敏捷的意识呢?我个人觉得两种方式:

  • 招聘具有敏捷意识的人。 其实我列的敏捷的意识和大家想象的SCRUM那些无关的。

  • 培养已有的人具有上面提到的敏捷的意识,方法比如说是宣传、强调、加薪、惩罚等等各种方法,关键是推行的人自己有没有这个意识来推行敏捷。

最后,最重要的是要让大家知道为什么要敏捷,敏捷解决的问题是什么?敏捷对工程师自己的好处是什么?因为只有人敏捷了,项目才能敏捷,所有的项目都敏捷了,公司才能说是一个敏捷的公司。若如此,强行推行一个SCRUM框架,持续交付等等,就必然走向文章开头的那两个例子,结果只能是适得其反。

愿君更饮一杯酒,敏捷路上无故人。

且行、且思、切记!

(注:佛光,图片拍摄于2015-6-6 太白山)
  

敏捷实践系列(一):什么是敏捷

开篇:

悟空:师傅,为什么你写东西,喜欢写系列呢?
师傅:因为很多东西需要长期的实践呀。
悟空:怎么又开始说敏捷了
师傅:就像一本好书,常读常新,人生不同阶段过的都是不同的人生呀。
悟空:师傅,为什么你原来用上、中、下呢?
师傅:因为原来只写了个上中,别人一直问下,现在如果只写一二,别人要问,我就说写完了呀!
悟空:。。。。。。

敏捷是什么?

其实别人问敏捷是什么?几年前我觉得很好回答,但是现在我觉得很难回答,就像你问天龙八部里的扫地僧:“功夫是什么?” 我觉得他可能真的会被问住的。你再问:功夫是“降龙十八掌”?, 是“九阴真经”?,是“一阳指”?他可能说不是,但是待会儿他可能会说:“也算”。

所以很多人问我什么是敏捷,我其实很难定义,用了SCRUM算吗?用了Kanban算吗?用了Target Process,Trello算吗?我也只能说:“也算,也不算”。

还有人觉得用了持续集成,用了测试驱动开发,用了结对这个应该算是敏捷了吧? 我只能说如果你用了“轩辕剑”,“倚天剑”,“屠龙刀”,”莫问剑”,”游龙剑”,你就是高手吗?君不见,扫地僧一把扫帚就制服两大高手? 由此我们可以看到功夫要好,更重要的是内功,是心法。

敏捷的心法是什么?

这个心法其实就是武林高手总结的敏捷宣言

  • 个体和交互 胜过 过程和工具

  • 可以工作的软件 胜过 面面俱到的文档

  • 客户合作 胜过 合同谈判

  • 响应变化 胜过 遵循计划

这个其实就是:“九阴真经”,“易筋经”,“六脉神剑”,“葵花宝典”(正宗的是不需要自宫的)

知道了心法,其实每个人的修炼方法都不一样,但是前人为了后人节省时间,给出了自己修炼的一些经验和原则,那就是十二条原则。

  1. 最优先的目标是通过尽早地、持续地交付有价值的软件来满足客户

  2. 欢迎需求变化,甚至在开发后期。敏捷过程控制、利用变化帮助客户取得竞争优势

  3. 频繁交付可用的软件,间隔从两周到两个月,偏爱更短的时间尺度

  4. 在整个项目中业务人员和开发人员必须每天在一起工作

  5. 以积极主动的员工为核心建立项目,给予他们所需的环境和支持,信任他们能够完成工作

  6. 在开发团队内外传递信息最有效率和效果的方法是面对面的交流

  7. 可用的软件是进展的主要度量指标

  8. 敏捷过程提倡可持续发展。发起人、开发者和用户应始终保持稳定的步调

  9. 持续关注技术上的精益求精和良好的设计以增强敏捷性

  10. 简化——使必要的工作最小化的艺术——是关键

  11. 最好的架构、需求和设计产生于自我组织的团队

  12. 团队定期地对运作如何更加有效进行反思,并相应地调整、校正自己的行为

注意:上面我说的是这是前人根据自己修炼的过程总结出来的经验,那么也就是说只是经验,100%可以拿来自己用?那就不一定。

比如有一条原则说:情人节一定要给老婆买花?那么问题来了,没老婆怎么办? 当然是先找个老婆。总不能买花送给别人老婆吧。

比如,上面有一条:“在整个项目中业务人员和开发人员必须每天在一起工作”, 很多时候很难,那么怎么办?第一创造条件能让业务和开发人员每天在一起工作,这个一起不一定是坐在一起,可以每天及时回复你的邮件,也可以你自己就当业务人员,那么你自己不就是每天和自己在一起?

怎么样才算敏捷了?

尽管公司实行敏捷也很多年了,这个问题,我想说我不知道怎么回答,因为我们也还在努力变得更敏捷,但是我理想中的敏捷,应该是这样子的:“草在发它的牙,风在摇它的叶,小鸟在唱他的歌,而敏捷团队的Leader静静的座在那里一句话也不用说”,此时敏捷就像空气一样充满了整个房间,知道有一天PM高了,这个时候大家说:“哎呦,最近是不是不敏捷了?”

怎么开始敏捷呢?

重要的事情说三遍:

  • 读书,实践!
  • 读书,实践!
  • 读书,实践!