作为创业公司很重要的一个环节就是在有限的时间和资源下把产品需求落地为产品,也就是研发和项目管理,毫无疑问,这个阶段的主角是开发人员,那作为一个PM,把做项目管理的过程也当成做产品的过程的话,是不是应该多思考下怎么面向开发人员来优化整个研发过程和项目管理流程。本文就是我们如何通过优化开发环境搭建,代码管理,需求生命周期管理,开发任务分配和追踪,项目整体进度管理来提高研发过程中开发人员效率,通过持续集成和交付让开发中的问题更早暴露,通过合理的测试反馈工具让开发人员最快定位和解决问题。
印象中很多关于产品和开发为了进度撕逼的段子,其实作为一个开发转的产品,对这两块都有所了解,就我的看法是研发效率不止是研发人员本身的技术能力和工作效率,而是整个研发过程和项目管理流程的效率,但我自己理解的高效的研发和项目管理有两个前提:
说到创业团队的研发和项目管理的实践,就逃不开先要说一下我们研发和项目管理中的工具作为背景:
最后切入正题了,本篇涵盖的是我们在研发过程和项目管理流程,以及当中在DevOps上做的一些努力去优化开发人员的体验,就试着从各个环节总结一下,因为不同团队的研发流程和项目管理都不一样,各位看官可以挑有兴趣的来看:
对新的开发人员,一般都会有开账号,装系统,配环境,跑代码这些过程,我自己发现每次都低估这些工作的耗时,以前就发现有时候不小心就一两天过去了还没跑起来代码,一两周还没搞清楚目前产品的功能,我总结了两点加快这个进度的方法:
1.加快能让代码跑起来的速度
有很多可以加速的环节,一个比较重要的就是自动构建代码,就是指开发人员checkout代码后通过简单的构建脚本就能完成代码依赖安装,代码编译,单元测试运行,也就是我们常说的跑起来。以Web为例,可以通过npm的脚本完成npm依赖的安装,然后用gulp完成代码的构建和运行,这也是持续集成的基础。
2.对产品功能需求和目前进度的了解
在背景的里说的保持一个尽量清晰的需求定义的一个用途就在这,新的开发人员可以通过浏览产品的需求文档来了解产品功能,我们的做法是在Redmine上把“系统功能汇总(含已排期未完成功能)”作为一个Custom Query,将所有功能的PRD列出来,有两种视图可以选择,一个就是下图这样按照产品线,能看到每个产品线的功能:
另一个视图就是按照功能完成的时间来归类,可以知道以前每个版本都做了什么功能,未来有什么功能正在排期中:
目前对于Web开发来说,一般构建的过程中代码都会进行混淆、合并、CDN地址替换、CSS Sprite生成等等操作,造成在Dev服务器上调试很不方便,我们采取的解决方法是在web的Gulp构建流程中分不同的Build Target,本地调试使用未混淆的代码加本地搭建的Python环境,连接Dev数据库,方便Web开发人员本地调试。
首先最重要的就是代码必须用源码管理工具,我们一直用的Git。代码的查看和管理都在Gitlab上,可以查看代码,code review,合并分支,打版本tag之类的,不过Gitlab对开发者不是必须要用的,所有这些操作都能用git command解决的事情,有两点我觉得需要关注的:
1.怎么让开发人员高效的使用第三方库
项目开发的过程中去抽象公共组件,使用第三方库或开发工具都可以提高开发效率,但需要做好模块和版本管理,有时候碰到一个开发人员引入了一个不合理的依赖,或者学习成本陡峭的组件,每个参与开发人员都要增加学习成本。这个一般都是根据不同的技术栈有相应的一套工具可以使用,我们自己在Web、Python、iOS、Android上面都有自己习惯的选择,需要加新的组件或者替换正在使用的都可以一起讨论之后加入,以免发生重复或者后期的分歧。我们主要考虑的点有下面这些。
2.如何做新功能开发的代码管理
只要多人开发,而且多功能并行开发都避免不了要考虑如何管理代码,一般有Feature Toggle和Git Branching两种,目前我们根据自己的需求定义了一个Git brancing model,对于复杂的新功能建立feature branch来开发:
虽然我个人更喜欢Feature Toggle的方式,不过实践起来需要的模板开发和构建方式上的配合,不如Git Branching对开发来说上手更简单一些,暂时就没有更换,建议看一下Baidu FEX的<< Feature Flag 功能发布控制 >>去考虑自己适合哪一种。
对于开发人员来说,开发工作一般是围绕着具体的功能需求进行的,而背景中提到过的“清晰的需求定义”就是研发的主要输入,由负责的PM来主导需求(User Story)的状态更新,本节以一个功能需求(User Story)为例,先上一个时序图来说明单个功能在研发中的生命周期是什么样的:
从功能需求(User Story)的时间线上可以看出来其分为下面几个状态:
PM创建后协作编写需求文档(New) -> 需求确认(Confirmed) -> 开发中(In Progress) -> 待测试(Wait for test) -> 已完成,可以上线(Finished) -> 完成,可以关闭(Closed)
可以划分为需求确认,需求开发,需求测试和上线三个阶段:
对于需求文档的编写和确认,不同团队方式不一样,我在前一篇<< “聊聊针对异地团队的需求协作和原型、设计的评审” >>聊了如何通过怎么协作完成清晰的需求定义,我的理解是包括功能需求的前置条件和后置条件,用户流程和规则,完整的产品交互原型,评审确认的设计稿。下图为在Redmine上定义的一个功能需求(User Story)
在需求定义清晰后,开发前需要整个开发团队的参与确认任务的分配。任务分配的原则就是将功能需求对应的任务按树形结构分解,敏捷开发里的学名就是 “Work Breakdown Structure (WBS)” ,保证其中每个任务都是可以开发,并且是可以测试的。下图就是一个功能需求对应的任务的实例:
具体到其中一个单独的任务项(Task),里面会有它所属的功能需求(User Story),当前的状态,优先级,任务指派的开发者,任务所属的产品线(Web, iOS, android…),一个简单的任务描述的,所属的milestone,预计开发时间和结束时间,任务当前的状态和进度等等。如下图所给的就是一个Task的实例:
从上文中“需求在研发中的生命周期”的时序图上可以看出其对应的任务的生命周期是如何管理的,包括前端和后台之间的任务协作是如何完成的,简单来总结的话Task有下面几种状态:
新建 -> 开发中 -> 待代码复查(目前仅junior developer需要被code review) -> 待测试 -> 反馈 -> 完成(可以上线) -> 关闭(上线以后可以关闭)
开发人员主要负责的就是开发的同时更新自己任务的状态,看起来状态蛮多,如果开发需要每次登录redmine来改也确实蛮累,在实践的过程中我们引入了一下优化的方法:
为Redmine自定义一些Git hooks来更新状态。通过自定义git提交语法,让Git提交能 自动更新在Redmine相应的issue 上,既节省了更新状态的时间,又能保持一个干净的git logs。下面是我们定义的Git提交语法:
[component] Abc: (Issue #Id) + Message (Issue Status)
[component] 就是任务所属的模块,比如[ios], [android], [backend], [web],这和Jenkins的Build Job绑定,当有相应模块的代码提交就会触发相应部分的持续集成和交付。
Abc 就是操作的Action是什么,比如 Add, Mod(ify), Ref(actoring), Fix, Rem(ove) and Rea(dability) ,用来让代码提交信息的目的看起来比较清晰。
(Issue #Id) 就是对应的Task的Id是什么,为了将changelog和task绑定在一起。提交以后就会自动更新Redmine的Task:
Server端接口文档自动生成。在需求定义里可以将规则和逻辑写的很清楚,但在前端和服务端协作开发的过程中,如果服务端没有文档可能会经常被前端打断,询问接口具体参数的名称或参数类型,也是比较烦的事情,可以考虑用Documentation Generator在代码中添加注释来自动生成文档,我们使用的 “Sphinx” 作为Python的文档生成工具,用Python的推荐使用。
开发中的持续集成和交付。这个后面会专门来讲如何操作,具体的意义就是开发人员提交代码之后在Dev服务器上进行自动构建和发布,这样一方面每次提交都做Lint检查,有单元测试的做单元测试,降低代码最后集成的时候出现问题的风险,另一方面让PM可以尽早的接触到成品,尽早进行反馈。
当单个功能需求下面对应的所有任务都开发完成后,由PM进行测试和反馈,在确认与PRD一致后可以由PM更新为“待测试(Wait for test)”。这里“待测试(Wait for test)”的意义就是该功能需求可以在发布到测试服务器(Test Server),由业务团队及测试用户参与测试。当测试没有问题后,如果是Web功能则根据上线计划上线到Production Server;如果是Native App,则按照版本计划,可能需要固定时间发布或者等待几个功能完成后一起发布。
由于这里讲的是单个功能需求的研发周期,而测试和上线更多是在整个项目这个Scope上来讨论,所以针对测试和上线的部分在后面持续集成和发布的部分会来细说。
顺着上面的思路,当你有单个需求研发的流程后,整个项目的管理就是管理所有的需求,安排优先级和迭代计划,然后对所有需求进行同样的研发流程管理。敏捷开发里把一个迭代周期称为一个Sprint,每个Sprint做一次产品发布,然后回顾Sprint内的问题,规划下一个Sprint的开发任务,如下图:
我们的实践不完全是Scrum,但比较接近,我们的迭代周期为一周,保证每周至少都往Production上做一次同步。项目的进度管理在Scrum的实践里其实就是在它的三个Meeting时完成的:
在产品体验的优化中有个理论就是在所有直接接触用户的 ‘Touchpoint’ 上进行体验优化,其实我个人觉得这三个Meeting就是项目进度管理里的Touchpoint,在这三个Meeting上PM会和开发人员或者Product Owner进行接触,如果这里体验不好就会影响项目的管理。其实我们总结的优化方案也比较简单,就是通过项目管理工具Redmine去实现的功能需求和开发任务的 “看板” :
平时积累下的需求我会建立一个Future Milstone来存放,这样在Planning Meeting上可以直接以这些作为Product Backlogs,作为产品以后可以去做的内容,这些需求可以按照功能模块来组织,然后在Sprint Planning Meeting上一起规划出下一个Sprint要完成的功能:
每日的站立会议是粒度最细的会议了,就是追踪每个人每天的任务情况,在这里我们在Redmine上建立一个叫“本周需要完成的任务(开发人员)”的Custom Query,将这个Sprint里的任务按照不同类别( 网站,后台管理,iOS或者Android)来归类,作为我们的看板:
对于开发人员,只需要按照前面提到的提交代码来更新任务状态,完全不需要额外的工作就可以汇报自己每天的进度。每天早上一上班,所有的开发人员聚在一起,按照不同的类别一个个过任务项,同步昨天完成的任务,确定今天的任务,有疑问的就在早会解决。
对于项目进度Review来说,Scrum的看板管理是将任务项按照状态来分类,这样能更清晰的看出来哪些已经完成,哪些还没有开始,可以通过变换Custom Query来实现:
不过Sprint Review Meeting一般就不只是研发团队参与,为了辅助相关的业务人员和测试用户一起来Review,我们通过在Redmine上建立一个叫“本周已经完成的任务(业务人员)”的Custom Query准备上线的功能,里面是这个Sprint已经完成的功能,将已经完成功能的按照来归类,PM可以按照这个来测试已经完成的功能,全部完成测试提交到测试服务器以后,相关的业务人员和测试用户也可以按照这些任务来试用,节省一下每次都要介绍更新了什么的时间:
当产品发布到测试渠道就是希望在正式发布前得到业务团队或内测用户的反馈,对比开发人员的测试反馈,业务人员和测试用户的反馈一般都比较抽象,就是问题描述不具体,环境上下文不清晰,没有复现流程,解决这些问题最好借助反馈辅助工具:
我自己的经验是每次发布前的测试都需要产品经理亲自来做,一方面确认发布功能的正确性,另一方面重新走一遍用户流程,确认产品目标可以达到。测试的方法比较笨,暂时就是通过Google Sheet维护一张测试用例的表,如果是移动端就每个版本维护一个测试用例表,开发版测试时会把表格分享给所有开发人员,每个人都可以遍历测试用例提交自己发现的问题。测试用例表的结构为:
一级目录,二级目录,三级目录,用例名称,优先级,前置条件,执行方式,操作步骤,预期结果,测试状态,测试备注,是否自动测试覆盖
前几年有一段为海外客户做移动产品设计开发和咨询的经历,这里面一个重要的痛点就是不停发测试版给客户,征求意见和反馈,但对于移动app来讲之前每次打包都需要打断开发人员,等待编译,改文件名加版本号,上传等一系列繁琐的过程,然后还经常因为客户没有装最新版而造成沟通时间的浪费,所以早期我们就开始着手建立持续集成和持续发布体系来避免这些问题。
我理解的一个完善的持续集成应该包括代码提交后的构建->部署->测试->发布几个阶段,可以看 ‘The Product Managers’ Guide to Continuous Delivery and DevOps’ 上这个图来理解:
在上文Redmine的Git hooks提到过githooks在持续集成中的作用,其实就是当Git Commit Message出现相应的模块,就会在代码提交成功后能在持续集成服务端(CI Server)触发相应的Server,Web,iOS或android端的自动构建,这是持续集成的基础。
这里面有个针对开发这边的优化就是尽量缩短自动构建的时间消耗,我们的Web编译就是个反例,优化前有10来分钟的时间,现在稳定在3分钟,还有优化空间:
部署分为客户端部署和服务端部署两种,就是构建以后要把可运行的代码发布到相应的服务器和手机端。
这部分在服务端和每种客户端都分为单元测试和集成测试,理论上来说能在持续集成的过程中执行测试,是对产品质量极大的提升,不过对团队的规模和时间要求比较高,一般还是按自己的实际情况来。
自我检讨来说我们客户端的单元测试这块做的比较少,自动化集成测试的话每个项目都只对主要流程做一下覆盖,这也是个从Linkedin早期开发流程里看到的经验,可以通过分析用户行为得到主要用户流程,然后先自动化测试这些流程。早期在Android开发的时候实践过,通过Jenkins+Spoon+Robotium可以在CI上跑多种不同的Android设备来对主要流程截图,实现多设备的测试,在CI的效果是这样的:
虽然效果确实不错,可以在持续集成阶段发现在什么版本或者分辨率下出现问题,但用Robotium测试写起来还比较麻烦,后来我在 Github上fork过一些优化测试的方法 进行优化,但如果不是非常长期维护的产品还是慎用吧。
持续集成后的持续发布是我们主要需要解决的痛点,发布的对象分别是给开发和测试人员的Dev版,给内测用户的Test版和给最终线上用户的Production版,发布的渠道又分为Web端和Mobile端,需要分别来考虑,一个涵盖上面所有的情况项目的Jenkins大概是这样的:
在之前Gitflow的图上有展示到,我们将发布的dev,test和production分为三个不同的服务器:
对于Production的正常的流程就是当Sprint Review Meeting之后,按照确认要上线的功能进行发布。这里我有个习惯就是发布的时候在CI上检查一下发布对应的redmine tasks,避免有不该被同步的内容。
需要注意的就是,Web的持续交付相对来讲比较简单和成熟,但Android端和iOS端处理起来都比较麻烦,首先就是对于Production版本,都不能直接发布到AppStore或者国内的一堆Android市场,能在CI上做的就是build好production以后自动在Git上打版本tag。然后对于Test版本的分发,可以考虑使用Testflight或者国内的蒲公英,绑定到CI上,可以集成好以后直接发布上去,然后通过Slack建立测试用户Channel,自动发送通知消息,收到通知可以直接点击下载安装。
关于 团队沟通 ,需求协作和项目管理三块的总结终于写完了,很久没码文字了,写出来感觉蛮生硬,很多地方没有说清楚,不过水平确实也就这样了。写的过程中还是想到不少现在的方法里面有缺陷的地方,正好给自己一个ToDo List,以后去优化。