Friday, February 5, 2016

三件事开启你的敏捷转型之旅

开启敏捷之旅的三个实践:持续集成(Continuous Integration, 以下简称CI),自动化测试,每日Code Review



为什么是这三件事?

抗拒变化,效果不佳,扎根不实,缺乏学习氛围等因素,往往是传统团队在敏捷转型中失败的重要的原因。
比如有些团队采用Scrum,引入迭代,计划会议,站会,回顾等实践,尝试两周上一次线,结果上线后产品质量很差,引来各方投诉,同时下个迭代有一半的时间在修bug,严重影响了产品进度,开发开始叫没时间,产品经理吹促着快点完成功能,于是,各种实践开始延期或取消,项目经理决定延长迭代周期,慢慢地团队回到了几个月上一次线的老路。
还有些团队,站会变成团队成员的汇报会,引入了BA却缺乏与其他成员的沟通,团队成员都不愿意暴露弱点,抗拒新知识等。这些团队的管理者,高估了成员走出舒适区和适应变化的能力,很容易导致团队转型的失败。

团队要能成功转型敏捷,必须要一步一步走,一步一个脚印,让团队每个人都能看到进步,感受到好处,并且能让这种获得的进步能在团队中长期保存,形成文化,让新来的人也能继承这些做事方式。

图:团队能成功变革的三要素


我们来看这三个实践是如何相互配合,做到以小的变化,得到大的效果,最终在团队中形成文化的。
  • CI可以在任何形式的团队中引入,帮助团队成员减少手工重复劳动,提高效率,比如打包和部署,回归测试等。同时CI不需要改变团队现有开发流程,对工作习惯的改变较小,且易于理解,容易被团队接受。
  • 自动化测试配合CI一起实施,能尽早的发现问题,尽早的修复问题,有效的提升产品质量。高质量不仅能帮助产品成功,还能减少团队的计划外工作,给团队带来更好的预估。
  • 每日Code Review,通过增加团队面对面交流,使知识得以传播,在团队中建立文化。虽然我们认为结对编程是更好的实践,可以更快速的传播知识,但对于刚刚开始敏捷转型的团队来说,实践难度太大,团队很难接受这种思想,也很难放弃现有习惯去工作,所以每日Code Review就是一种较好的折中。每天的相互反馈,可以帮团队尽早发现和修复问题,使每个人都能向相同的方向前进,同时可以有效传播好的工程实践文化,使新人快速了解团队的做事方式,帮助新人融入团队。

综上所诉,团队能以最小的变化引入持续集成,减少浪费,加上自动化测试使之更有效,再以每日Code Review在团队中建立文化,从而为成功敏捷转型打下基础。


应该怎么做?

CI

CI是一种软件开发实践。在持续集成中,团队成员频繁集成他们的工作成果,一般每人每天至少集成一次。每次集成会经过自动构建的验证,以尽快发现和修复集成错误。

在实施CI的过程中,要有独立的CI服务器,并在服务器上自动化以下事项:
  • 持续检查:静态代码扫描,关注代码质量
  • 持续打包:自动编译和打包,为部署做准备
  • 持续部署:能使同一个包,对各环境做自动或一键部署
  • 持续验证:自动运行所有测试脚本,验证产品功能
  • 持续基础设施:自动创建所需要的环境,并且将代码纳入源代码版本管理
  • 持续报告:建立CI状态墙,所有人实时关注;展示和发送每次运行的结果报告给相关人员
每完成一项自动化工作,都意味着团度效率的提升。

另外,所有人都要参与:
  • 开发人员
    • 提升提交代码的频率,每天至少一次
    • 编写单元测试和集成测试
    • 在代码提交前通过所有的本地测试
    • 第一时间修复CI上失败的Build
  • 测试人员
    • 尽早参与到整个产品的交付流程中,站在用户角度理解需求
    • 自己或和开发一起编写自动化验收测试
  • 运维人员
    • 搭建CI服务器和CI状态墙,优化CI速度和稳定性
    • 帮助产品团队建立CI流程
    • 做好环境和数据库的版本管理
  • 管理人员
    • 做好各种支持,如激励员工,提供资金购买服务器,大电视机等

工具:Go CD, Jenkins, Bamboo, travis-ci, TeamCity ...

自动化测试

根据测试金字塔原则,为不同的测试目的写不同的自动化测试脚本。

图:测试金字塔


单元测试,最容易编写,同时可以实现高覆盖:
推荐的单元测试编写方法是TDD。通过TDD帮助我们理清需求,想清设计,从而编写更高质量的代码。
测试因该关注需求,每个测试都应该通过“我要实现什么业务”的方式思考。我见过很多人在讨论单元测试的时候,都纠结于是给所有公共方法写单元测试,还是只给部分写,是覆盖所有分支,还是覆盖重要分支。而事实上这种讨论没有什么意义,测试要保障的是价值的实现,只有关注需求而不是关注代码,才能写出有价值的测试。
单元测试应该隔离依赖,可以反复且快速运行,并且只有一个失败的原因,一旦失败能快速定位问题,帮助开发人员快速修复。

中间层集成测试,编写难度适中,实现组件级重要场景覆盖:
在产品开发中,无论是传统的横向根据技术分层,还是现在的纵向根据业务分块,都免不了出现大量组件,以及组件之间的互相依赖。比如持久层要访问数据库,资源组件要访问网络等等。
集成测试就是把一个或多个组件,当成一个整体来进行独立的功能测试。这种测试通常不包含前端UI界面,这样可以避免处理UI所带来的复杂性。
集成测试有时需要通过一些手段,自建依赖,比如自己创建像H2这样的内存数据库,或者用mountebank之类的工具自建API服务,以便能在独立的环境中重复运行。

上层功能测试,最难编写,重点覆盖完整的业务流程:
功能测试也就是应用测试,是对产品整体的验证。功能测试只关心用户能接触到的内容,模拟用户使用产品,验证产品是否能解决用户问题,符合场景预期等。
一般功能测试会通过Selenium等自动化框架,来模拟实际用户对UI界面的操作,再通过验证界面上返回的信息来验证功能。
功能测试往往会有比较长的操作步骤,因此运行一个测试也会有比较长的耗时。同时功能测试会涉及多个页面和组件,所以运行失败后的问题定位往往会比较困难。这就要求一旦找到问题后,必须要补单元测试来对问题进行覆盖。

总之,根据测试金字塔,为不同目的编写不同单元测试。对于传统团队或遗留系统,可以从单元测试和功能测试入手,让开发写单元测试,测试人员写功能测试,在一段时间之后再引入集成测试。

每日Code Review

在团队转型初期,我见过一些团队会在Code Review中关注注释,代码执行细节,盲目听从一些老员工的建议。这样做不仅不能帮助团队成员得到有效的反馈,提高产品质量,也无法促进知识传播,建立团队文化。

因此,要让这个实践真正有效,我们要更多关注以下左项:
  1. 业务需求理解 > 代码执行细节
  2. 代码业务逻辑 > 代码风格规范
  3. 是否测试覆盖 > 人为逻辑判断
  4. 知识传递 > 听从权威
同时,关注别人代码中的亮点,指出来。

团队可以在每天下班前或站会后,半小时左右,团队中每个人通过介绍实现的需求、代码设计、读代码实现,使团队其他成员理解自己的代码,让大家提出反馈。

总结

敏捷转型没有捷径,通过踏踏实实提高产品质量,以较小的改变带来较大的收益,建立起持续反馈和改进的文化,这样才能慢慢提升团队适应变化的能力,使团队习惯变化、拥抱变化,真正开启通向敏捷的大门。




Tuesday, February 2, 2016

Code Review的正确姿态

正确的做Code Review可以帮我们解决两个问题:
  1. 减少Bug
  2. 使修改别人的代码变得容易


那么怎么样才是正确Code Review的姿态呢?


我们先从bug说起


bug的形态可以分为以下几类:
  • 逻辑不全 - 如缺少输入检查、缺少逻辑判断、少存一个字段等
  • 逻辑错误 - 如字段错位、与或搞错了等
  • 业务理解偏差 - 这个容易理解,就不解释了
  • 集成错误 - 如组件与组件之间接口不匹配等

要让别人鉴别出这些问题,首要任务就是让别人通过理解业务需求来理解你在做什么,对应的需求有没有相应测试覆盖,有没有被遗忘的角落或理解上的偏差。其次再将需求对应到功能代码上,讨论你实现的合理性,是否有更好的实现,是否有已存在的技术,可以简化现在的设计等。
因此这里我们得到了三个关注点:
  • 业务需求的理解
  • 相应的测试覆盖
  • 可读性高的代码

然后我们说说代码共享


容易被修改的代码通常具有以下几个特征:
  • 意图明显 - 让阅读者很容易理解作者为什么要写它
  • 容易阅读 - 有良好的层次,业务逻辑清晰
  • 反应当前业务 - 可以通过阅读代码了解系统现状,从而很容易找到修改点
所以这里要关注的依然是:可读性高的代码,也就是说同样的功能,能越快读懂的代码越好。关于更可读的代码,请参考《Clean Code》。

Code Review在这里能起到避免误读的问题,所以我们还是应该像上面提到的一样,先理解需求,随后看一下代码和需求是否一致。

另外,我们经常看到一些团队,在做Code Review的时候会有一些低价值的讨论,比如:
  • 用if还是switch
  • 注释应该怎么写
  • 大括号前要不要换行
  • 代码在什么情况下会被执行
  • 把Tech Lead的话当圣旨
虽然以上讨论能形成一些代码规范,但这些规范只能非常有限的帮助减少bug和代码共享。

因此,我们从要解决的问题中,找到了Code Review的正确姿态,总结如下:
  1. 业务需求理解 > 代码执行细节
  2. 代码业务逻辑 > 代码风格规范
  3. 是否测试覆盖 > 人为逻辑判断
  4. 知识传递 > 听从权威
同时,关注别人代码中的亮点,指出来。

那么下一个问题,Code Review的频率应该是怎么样的?


通常,大家都希望反馈的周期越短越好,只有得到足够快速的反馈,我们才能尽早的避免引入问题,保持始终往正确的方向前进。
所以基于期望得到最快速的反馈,最好的实践是结对编程。
但是结对编程对于刚刚开始敏捷转型的团队来说,实践难度太大,团队很难接受这种思想,也很难放弃现有习惯去工作。所以比较折中的方式就是进行每日Code Review,也就是团队每天找一个时间,大约半小时左右,在一起互相Review代码。这个时间可以在下班前,也可以在每日站会后,由团队根据自身情况而定。


总结


总之,通过每天在Code Review中关注业务、自动化测试和代码可读性,每个成员分享和传递知识,从而增进团队成员之间的相互了解,共同实现更高质量的产品,进而帮助团队在走向敏捷的过程中迈出重要的一步。 

Monday, February 1, 2016

自动化测试简介

为什么要做自动化测试?

自动化测试可以做到人类无法做到的快速验证和反馈,从而提高软件质量和降低修复成本。
想象一下如果不写脚本去做压力测试,会是什么情形?

测试总类繁多,包括但不限于:

通过几十年的技术积累,上面所诉的90%都有相应的技术做自动化支持。由此可见业界对于测试自动化的推崇。


根据测试金字塔原理:


图:测试金字塔

金字塔越下层,说明运行速度越快,编写成本越低,应该在所有测试中占比最多。
金字塔越上层,说明运行速度越慢,编写成本越高,应该在所有测试中占比最少。

由此引出保障软件产品质量的三个基本测试:单元测试、集成测试和功能测试。

底层单元测试:

我们推荐的单元测试编写方法是TDD。通过TDD帮助我们理清需求,想清设计,从而编写更高质量的代码。你得到高覆盖率的单元测试仅仅是额外的奖励。
Java中我们用到的测试框架通常是JunitMockito
Scala我们会用Specs2

测试的编写应该从需求出发,每个测试都表达一个功能点,而不是从代码的角度去讨论单元测试,这点同时应该在方法名中体现。比如在猜数字的练习中,我们的测试描述应该写成 "should_tell_how_much_numbers_are_matched_when_I_guess",而不是"test_method_guess"。

另外为什么先写测试?
试着回答两个问题:先有需求还是先有代码?如何验证代码的正确性?
答案显而易见:先有需求,“实现了的需求”是检验代码正确性的唯一标准。
因此,我们应该根据需求编写相应的测试,并通过所有测试,以此来证明我们写的代码是可靠的。

具体写法,请参考示例

中间层集成测试:

在Restful应用中,集成测试通常是测试服务API。这种测试可以对某个服务进行端到端的测试,同时又避免了处理UI的复杂性。
API的测试,基于Java最常用的就是RestAssured,再加上Spring Test内存数据库等技术,使API测试能独立、可重复的运行。


上层功能测试:

功能测试也就是应用测试,是对产品整体的验证。功能测试只关心用户能接触到的内容,模拟用户使用产品,验证产品是否能解决用户问题,符合场景预期等。
最常见的就是UI测试,通过Selenium来模拟实际页面操作来验证产品功能。



所有测试都应遵照以下流程:

  1. 准备测试环境和被测对象
  2. 给定输入
  3. 验证输出

所有测试都应该符合以下标准:

  • 独立性:测试应能够清楚的表明一个功能或场景
  • 可重复性:测试应可重复运行,且都以同样的形式成功或失败
  • 自我验证:测试要无歧义的表达成功或失败
  • 完整性:测试应该不需要人为参与做类似于调整数据的操作

最后,要学会如何写自动化测试,重点是多练。可以从基础的题目开始练起,用自己擅长的语言。重点训练自己的测试思维,即我们是在验证用户价值,这样才能使测试真正有效。


Resources:
Kata练习题:http://codingdojo.org/

Books:
程序员的职业素养:http://book.douban.com/subject/11614538/

Wednesday, September 2, 2015

为什么应该在团队内完成整个开发生命周期

经常听到有人说,数据库很重要,所以应该有一个经验丰富的团队来把关。架构很重要,所以应该由一些架构师来统筹。

先不说这些高级团队到底能做到多大改善。但这种组织架构会带来致命的问题——组织的开发速度上限受限于这些高级团队,甚至是效率最低的那个。




如图,首先问题是,这些团队会在整个开发流程中成为瓶颈,我们无法通过横向扩展团队来提升组织的开发速度。这对软件产生价值的组织来说是致命的。

第二个问题是,开发中的沟通成本大量增加,团队间的沟通成本会远远大于团队内的沟通成本。如果功能开发团队依赖于大量其他团队,就会使整个开发成本变高,价值流速度降低。

第三个问题是,核心团队成员需要长时间培养,人员流动往往会造成致命的打击,就好像组织中有一根软肋,如果竞争花钱把某一个核心团队成员全部挖走,该组织可能会立刻消失。

由此可见,为了保持某些看起来很重要的东西(比如数据库,架构)运行良好,就让专业团队去做的想法有很多致命问题。

那怎么样才是好的实践呢?



特性团队 + 微服务

团队自己负责所有事务,从一开始就避免大型数据库和大型架构的设计,每个功能根据自己的需求,完成相应的服务设计、开发、测试、部署和上线。如果需要修改其他服务,也由该团队自己完成。通过自动化测试去保障质量,而不是由某个高级团队。

如果遇到遗留系统怎么办?

因为遗留系统可能没有足够的自动化测试去保障质量,所以首选的一定不是去修改它。把遗留系统API化,给遗留系统补测试,把一部分功能拆出来另外实现,都是我们常用的方法。
当然,我们还是避免不了去修改它,这种时候,修改还是由团队自己来完成,因为没有人能代替你去理解业务,没有人能代替你提供实现方案。只是团队需要自己足够谨慎,通过增加对遗留系统的了解以及找曾经在系统上工作过的人沟通,从而具备修改它的能力。



总之,指望把当前团队解决不了的问题扔给另一个团队去解决是不靠谱的,我们应该提升团队能力,使该团队具备解决问题的能力,而不是把希望寄托在别的团队上。

Thursday, July 16, 2015

Never register a domain on oray.com

Oray has a terrible service.

First, they require me to upload a photo within a personal ID by email and mention that the DNS would be affected if I don't upload the photo.

Second, I tried to transfer my domain out but there is on way to do that by myself. They don't provide the service in their web site. And they still require my to send a mail which include a hard copy of my personal ID and an additional paper form to their office. And they won't pay the fee of the mail.

Third, I made a phone call to their service centre. They told me the only way to transfer my domain out is to fill the additional paper form and send it to them with a hard copy of my ID. They had nothing to do.

Last thing is they never mentioned these above until I tried to transfer my domain out. Which means I was informed 5 years later after my first payment for my domain.

So at last, I try to get my auth-code through ICANN. The address of complaint page is below:

Hope this could help anyone who has the similar trouble with me.

Be away from these terrible registrars.


Wednesday, May 20, 2015

Surprise by the new html5 and css3 technologies

Recently I joined a course about how to do a UI design.

Here's the showcase.
https://heaton.github.io/week3/ (not available for IE and old Browsers)

These pages are all made by pure css 3 and html 5 with a little jQuery code for click events and touch events.



Friday, May 15, 2015

Git Tips

Shorten your Git commands - Alias

git config --global alias.[name] "command"

Examples

git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.cob "checkout -b"
git config --global alias.back "checkout master"
git config --global alias.cof "checkout --"
git config --global alias.pl "pull --rebase origin master"
git config --global alias.plf "!git stash && git pl && git stash pop" # ! means runing the command as shell command
git config --global alias.lo "log --graph --oneline"
git config --global alias.l "log --pretty=format:'%C(yellow)%h %C(red)%ad %C(green)%d %C(reset)%s [%C(blue)%an]' --date=short --graph"

A better git log tool - tig

TIG

install

brew install tig

Get commit from other branchs - cherry-pick

cherry-pick
git cherry-pick [CommitID]
git cherry-pick --no-commit [CommitID] # 把commit内容放到本地的cache里面
You can test cherry-pick on GitStudy.

Find commit which you force deleted - reflog

Git won’t really delete any commit even you use git reset --hard HEAD^. You can get it back by reflog.