我的ODC项目经验分享

项目介绍:客户公司旨在为病人提供更加优质低价的治疗方案。其主系统联合病人、医师和医保公司,根据病人的病症、体检数据、过敏情况、生活习惯和过往服药方案等信息,结合其内部一套引擎工具,检查用药过程中的问题(Drug Therapy Problem)并提出给药建议。

 
在三年的合作过程中,我们不断丰富其主系统的功能,提高用户体验,降低页面响应时间,并对过去的代码进行重构和完善。项目后来变得越来越庞大,我们将其中三个次要功能抽出作为服务,于是我们又多了三个新的子项目。 今天,我们打算分享一下我们维持项目稳定的一些因素。

一 、重视客户所重视的

对于客户来说,这个项目应用于美国,同时涉及到病人的隐私信息存储,因此,客户一开始最重视的并不是我们对项目的理解程度和开发进度,而是信息和代码的安全和保密问题。这个项目基于HIPPA(Health Insurance Portability and Accountability,中译:健康保险携带和责任法案),对于客户来说,只要发生一次病人隐私泄漏,他们就无法继续运营了,因为美国对此要求及其严格--“病人的隐私高于一切”。
 
在项目初期,客户对安全保密这方面极其敏感,我们也不得不小心应对,一再承诺我们的邮件只会使用公司专用邮箱,信息不会发送到私人信箱,定期分析服务器日志,同时也不会在外网开放访问权限。我们认为,此时取得客户信任,比进入迭代开发更加重要,因为这意味项目是否能真正开始。为了向客户展示我们的诚意,我们申请在苏州分公司单独隔出一个小办公室,安装有单独的门禁系统。此外,为了进一步确保安全,也申请了专线网络。这一切,都是为了告诉客户,我们和你们一样,甚至比你们更重视信息隐私工作。
 
尽管如此,还是会有些意外的小插曲。在我们接手项目不久,有家公司开始申请注册PharmMD作为他们的商标,客户发现了这件事,于是来询问我们。虽然说不上“危机公关”,但在那个敏感时期,这件事也确实也让大家紧张了一回。于是,我们开始调查这件事,发现这只是一个巧合,正好只是一个药妆公司在申请商标,并不涉及到我们的员工泄密或者恶意抢注。药店使用Pharm开头的单词作为品牌十分正常,比如连锁的PharmPlus药房。这件事细说起来其实和我们没有关系,但是为了安抚客户,后来还是做了一系列说明工作。

二、从UI开始,从测试代码开始

在我们接手这个项目以前,这个系统已经开发并且使用了五年多,在外包给我们之前,曾经也外包给Intridea公司--他们的设计和模块封装都是非常不错的。对我们而言,这既有便利,也带来不少困扰,便利在于免去了初期定义的麻烦,而要明白项目的逻辑和业务,对我们来说也是一个挑战。
 
我们拿到项目源代码时,发现这个项目同时使用了PostgresQL和Redis两个数据库,PG这边已经建立了五六十张表,项目中对应的模型达到了七八十个,源代码达到了1万9千行左右,测试代码更是达到1:3左右的比例,spec测试和feature测试代码加起来接近五万行,全部测试运行完大约需要三四个小时。而一般的ROR项目,都会尽量把源代码控制在3000行左右;这个项目能如此庞大(这还是在尽可能优化之后的结果),并且没有说明文档,也是让人始料未及的。
 
面对这样的庞然大物,如何开始着手是我们遇到的第一个问题。好在客户也了解这一点,一开始的几个迭代并没有特别多的逻辑功能需求。为了了解现有的代码,我们从两条路出发。一是从UI的修改开始进行开发,二是通过阅读测试代码。 从UI开始实际是一个逆向了解代码的过程,同时也在进行着简单的开发,可以让客户看到我们在前进。比如某个story可能只是对CSS或者页面的文字进行修改,一开始我们很可能都不知道这个页面是哪个控制器渲染哪个页面返回的结果,这时候可以通过页面上的特征文字或者CSS的class和id进行搜索,由于项目中涉及I18n,我们可以现在locales文件中定位文字,找到相应的key,从而反向搜索出对应的view和controller。这未必是一个非常理想的方法,但是还比较适合这个有特别多lib封装和module而又需要尽早看到产出的项目。
 
另外一条路则是阅读测试代码,这个项目的测试代码十分繁杂,但也给我们带来了一些便利。feature测试的部分代码,由于其几乎接近自然语言,配合其详尽的测试用例,我们可以了解某一部分的逻辑需求。这个方法比较适合去了解一些“客户认为是常识,无需多言,而我们却不知其然”的需求。

三、保证代码质量

如何保证代码质量,准确的说,和客户合作的这三年,我们和客户其实在共同探索这个问题的答案。
 
一开始我们每周会有code review meeting,确实有些效果,但是就我们实践下来觉得效率并不高。当review某个人的代码时,reviewer如果不了解前因后果,可能会觉得相当乏味,而且那时团队里有五名开发人员,每周花一个下午review所有人的代码要消耗相当多的时间,往往在会议进行半小时以后,大家打酱油的成分就变多了,因此这种会议大概只持续了半年。那之后我们之后是如何保证代码质量的呢。
 
首先,针对RoR项目,我们有许多约定和代码风格规范,成员们尽量尊守这些约定和规范,另外我们还有一些工具可以对代码风格进行检查。具体的代码风格规范,可以参照RubyChina上wiki里的相关内容,在github上也有ruby和rails的风格标准化文档。而工具方面,我们可以用rails_best_practice来检查项目中的代码风格问题,同时也会利用brakeman来扫描项目中是否存在安全漏洞;不过这些工具毕竟是机械化的扫描,所以经常会遇到他们过于敏感的问题,所以有选择性地修改一些就好。
 
其次,我们会保证自动化测试代码能有高的覆盖率。我们在提交代码时有两个约定,一是每个新的方法,都必须和对应的单元测试代码一同提交,即no spec no commit,二是修改完现有代码后,至少应该保证spec测试能全部通过再提交。 最后,虽然我们取消了code review meeting,但我们仍然有代码review的工作。由于我们使用Github做版本管理,所以我们使用了其PR(pull request)功能。对应每一个user story,程序员开发时会建立自己的开发分支,分支的名字一般用story ID加功能描述命名,当整个story的工作完成后,开发者建立一个PR,请求将当前分支的代码合并到主开发分支上。在PR中,程序员会先@一位对相应代码或者需求比较了解的组员进行初步review,如果reviewer对有代码疑义,则会在对应的代码块进行备注,接下来就需要进行讨论或修改;如果没有疑义,初步review通过,则会进入final review的阶段。在我们的项目中,final review是由客户方的一位技术人员进行把关。Final review也通过后,开发者就会将代码合并到主开发分支上并关闭PR,接下来测试人员就可以对该功能进行测试。 此外,尽管如此层层把关,终端用户在使用时,还是会因为一些没有设想到的操作,或者系统原因而遇到一些错误。这时候我们使用Airbrake记录每一次程序报错和error stack,便于我们进行修改;也会利用NewRelic,捕获项目中存在效率问题的部分,以便我们在重构时有优化的方向。

四、有人情味的团队

我个人认为团队的人情味和项目的稳定性是相互影响的。只有项目稳定了,团队里的组员才能有足够的时间磨合和培养默契;而大家足够团结时,才有机会让项目稳健地进行下去。
 
我们比较期待team building活动,虽然只是吃喝玩乐,我们还是能争取则尽量争取。于是乎大家似乎或多或少都学会了三国杀,我也能在台球桌边装作姿势还不错的样子(不过基本打不进球)。也就是在这些玩闹的过程中,大家增进了解,也会明白各自处事的方式。我们的团队虽然不大,算上汉族,我们居然有四个不同民族的成员。多样性带来了很多不一样的观点,也带来了一些创造性,我觉得这可以算做是一个团队的asset。
 
同时我们对待客户也是比较有人情味,大家遇到节日会发送问候,遇到有人结婚生子也会发一些祝福。有次客户那边有人结婚的时候,我们从亚马逊给其买了一些小礼品;又或者我们会把团队的照片做成明信片再寄给客户。我觉得看得见摸得着的实物,比网络上的文字更能打动人。
 
如果说编程能力、代码质量是硬件,那么人情味就可以算是一种软件,二者都十分重要。
Categories: 
up
0 users have voted.

Add new comment