–本文于2016.08.23最新更新–

早在读研究生的时候,自己负责着实验室的项目,就一直在思索如何建立一套简单又高效的研发管理体系,能够在保证项目高质量顺利进行的同时还能够提升团队成员的技术level。后来在自己在校的几次小的创业中,也做过一些尝试。直到毕业后进入前东家,在几个项目的参与过程中,见到了大公司的研发管理是如何进行的。直至加入目前的公司,将研发体系梳理一遍,且学且抄且实践,对这一套东西算是有了一定的实践感悟。

对于一个研发管理体系,其核心是围绕着产品的整个生命周期来进行的。因此,根据一个产品的生命周期,可以把研发体系划分为几个关键的环节,如图所示:

更为具体的一个研发流程则如下图所示,标注了每一个环节的参与角色。

prject_manage_detail.png

可知,即时沟通和技术提升虽然不属于研发流程中的某一个环节,但它们是贯穿整个研发体系不可或缺的一部分,有着不可替代的作用。此外,任务管理需要对任务做整个研发生命周期的管理,除了作为其中的一个关键环节,也是贯穿整个研发流程的。

任务管理

任务管理是产品整个生命周期首要的环节,其对研发体系也是至关重要的。项目生命周期模型,传统的有五种:瀑布模型、原型模型、螺旋模型、增量模型、V模型,而现在最为流行的是迭代开发模型,敏捷开发则是采用迭代模型的一种典型项目管理方法集合。Scrum是目前敏捷开发中最为大家熟知的开发模式(XP极限编程也是一种比较常见的敏捷开发模式),其开发流程的概览如下图所示:

简单来说,Scrum是依赖于三种角色、四种会议的自组织、信息透明化、成员平等的一种敏捷开发流程。更为详细的描述,可参见此篇文章:http://blog.devtang.com/2014/09/13/scrum-introduction/

除了Scrum之外,看板是最近兴起的另一种开发模式,在最近很火的美剧《硅谷》里面“魔笛手”就是采用的这种方式。看板将工作流程形象化,首先把工作细分成任务并根据需要将任务分为Pending、Analysis、Development、Test、Deploy等状态,然后根据任务的进行,在几种状态之间进行转换。对比Scrum,看板使用开发周期作为计划和过程改进的度量数据,不强调迭代的概念,也没有很强的时间期间概念,也不需要制定任何团队角色。对于看板方法论的详细介绍可见此篇文章:http://kanbanblog.com/explained/http://www.jianshu.com/p/e44b1038c9cf这篇则做了比较形象具体的说明。这里有一点需要注意,Scrum和看板并非是对立的,它们是可以结合起来使用的。使用看板来管理每一次迭代的任务是一种可取也是很常见的精益实践。

依赖于任务管理方法论,市面上很多软件都做了相应的支撑,自己曾经使用过的任务管理软件如下:

  • Redmine: 这个是自己最开始接触的任务管理软件,使用也比较广泛。比较遗憾的是,redmine安装有点繁琐,而且基于ROR,如果需要二次开发,需要重新学习ROR。
  • Tower.im: 这是一个任务管理云服务,界面设计的简单优雅,一目了然。很多小的私有项目,我都会用这个进行任务管理。类似的还有teambeation等。
  • Jira: 这款软件是商业版的任务管理软件,对于这一块做的是非常专业的,很多大公司都在使用。但是,它是收费的。所以,如果你要用,要么付钱,要么去破解。。。
  • 禅道:这款软件最早是叫做bugfree, 是开源且主要针对Bug管理的,后面慢慢发展成现在的集任务管理、bug管理、团队管理等的项目管理软件,并开启了收费策略。总体来说,功能很全,也比较专业,但是ui上有种传统it系统的感觉,流程上也不具有现在敏捷开发的一些优势。
  • Kanboard: 是实现了Kanban方法论的任务管理软件。

对于个人的项目,其实依赖于tower.im这种第三方云服务完全足够了。如果担心数据安全的话,那么推荐在内网搭建Kanboard进行看板任务管理。

文档协作

研发中首当其冲的就是文档撰写,这个很多情况下都决定了项目的可维护、可管理性。有人会说现在流行的是敏捷开发,根本不需要写文档,但其实这是对敏捷的误解。敏捷开发强调的是快速试错、快速迭代,而非简单粗暴,对比传统开发模型虽然并不强调文档,但并不代表不需要。对于一个项目,从开始就需要需求文档、产品原型文档、项目进度文档等等,而到了研发这一步,在系统实现、写代码之前最好的就是先“想”再做,而“想”的一种比较好的输出形式就是文档。对于一个软件系统,一般来说需要写的文档有以下几种:

  • 系统业务流程文档:描述系统业务逻辑的文档,能清晰的说明真个业务的流程。
  • 系统架构设计文档:对整个系统的架构的描述,需要包含系统的各个关键组成模块以及相关的各个关键技术点等。
  • 系统功能模块概要设计/详细设计文档:对于某一个模块的流程、逻辑的描述。
  • 数据DDL/DML文档: 与系统相关的数据库的DDL和DML文档,对于前者,是需要包含所有的操作的,而对于后者,必不可少的是查询语句,用来提供给DBA,来做查询sql的review,以保证索引的正确建立和查询语句的合理等。
  • 系统部署文档:描述系统关键部分部署在哪里,需要做哪些配置。
  • 系统发布ChangeLog:对系统每次发布的改动进行描述,包括数据库、缓存、数据队列、新增/变动了哪些依赖服务等。此外,对数据库、缓存这种关键服务的量化分析也可以写在这里。

尤其对于一些相对复杂的功能来说,整理思路形成文档,不仅可以让自己逻辑清晰,也让后续维护的人能够更快地接手。当然,这些并不是死板要求的,应该根据实际的业务选择,不一定所有的文档都是必须的,也不一定要分开这几个文档写(可以将这些内容集成在一个文档中,这也是目前我经常采用的方式)。这些文档的范例可以见:https://github.com/superhj1987/awesome-tech-collections/tree/master/document

而对于文档撰写协作的方式,我自己经历过的有以下几种:

  • 使用word撰写各种文档,提交到svn等版本管理工具上
  • 使用google doc进行协作
  • 使用word撰写文档,然后提交到项目管理软件中进行管理
  • 使用markdown撰写文档,提交到版本管理工具上

我自己比较推崇的是使用markdown撰写文档,然后使用git、svn版本管理工具或者是其他团队协作工具做版本管理。之所以使用markdown, 能够极大地节省使用word时调各种格式、样式耗费的时间。对于程序员来说真的是如虎添翼。如果是对文档多人协同编辑有刚需的团队,可以选择使用google doc或者国内的石墨(http://shimo.im)。

此外,在移动app开发中,还有一个非常关键的文档就是api文档,是服务端提供给客户端调用接口的说明文档。比较简单直接的方法就是定制一套api文档模板,然后在写接口代码之前或者之后,按照模板编写接口文档。此外,可以实现一套根据源码自动生成文档的机制,在代码编写的同时就能自动生成相应的接口说明文档。在使用Spring MVC开发的后端应用中,个人推荐SpringFox,使用此项目能够通过在Controller中加入相应的注解信息从而自动生成Api接口文档,同时也提供了在线调试的功能,极大减少了api文档的工作量。

代码协作

对于一个技术团队,最最关键的肯定是写代码。一个人单打独斗那倒好说,但是这就像篮球场上,一对一靠个人硬实力,但是5对5,那就不仅仅是一个人实力强就赢得了的了。因此对于技术团队来说,代码协作是至关重要的一个部分。

  • 代码版本管理:Git + SVN

    几年前最流行的代码版本管理工具是svn(当然此前,更加古老的还有cvs之流),的确为程序员们的代码管理带来了很多便捷。但到了现在,相比起这种集中式代码管理,目前最为火热的当属git这种分布式代码管理工具,在Linux上直接搭建git服务器来构建项目的git系统的。而这几年随着Github以及类似系统的涌现,对于很多私人项目我都是采用oschina或者gitcafe提供的git私有代码管理来做代码版本管理的。当然,对于公司来说,有很多开源类github系统可以搭建在企业内网。详细的可以参见:搭建自己的github。当然,对比svn,git也是有缺点的。无法天然的支持对于目录级别的权限管理和基于目录的版本管理操作是目前不得不结合svn和git一起使用的重要原因。通常情况下,可以使用git做版本管理,辅以svn做基于目录级别的发布包管理。

  • 代码分支/Tag管理: Git Flow

    其实分支/Tag管理是代码版本管理包含的内容,之所以单独出来,是因为对于分支的使用其实还是有一定的原则和技巧的。并非如很多人一样,所有项目就一个master分支,所有修改都往这里塞。目前,最为流行的一种基于分支的工作方式就是:Git flow。介绍可以见: 基于git的源代码管理模型——git flow。简单概括就是:

    • master和develop作为主分支。主分支是所有开发活动的核心分支。所有的开发活动产生的输出物最终都会反映到主分支的代码中。master是可以随时发布的分支,而develop则时刻保持最新的开发代码。
    • 辅助分支是用于组织解决特定问题的各种软件开发活动的分支。辅助分支主要用于组织软件新功能的并行开发、简化新功能开发代码的跟踪、辅助完成版本发布工作以及对生产代码的缺陷进行紧急修复工作。这些分支与主分支不同,通常只会在有限的时间范围内存在。包括:
      • 用于开发新功能时所使用的feature分支;
      • 用于辅助版本发布的release分支;
      • 用于修正生产代码中的缺陷的hotfix分支。 对于此种开发模型,这里也提供了一个命令行工具:https://github.com/nvie/gitflow
  • 代码质量保证:结对编程 + 定期review + PR目前一种比较好的方式。结对编程这个是一个老生常谈的方式,两个人共同承担某一开发任务,互相保证对方的代码质量,在很大程度上能够提高代码质量。而定期review则是让团队所有的成员都能够参与到这个过程中,不仅仅能够保证被review者的代码质量,也能够让团队成员学习到好的代码是怎样的而差的代码又是怎样的。PR是Pull Request的简写,当开发完成的代码提交到主分支时,需要发起pull request,此时团队负责人需要review相关代码,确保没有问题之后,才能accept此次pr。当然,上面讲述的是如何通过人来保证代码质量。除此之外,还可以通过技术上的手段在一定程度上保障代码的质量,这一部分在后续的自动化测试机制会讲述。

此外,在移动app项目中,一个很普遍的问题就是:在定义好Api文档之后,客户端如何在后端并没有完成接口开发的情况下开发或者调试程序?这里有两种方案:

  • 客户端做好接口封装,在后端接口未完成前,客户端不经过网络io直接返回静态格式数据。这种方式最好是由客户端定义接口格式数据。
  • 后端将示例接口返回数据写在文件里,接口直接返回静态文件数据。此种方式,由后端定义接口数据格式。另外,有一个开源的工具: httpbin可以用来提供接口返回指定格式的数据,中文介绍可见: https://blog.phpgao.com/how-to-httpbin.html

关于客户端和后端的接口代码协作,还有一个Chrome插件POSTMAN可以使用。后端可以使用此插件在编写完接口后进行自我功能测试,测试无误后可以将接口以文件或者url的形式分享给客户端供客户端参考和调试。

质量保证

当代码开发完成之后,需要质量保证机制的介入来保证功能的正常运行,从而保证代码是可发布的。一般来说,质量保证的手段就是测试,分为:

  • 代码质量测试
  • 功能测试
  • 性能测试

代码质量测试一般是在编译打包代码之前进行,通常是自动化进行的。针对Java项目,自动化代码质量测试可以分为以下几步:

  • 源代码规范检查:对于Java来说,代码规范的检查一般使用checkstyle来检查。默认的规范非常严格,这里大家可以根据需要放宽一些规范。
  • 源代码静态质量检查: 常用的工具是pmd, 可以检查Java源文件中的潜在问题, 比如空try/catch/finally/switch语句块等。
  • 字节码bug检查:常用工具是findbugs,基于Bug Patterns概念,查找javabytecode(.class文件)中的潜在bug。如NullPoint空指针检查、没有合理关闭资源、字符串相同判断错(==,而不是equals)。
  • 单元测试:使用junit即可,当然在这里当使用mvn时,其test phrase会默认生成测试报告到${project.build.directory}/surefile-reports文件夹中。这里建议使用coverage生成单元测试报告,其中一个关键的单元测试覆盖率指标达到98%以上才为合格(根据需要自己调整即可)。

以上提到的工具,都是有maven插件的。通常情况下,也推荐使用这些工具的maven插件来调用。目前流行的自动化ci工具jenkins、QuickBuild等结合各种丰富的插件可以提供这些功能,将他们集成到一个测试流程并形成最终的测试结果报表。

在代码发布到线上环境之前,一个关键的步骤就是功能测试,通常都是工程师来进行的。需要测试工程师根据产品需求,形成测试用例,然后根据这些用例做相应的测试。测试用例的一个模板如下:

用例ID 功能名称 用例名称 测试数据 前置条件 操作步骤 预期结果 测试结果 备注 review说明
- - - - - - - - - -

需要测试工程师根据需求创建并经过研发人员reivew确定测试用例,待到发布前进行测试以及反馈,直到所有测试用例都通过。

对于移动app功能的测试,目前市场上有类似bugtags这种所见即所得提交测试工具,可以很方便的提交bug。

功能测试通过之后,对于一些对性能有要求的项目,还需要进行性能测试。对于这种测试来说,通常有以下几种方式:

  • 测试工程师写性能测试代码来进行测试
  • 使用性能测试工具测试,如LoadRunner、ab等

当然,所有这些测试都是在项目发布上线之前进行的,通常是在项目的测试、预发布环境中进行的。

此外,对于测试任务的管理工作一般在任务管理软件中都做了集成。也有类似Mantis这种事专门做缺陷管理的。

自动化部署

对于Java项目的发布流程,如下图所示:

deploy

使用ci软件可将以上步骤自动化的。

如上图所示,对于一个项目,我们是划分为三种或者四种环境的。

  • 测试环境: 这个环境是一个相对来说比较宽松的环境,所有代码的提交都会触发jenkins的自动代码质量检查和部署。测试工程师也是首先在这个环境下进行功能、性能测试的。只有通过了,才能部署到后续的下一个环境。
  • 集成环境:这个环境不是必须的,只有当项目出现了两个大的分支并行开发,发布前需要集成两部分代码时才需要这样一个环境。一般来说只使用jenkins进行部署前的打包流程,部署流程由相关人员进行。这个环境也是需要测试工程师进行测试的。
  • 预发布环境:这个环境和线上环境是一模一样的,不同的是此环境下的服务器是不在线上服务器集群中的,并不为用户提供服务。此环境下的项目发布也是需要人工参与的,也必须由测试保证功能和性能的正常。
  • 线上环境:这个环境是比较严格的一个环境。在发布前,一般来说会进行发布确认等一系列上线评审工作后,由项目负责人或者运维人员部署发布。
    • 功能列表 vs 实现情况:检查是否已经实现所有计划的功能?如果有某些功能没有实现需要说明原因。
    • 软件演示
    • 测试结果和遗留问题列表:测试用例的情况,遗留的Bug以及情况说明
    • 上线确认
    • 后续任务计划

其中,上线确认书的一个例子如下:

xx项目上线确认书
需求方验证结果 意见: 确认人:[由各个负责人签字]
开发确认 意见: 确认人:
测试确认 意见: 确认人:
服务器是否需要重启 [是否需要自动更新那些App?] 确认人:
服务器配置影响 [是否需要增加新的服务器ip,是否需要修改nginx/tomcat,是否新装软件,是否新建域名?] 确认人:
数据库更改 [是否需要修改线上数据库?是否有初始化语句?索引是否正确建立?查询语句是否合理?量化分析数据(包括缓存)是否无误?] 确认人:
数据初始化 [是否有初始化数据?如价格,默认分类等] 确认人:
上线评审结论 [ ]通过
[ ] 未通过,不能上线
[ ] 未通过,但修改完制定Bug后可直接上线
确认人:
计划上线时间 2016-08-01

后续任务计划,示例如下:

问题描述 责任人 计划完成时间 状态
xx xx xx xx

故障管理

由于各种客观原因如带宽、主机配置、流量异常或者程序逻辑不够严谨等原因,线上服务并非100%可用的。研发体系中最后把关的就是这一道故障应急机制。也就是说,一旦发生线上故障,如何快速反应并修复问题,如何避免下一次犯同样的错误。

对故障的快速反应需要依赖于运维的监控机制,包括基础设施层面的监控以及业务层面的监控,一旦发生故障应该立刻发出告警到相关人员。这里可以使用nagios、cacti或者第三方服务(如:监控宝)实现,当然,如果你使用的是云服务,一般也会有相应的云监控服务提供给你的。后续的故障问题定位很多情况下则是取决于你的应用日志打的是否合理,是否有足够的覆盖面的,是否有足够的信息。ELK和请求链路监测系统(同染色日志系统)是目前比较流行的基于日志的故障定位解决方案。问题一旦定位到了,那么修复就是水到渠成的事情了。

这里需要说明的一点是,上面讲述的是后端的故障快速反应和修复。针对客户端的故障,一般情况下都是由用户发现的。但是由于客户端发布流程的繁琐,很难及时修复一次发布版本的故障,只能等到下次解决。但是,目前一些客户端使用混合开发,其中的h5页面是可以在线修复的,另外,很多安卓app热更新方案也都能在线修复一些代码故障,如NuwaHotFixdexposed

故障解决完并非最终的结果,之后的故障总结也是故障管理尤为关键的一点。大公司会根据故障产生的影响不同定义不同的故障级别,从而追责到个人,再进一步影响个人的职级评定或者绩效考核、奖金之类的。但这一套却并不适用于小公司,毕竟大多数小公司没有那么完善或者说根本就没有职级和绩效这么一说。其实,追责并不是主要目的,最主要的是如何避免再次出现问题。因此,对于小的创业公司来说,最需要做的就是如何对已经发生的故障做总结,吸取教训。构建一套故障总结wiki则是一种很好的方式。下面是一次故障总结模板;

2016.08.01xxx故障总结
故障等级 [故障等级]
故障描述 [描述故障发生的现象]
故障发现时间及发现人 [xxx于xxxx年xx月xx日 HH:mm 如何发现该问题。]
故障影响 [影响时间范围、影响版本范围、影响产品范围]
故障原因 [阐述故障发生的原因]
解决方案 [详细记录如何解决此次的故障]
故障教训 [如何避免下次出现类似的事故]
责任人 [责任人签名]

即时沟通

显而易见,即时沟通是任何团队都必不可少的一个机制,同样也是研发团队必不可缺的。常用的就是QQ、钉钉或者企业内部的im软件。那么对于小公司或者创业公司,不想用第三方服务的该怎么办呢?之前蘑菇街开源过一个teamtalk的软件,不过后来由于某些原因已经下线。目前,有一款开源的web im软件可以供大家选择:Rocket.Chat,能够搭建出内网的slack服务(将分散的沟通方式聚集到一个地方,融入到一个信息流中)。

此外,我自己还尝试过使用intellij自带的IDE TALK来进行研发团队的在线交流。使用这个比较好的一点是可以直接做基于代码的即时交流,比如能够发送一个代码片段给同事,他那边接收到之后是直接能在他的项目里相关代码处进行操作的。

技术提升

一个研发团队,很重要的一点是如何提高团队的战斗力。对于个人来说,在平时的工作中,提高技术的熟练度和深度,在业余补充学习专业知识,提升技术广度,这些都无须多言。那么如何在整体层面或者说是管理上促进团队成员的技术提升呢?可以采取的方式有以下几种:

  • 构建内部的技术wiki并建立技术分享机制,鼓励大家以演讲或者技术博客的方式分享自己的技术经验或者教训,既可以对自己进行review又可以给其他成员以启示。这一点,很多公司都是纳入绩效中的。
  • 将一些项目开源,让团队成员能够享受到开源项目带来的各种好处,比如提升个人在业界的知名度、提高编码的水准(毕竟不好的代码,你也不好意思放出去)。
  • 定期举办类似黑客马拉松的比赛,提高团队成员的凝聚力,也能够提升成员解决实际问题的技术能力。

自己比较推崇的是第一种方式,但是开始的时候往往会发现很多人是不会主动去分享的。要么是觉得自己的东西技术含量都很低,要么就觉得自己的知识为何要分享给别人。可以采取的办法就是从最初的周期性安排人员进行技术分享,然后慢慢形成一种氛围和习惯,再到后续鼓励大家主动分享。当然,辅以奖品激励或者绩效奖励也是一种不错的方式,但切忌不要忽视一些业务能力很强但不爱或者不善于分享的工程师。

至于项目开源,前提一定是团队的项目真的是高质量并且会对开源社区有贡献的,不能为了开源而开源。尤其是对于一个公司来说,一个开源的项目直接体现了公司技术水准的高低,会对公司的pr、招聘等都带来一定程度的影响。

而黑客马拉松比赛这种方式,尤为关键的一点是要选择合适的主题。一般来说,围绕现实的业务场景来出题不仅能够提升大家解决问题的能力,也能顺便解决实际问题。比如"根据用户已有行为日志预测用户未来的行为"、“怎样构建合适的用户质量模型”都是比较合适的主题。此外,借鉴黑客马拉松的这种形式,可以采取类似“每周一题”的做法:在每周例会上给出一道和线上业务相关的问题,如“如何提高信息流的点击转化率”,然后每个人发散思维给出自己的解决方案,最终形成文章发布在内部的技术wiki上。对于每次主题,都会在下周的例会上针对每个人的解决方案进行讨论。

此外,在安排团队成员去调研一种将要使用的新技术的时候,务必要深入到源码层面,这个观念是需要灌输到团队每一个人的意识中去的。去使用一个没有看过源码或者没有掌握其运行原理的开源软件是一件风险非常大的事情,极有可能造成巨大的线上故障。

以上,是自己对于研发体系的一些实践和感悟,很多地方仍然有所欠缺或者并非最佳实践,也一直在探索更好的方案。