一些共性实践
按照通常的产品研发过程,我们大约要经历如下几个阶段:需求 -> 设计 -> 开发 -> 测试 -> 上线 -> 维护,下面针对这些阶段分别提出一些建议 (着急的同学可以直接跳到最后的归纳部分,对感兴趣的内容再跳回来读 )
需求阶段
- 需求文档(Recommended):在我过往的项目中,以及周五会议的总结中,我都看到有不少问题是需求和实现不一致造成的,因此这里我的建议所有的需求(产品需求 & 运营需求)都有一份文档描述。这里有几个细节:
- 格式:格式本身我觉得不是最关键的,一般来说建议复杂的产品需求有详细的PRD出来,简单需求也建议有wiki文档,或者至少可以在task中看到清晰 + 准确的描述,考虑到方便追溯,不建议用邮件来写需求文档
- 质量:原则上建议所有需求都是经过产品/运营+技术review过的,这有两个好处,1是通过review过程中的讲解和问答保证大家对需求理解的一致性,2是通过review集思广益,避免考虑不周、产品上线后再临时紧急处理。实际研发过程中,我们有很多需求并不是很大,这里一个formal的review就显的有些过重了,这种情况下,一般是产品/运营和RD直接沟通,这里我建议的一个小技巧是,PM、运营讲完需求后,再请研发同学复述一遍需求,我过去通过这个小技巧发现了很多需求细节理解不一致的问题
- 文档管理:我的建议是所有的需求都是可以追溯的,不管我们的文档是PRD,wiki,task,建议有一个统一的地方归档所有的需求文档以及相关的review记录(如果有的话)
- 变更管理(Recommended): 我看到的另一类常见问题是,需求刚开始大家的理解是一致的,但是随着项目的进行,需求变更不断出现,理解不一致的现象也开始出现了,建议:
- 周知: 建议在项目一开始的时候就明确哪些人对这个项目关注或者会被影响到(干系人),需求有任何变更都需要及时周知大家。特别地,任何确定的需求变更都应该第一时间周知到RD,很多质量问题的源头都是last minute change
- 变更控制: 变更是不可避免的,但这些变更应该是受控的变更。互联网公司强调小步快跑,不断迭代,但这些变化都应该是有规划,有节奏的。我经历过的一个项目,关键的排序算法几个月内若干次大幅修改规则,不但产品出了一些质量问题,同时也给用户带来很大困扰。我的建议是:凡是大的产品变更一定要review,小的变更(紧急情况除外)做好规划,定期批量进行,避免随时有想法随时变
- 衡量产品的数据需求(Recomended): 先讲一个我经历的具体例子,我们一个用户相关的产品,开发,测试上线后一直看起来运行正常,直到有一天做另一项业务分析的时候才偶然发现有一个用户渠道用户量下降不少,仔细分析发现是一个Bug已经存在好几天了...由此引申开,参考业界的经验,我的建议是做产品的同时做好衡量指标,产品的数据收集、量化衡量相关功能本身就应该是需求的一部分,二者缺一不可。上述的例子,如果我们有相关的产品指标,并且每天在看,那么即使测试时漏过了问题,通过指标异常也能迅速判断出有问题了.
设计阶段
RD团队接到需求,理解需求后,下一步就是实现需求了,这里我从不同团队,不同项目观察到的一个并不少见的现象是,有些RD同学接到需求后,考虑一下后就直接开始coding了,然后我观察到导致的一些问题有:
- 代码上线后run的很好,突然有一天线上业务出问题了,一番手忙脚乱各种检查后发现是一些基本问题没做好,比如对一些外部依赖没有做异常处理(如超时处理),比如没有考虑热点数据,比如没有考虑冷启动的问题,比如没有考虑大量低效的重复调用在大压力下是否会雪崩...,这些问题如果在coding前有一个design review,都是可以事先避免的
- 代码上线后自己的功能run的很好,第二天上下游团队的系统出现异常,各种检查后发现是这个代码变更没有考虑对其他系统的影响,这类问题通过design review也是大部分可以规避的
- 代码写了很久要上线前突然发现设计有问题,性能不达标或者上线后相关迁移成本过高,不得不紧急重构、重做、延期...,这类问题也是design review可以解决的典型问题
- .....
对上述种种问题,我只有一个建议,那就是
- Design Review (Must): 这个是今天很多项目没有做的,但我强力建议一定把这个工作做起来,我在之前的工作中从中受益匪浅,具体地可能会有几个疑问:
-
- 形式:我依然认为形式不是最重要的,重要的是1 不能走过场,2 review中要邀请到合适的人参加,很多时候review我们会收到一些尖刻的反馈,但往往这些反馈对我们帮助最大。复杂需求可以有一个正式的设计文档,相关同事(根据需要可以请组外同事参加)一起开会讨论,这是比较formal的做法;小的或者简单的需求,可以相关RD在座位或者找个地方直接讨论
-
内容:不同的团队,不同的产品具体的讨论的点可能不尽相同。下面是我之前在风控团队和大家整理的一个list供参考,可以看到重心主要还是在各种异常的防范上
1)功能完整性(保证PM、运营需求完整的实现)
2)通用和扩展性(是否很容易扩展和复用)
3)系统可用性(本身HA的考虑,如果依赖的服务出问题的处理方式,如果超时或者返回默认值,是否会影响整个的业务系统等)
4)监控报警是否需要,如果需要是否完善
5)安全方面有什么考虑(接口需要防刷吗?接口需要对谁暴露? 接口需要做XSS,SQL Injection等安全处理吗?数据传输需要加密吗?...)
6) 资源消耗方面有什么考虑 (是否会有文件描述符的限制?IO、网络、磁盘、数据库空间等资源消耗是否需要考虑?)
7)...
-
文档: 设计文档往往看起来对当时做项目的同学帮助不大甚至是一个负担,但实际上短期看:写文档的过程往往是一个梳理思路的过程,通过把我们的思路准确、清晰、简练地写下来往往能让我们对设计的认识更透彻,更本质;从中长期的角度,这些设计文档往往对后面接手的新同学帮助巨大。强烈建议除了小于1人天的简单小需求,所有的设计都有设计文档;最后所有的设计文档建议和需求文档一起有一个统一的管理的地方.
-
内容补充:
-
业务、性能指标监控方案,这些应该在设计阶段就充分考虑,并在开发、测试、上线以及监控中保持关注
-
开发阶段
开发阶段主要有两个建议:
- Code Review (Must): 我们的代码在check in时, 都需要进行代码review,这个在今天几乎左右的团队都在做。但从观察看,有些团队这些review做得很细,有些团队则流于形式,所以这里再重复一下。我个人的经验,细致的code review至少可以减少20%的测试或线上阶段发现的bug; code review具体的做法各组都有自己的实践,我的几个建议是:
- coding style的检查不是code review,几秒钟,几分钟就完成的code review往往是低质量的
- code review一定、必须是对整个代码逻辑的逐行review,是一个细致到不能再细致的工作;我至今印象深刻的一个例子是和一些年前和一个日本工程师的合作过程,当时他是我们虚拟团队里面最资深的工程师,我们项目组的每一行代码他都会review,他会细致到每个函数的实现,每个变量的命名,甚至每一行的空格数都会认真检查,当时的过程整个项目组都很痛苦,但那也是我10多年工作中质量最好的一个项目
- code review是带着目标的review:通过分析我们现在有多少bug是code review阶段就可以消灭的,然后定一个量化目标,比如通过code review减少20%这类bug,然后在整个团队中带着这个目标去做code review,整个code review的质量一定会有大幅提高
- 最后也是最关键的,作为团队的manager/tech lead, 我们自己是否相信code review?如果我们内心真的对此确信无疑,那么我们需要想尽一切办法再团队内营造一个注重质量,坚决执行高质量code review的工程文化
- Unit Test (Recommended ++):
做与不做UT,这不是一个技术问题,这是一个工程理念的问题,不相信的UT的理念,会认为UT是额外负担,会降低开发速度;相信UT的理念会认为UT就是开发的天生的一部分,没有UT的开发就如同怪胎一般难以理解;不相信UT的理念,会在工作量评估时不估算UT时间或者单独为UT估算时间;相信UT的理念,会在工作量评估时自然地考虑其所需的时间,实际上他们甚至不会想到还可以不考虑UT来估算时间;不相信UT的理念,会在功能代码开发完成后就进入功能自测或者交给QA测试;相信UT的理念,在进入功能自测或者交给QA测试前他们一定已经对自己代码UT了数遍,事实上他们可能每天都在持续集成,代码已经测试过了上百次...
是否做UT,这个已经被无数公司(包括Google, Facebook, Amazon等一流工程团队)证明是开发过程中必不可少的一环,我个人也强烈地相信UT能大幅提高我们的工程质量,这里我强烈地建议我们在不论大小的项目中都一定把UT做起来。
过往我也看到推进UT时的种种问题,比如老代码没人敢动,比如有些UT比较难做,从工程的角度,一个可操作的建议是,作为技术团队的manager/lead,我们第一步建立UT的基线,也即我们今天的UT覆盖率到底是多少?然后再制定一个明确的可衡量的目标,比如下个Q提高10%,然后以终为始地推进,不论每个Q目标是多少,只要坚持每个Q,每个月我们都能前进一点点,我们的UT覆盖率会迅速提高
- ODC (Orthogonal Defect Classification) (Optional): 来自于IBM的一个理念,简单说就是从一系列正交的维度对bug进行分析,找到最关键的需要提高的点,有针对性性地对开发过程进行改进,这个操作起来略复杂,基于我们目前的现状,我觉得是optional,在建立起有效的bug跟踪管理的前提下,有兴趣的团队可以进行一些尝试
测试阶段
我们目前除了客户端外,各个团队没有专职的测试团队,但这并不表示我们不做测试、或者可以简单测试上线发现问题后再来fix;实际上,不论是否有专职的测试团队,测试都是开发流程中必不可少的一个环节,所有好的测试理念和实践对于我们目前开发团队自测的模式都没有例外,结合我观察到的一些问题,一些建议如下:
- 典型用户场景以及test case的梳理(Must): 测试的目的是保证我们的产品可用、好用,基于此我的建议是从用户角度出发,梳理出用户典型使用场景(比如登录、注册、找回密码、搜索、下单、支付、退款等), 然后从这些典型使用场景出发,整理出我们的test case,这里的test case既要考虑正case(正常的用户行为),也要考虑反case(安全、异常、边界、约束条件等);具体的实践中,各个团队可以先从最典型的test case出发,逐步完善,最后就能形成一套比较全面测试覆盖了。这些test case强烈建议在一个集中的地方保存(如wiki),并且作为团队新人介绍的一部分,从第一天就强调质量的重要性
- 搭建专门的测试环境(Recommended): 准备一个好的测试环境并不容易,尤其是数据的准备和同步,但工欲善其事必先利其器,要做好测试就必须要有一个好的测试环境,我之前也观察到一些上线没有做测试,原因就是没有合适的测试环境;还有些问题是测试环境污染了线上数据;因此,我强烈建议每个组都有自己一个比较完善的测试环境
- Do the test (Must):没什么太多说的,这个是必须要做的,并且需要按照#1整理的test case来做
- 集成测试 (Optional):这个依赖于具体的产品改动,如果涉及到对上下游不同系统的影响,建议及时周知上下游团队,做完集成测试再上线
- 性能测试 (Optional):这个也依赖于具体的产品改动,如果是如后台服务等系统需要考虑高并发、大数据的,建议做好容量预估、压力测试后再上线
- 自动化测试 (Optional):由于#1中的test case往往需要被反复执行,每次都手工执行既不scale,也很枯燥,对那些相对稳定的功能,自动化测试是一个很好的选择
- Bug跟踪整理 (Optional):从过程管理的角度,如果我们对测试中发现的bug都有记录,那么就可以定期review,进而发现整个团队在哪些方面可以做一些改进
上线阶段
完成了开发和测试,下一步骤就是上线了,结合我观察到的一些问题以及业内的一些实践,我的建议如下:
- 适当控制上线节奏(Optional): 这一条主要是针对后台服务以及PC和I版这两个端来说的,我们的Native App现在本来就有严格的版本管理; 我们的很多团队现在都是可以随时上线的,这不一定是什么坏事,这里只做一个参考性的建议;
- 业界有大量的互联网公司都在践行发布窗口的做法,也就是每周有几个固定的发布窗口可以发布,代码发布时,相关的PM、RD都会在线,保证有任何问题可以及时跟进(我们有同学有过上完线就去吃饭,出了问题找不到人的情况);这么做的主要原因是质量风险控制,因为不论我们对自己的修改有多么自信,理论上每次线上改动都有出错的概率,通过减少上线次数可以一定程度上减少出错概率,或者即使有问题也是集中出现处理,而不是任何时候都有不稳定的风险。当然按窗口发布,我们的代码分支管理也需要一些相应的适配。
- 从质量保证的角度,每次上线我们都需要做充分测试,这个是有工作量的,尤其是在没有自动化测试的情况下更是如此,集中发布可以集中测试,在一定程度上减少总测试工作量,但又能测试的更加充分
- 从实践的角度,我之前在风控、流量、网站后台等团队做过试验,每个团队不完全相同,但都是大约每周3到4个发布窗口(紧急情况例外),实际结果是RD团队并没有因此感到开发节奏变慢,同时测试相对却更加充分了.
- 灰度发布 (Recommended): 这个是控制风险的一个有效手段,也是业内非常普遍的一个做法,建议我们一定把这个做起来;有一个可能的争议是,我的改动很小,没啥风险,是不是就可以不走灰度发布了?我的建议是不论改动大小都走灰度发布,不怕一万就怕万一,那种觉得没风险但实际带来了预料外问题的case并不少见,而我们现在每个线上错误的成本已经是动辄几十、上百万的交易额影响了,灰度发布能有效地降低这个环节的风险;另外一个连带的问题是,如果我做灰度发布,那么会锁住其他人的发布,这确实是一个问题,我的建议是结合上面的一些推荐动作一起来做,具体是
- 控制发布节奏:这样即使有锁,也只在发布窗口有影响
- 设计环节就做好各种日志、监控、数据指标的设计:这样小流量发布时如果有问题能迅速发现
- 关键场景的test case梳理:通过覆盖主要的用户场景,迅速检查关键用户体验是否有问题
- 自动化测试:通过自动化测试,能迅速做完线上测试,减少锁定时间
- 代码仓库适当拆分:有了拆分,一些锁就自然不存在了
- 上线周知 (Recommended): 这个现在很多团队都在做了,一般是提前发预上线通知,上线后发上线通知,这个通知目前主要是PM来发的
- 线上测试 (Must): 不管我们在测试环境做了多充分的测试,产品上线后,我们一定要在线上再做一遍最后的检查,以确保万物一失,测试的范围还是按照测试阶段整理的测试用例来进行
- 产品验收 (Must): 这个基本各个团队都在做,主要是保证产品确实是按照设计实现了,需求被100%没有歧义地实现了。这里常见的一个争议是PM、运营要不要做测试?我个人的看法是PM、运营不但应该要验收正case(主要用户使用场景), 同时也要验收反case(异常、安全、边界、约束等),类比一下,作为父母我们的小孩出生了,我们肯定不仅会关心他/她是不是一个人这个基本事实外(正case),还会关心他是否健康(反case),是否有哪里可能会不太好(反 case)等等, 产品就像是PM、运营的baby,所以我觉得PM、运营是应该验收产品的方方面面的.
- 线上指标观察(Recommended)
- 核心产品/业务指标监控:如在需求阶段介绍的,如果在需求阶段就做了产品的数据收集和指标,并且在产品开发阶段做了相关数据的收集(至少是log),那么产品上线验收后,我们就可以观察产品指标,有时一些潜在的问题会在指标上首先反应出来。指标的观察,我建议PM、运营和RD都应该做
- 核心技术指标:上线之后通过zabbix监控观察涉及资源(db、cache、apache)的状态,比如某一个上线SQL索引存在问题,上线之后看一下cpu、io,可能极短的时间就发现问题回滚了;比如一些性能数据的Tp50,Tp90数据异常,都能在上线后迅速发现
- 异常日志:如果在产品开发过程中,在一些关键的业务逻辑,加一些观察日志,结合灰度发布,那么就可以在用户大规模反馈问题前,迅速提前发现问题了
线上问题处理
产品上线后就进入了维护或者新的迭代周期,在这期间,发现问题是不可避免的,针对我们发现的线上问题,一些建议如下:
- 线上Bug/事故定级(Must): 这个是会议上讨论的内容,定级的原则主要是从用户出发,根据对用户的影响(数量/比例)来决定严重性,同时级别也不建议分的太细,难以界定,具体的分级,自强和海峰还在讨论并会和大家讨论
- 线上事故期限内fix (Must): 借鉴Amazon的做法,根据事故的级别,分别定义一个必须修复的时间,当相应事故发生时,相关团队需要在期限内修复,如果因为一些特殊原因无法按时修复的,需要及时周知相关干系人,并继续紧急处理,直到解决为止,其间没有假期和休息(团队可以轮班)
- 线上事故的周知(Must):凡线上事故,不论大小都应该及时周知,一般是发生问题时及时周知,解决过程中定期update进展,解决后也告知大家,并做记录,具体记录者可以是处理问题的某个RD;
- 线上事故的总结 (Recommended): 各个团队根据事故的严重程度,决定哪些case需要进行case study,我的建议是严重的事故都应该进行case study,并有可执行的action来跟进。同时定期对线上事故的记录进行总结,产出质量报告
总结
就像游泳运动,要想取得专业选手的成绩,就需要在入水、划水、转身、触壁等每个环节把每个技术动作都做好100%准确,1毫秒、1毫秒地提高成绩;产品研发是艺术,也是工程,关于工程质量,业界已经有几十年的讨论和很多很好的实践了,只要我们找到适合我们的工程实践并且严格执行到位,我们就一定能取得专业选手级的工程质量。而且相对于研发的艺术部分(具体到各个领域的各种复杂的技术),工程部分的这些实践都是可操作,可复制的,只要我们真的重视质量,认真去做就一定能迅速做好,归纳一下前面的建议列表如下:
阶段
|
实践 |
Rating
|
谁R
|
注释
|
---|---|---|---|---|
需求 | 需求文档 | Recommended | PM或运营 | |
变更管理 | Recommended | PM或运营 | ||
衡量产品的数据需求 | Recommended | PM或运营 | ||
设计 | Design Review | Must | RD | |
开发 | Code Review | Must | RD | |
Unit Test | Recommended++ | RD | ||
ODC | Optional | RD | ||
测试 | 典型用户场景以及test case的梳理 | Must | PM或运营+RD | |
搭建专门的测试环境 | Recommended | RD | ||
Do the test | Must | RD | ||
集成测试 | Optional | RD | 对影响上下游的产品改动Recommended | |
性能测试 | Optional | RD | 对后台服务等承载高并发大数据的系统Recommended | |
自动化测试 | Optional | RD | ||
Bug跟踪整理 | Optional | RD | ||
上线 | 控制上线节奏 | Optional | RD | |
灰度发布 | Recommended | RD | ||
上线周知 | Recommended | PM或运营 | ||
线上测试 | Must | RD | ||
产品验收 | Must | PM或运营 | ||
产品指标观察 | Recommended | PM或运营+RD | ||
线上问题处理 | 线上Bug/事故定级 | Must | PM或运营+RD | |
线上事故期限内fix | Must | RD | ||
线上事故周知 | Must | PM或运营 or RD | ||
线上事故的总结 | Recommended | PM或运营 + RD |
Q & A
Q. 小需求是不是就不用写文档了?写文档太费时间了,我直接和RD一说就清楚了
A. 文档的格式不重要,但是建议大小需求都有一个文档,并且可追溯,写文档的目的不仅在于可以让当时的RD明白进展,同时也可以让未来接手的PM、运营、RD能了解产品的背景和来龙去脉
Q. 这么做是不是流程太重了?
A. 建议至少标记为Must的要做到,本质上这个还是一个我们怎么看待质量的问题,我认为时间、功能、投入资源这些都是可以讨论和变通的,唯有质量是坚决不能妥协的
Q. 质量和进度有冲突怎么办?
A. 与其上线一个有质量问题的产品不如不上线,建议做完所有的规定动作再上线,具体的冲突中,可以向自己的lead寻求帮助
Q. 团队人比较多如何在实际工作中保证上述的这些实践都确实做了?
A. 建议在发布环节做一个checklist,不论是谁,提上线申请的同时也提交一个check list,并确认其中的必做项和推荐项哪些做了,哪些没做,如果这个环节有虚报,那就是价值观的问题了
Q. 如果控制发布节奏,有PM、运营的临时或紧急需求无法发布怎么办?
A. 紧急的需求可以紧急处理,但反过来,如果每天都是各种紧急需求,那么就需要考虑需求管理环节是不是出问题了
Q. 团队的代码有很多老代码,如何UT?
A. 至少可以保证新代码是有UT的,对老代码可以逐步重构。UT的意义不仅在于当时可以避免错误,也可以避免未来出现错误
Q. ...
A. ...
PS:转载自之前总监的总结