首页 » 技术分享 » 怎样破解项目中的“死亡螺旋”困境

怎样破解项目中的“死亡螺旋”困境

 

    你在项目中有没有遇到过这样的困境——

    时间不足,团队新人较多,项目前期质量问题不时爆发需要加急解决,新特性排上日程又没时间开发……

    这些问题往往一环扣一环,如果处理不好,会出现这样的恶性循环——

    爆发的问题会越来越多 ==> 挤占特性开发升级的时间使日程越来越紧 ==> 加班使得效率下降质量降低==>出现更多的问题……   老员工忙于解决问题抽不开身去培训和带新人,新员工又得不到锻炼水平没有提升。项目不断延期,团队压力越来越大,这些环环相扣的问题就像一个下降的旋涡,最终会将整个项目推进失败的深渊。


那么,我们怎样破解“死亡螺旋”的困境呢?

我们以实际工作中一个项目经历来谈一下破局的思路和做法。


                                            1 项目中出现的问题

 项目背景: 安防行业,道路摄像头采集的过车和违章数据需要向本地交管部门服务器或上级服务器汇总,但这些服务器设备往往是由不同厂家提供的,传输的协议和标准又各不相同,于是从发送端到接收端就需要一个在各种数据格式之间传输和转换的过程。

常用的方式有:数据库(oracle/mysql/pgsql)、webservice、读写文件、FTP、http、tcp、mq(rocketMQ、activeMQ、rabbitMQ)、以及一些通用的国有标准。获取和发送数据是一种方式,接收和存储数据是另一种方式,为了使其与平台解耦,特意独立出来一个中间件来做这个收发转换工作,由C/C++语言开发。因为各处协议和方式不同,除了少数复用的例子,大多数都要为每个项目点单独开发。这些项目点分布在全国各地,每个地方的版本也不一样,比如最新发布的是3.6版本,而有的地方还在用1.6版本。而旧的版本可能存在一些已知问题。

出现的问题

(1)前期版本一些不完善的地方不时爆出问题,需要加急解决,特别是稳定性问题,如崩溃、卡住、延迟、丢数据等,占用不少时间,且问题层出不穷。
(2)团队人手不够,大力招人,短期内从四个人迅速扩张到十几个人,新人以Java为主,不少人不懂C语言,而且业务也不熟悉,需要尽快培养。
(3) 解决了一些前期的bug但还未来得及合并上线,不少项目点还是用的较早期的版本,存在一些已知问题,经常会打电话来求助,占用时间。
(4)软件亟待升级,并且总结了一些需要改进的特性,但是没有时间去完善,疲于应付出现的问题。

 


                                                  2 破局思路

    有思路才有出路。

    首先要确定事情的主次、因果,这个过程我称之为“开源节流”。开源节流,要搞清楚谁是源,谁是流(其实也就是谁是因、谁是果,谁是主、谁是次)。
 

    现在最大的麻烦是这些冒出来的越来越多的bug亟待解决。这些问题主要是版本质量的bug和一些前期版本已经解决但未合入主线的bug。但若是出现一个解决一个,这也是治标不治本。根本原因还是版本质量不足,需要尽快升级,否则后续会给更多的项目点带来问题,这个才是源。 而目前众多的bug看起来很急,其实都是流。这样就定下了思路——

(1)先将已经解决的bug补丁合入主线,之后由于版本旧而出现的问题直接使其升级最新版。 (开源)

(2)再集中精力解决积压的问题和新出现的问题(节流)

(3)后续对软件进行重构、优化,使其架构和接口更通用、更稳定。后来我们还开发了Java版本,以加快开发速度和稳定性,这是后话。(后续升级)

(4)新人培养,我们抽晚上的时间讲解软件的架构、作用、流程,下载分支代码指导并其看,并选一个简单项目让其在本地能够跑起来消除陌生感。给每个新员工分配一个简单的开发项目和一个bug定位,每个老员工带两三个新员工,让其边学边做,快速熟悉。后续每周两晚培训,形成了制度。


                                                        3 破局过程

(1)在第一天的晨会上确定了优先级最高的事情是统计已经解决的bug并开始合补丁入主线,争取当天完成。当然合入的代码都要抄送到全组review,每个合入的补丁能否达到效果还要搭建环境验证。

         还要记下每个补丁属于哪个版本的,出现这个bug时的“临床现象”是什么,需要收集哪些日志和信息来定位。再有因为老旧版本问题而出现的bug,让其直接升级到最新版。

(2)接下来集中精力处理积压下来的和新出现的bug。

     工欲善其事,必先利其器。

     我们首先对前面近一年时间里解决的数百个问题进行统计分析,确定遇到最多的麻烦是什么,有针对性地解决重点问题。经过统计,发现比重最大的是国家通用标准占40%多,环境、配置和第三方问题占30%多,而真正的版本质量问题并没有我们想象的那么多,占20%左右。

  •  

    针对国家通用标准,因为国家标准是统一的,而软件和接口是固定的,出现的问题大多是配置文件、运行环境没有配置好。

    于是我们尽可能全面地给出了国家标准类项目的配置流程与方法、日志错误码和打印信息、问题排查定位方法,每一步的操作之后该怎样去确认操作效果,对出现次数多的配置写了注意事项和自检项。之后将这份操作指南发给前方的技服人员和公司总部的运维人员,让其有问题先利用这份操作文件自检。这样,就挡掉了占比达40%多的内容的大部分。

  •  

    之后就是其他类项目的配置、环境 问题。

    我们仿照前一个方法,给后续的每一份操作文档都加入了注意事项与自检项,常见错误的定位方法,以减少因为配置问题而带来的时间耽误。

      同时我们也意识到操作界面不够友好,对界面进行了升级,使大部分配置能够在界面完成而不需要操作配置文件,加入了一些默认配置项,减少因为一些不痛不痒的配置而引起的警告或错误。

  •  

    最重要也是最值得关注的地方是版本质量问题。

    经过统计分析,这些问题其实90%以上都是因为没有遵守编程规范造成的。在定位之后,组内编码和review时着重加强了编程规范的要求。只要遵守编程规范,可以规避掉绝大部分莫名其妙的bug。而且要将这些要求刻意培养成习惯。只有将其培养成习惯,才能最终不需要记忆而自觉为之。

    常见问题:

    内存泄漏,是因为在部分分支中return的地方没有释放内存。之后组内代码review要求,每个分配内存的地方,都要将return关键字高亮检查有没有释放。后续升级为内存守护类和内存池。

    野指针问题,对象或内存析构之后没有将指针置空,导致后面判空部分出错。这在编程规范里有明确要求。

    死循环中没有定时释放CPU,导致CPU使用率过高,影响其他业务线程致使数据延迟。

    一个花了一天时间定位的概现的崩溃问题,是for循环里用循环变量操作下标。我们的图片最多是4张,而且是由平台自动统计的,理论上说用下标操作也没问题。但平台一个意外的设置导致平台部分数据数量变成了5张,于是就出现了访问越界。编程规范里也没有这一条,于是我们在组内编码时多加了一条规定,每个for循环都增加一个绝对不会越界的最大常数:
                                                             for (... i < x && i < MAX_VALUE ...)
,于是类似越界的问题再也没有出现过。

    用局部变量给线程赋值,或者多线程时没有考虑到线程安全问题的,我们对多线程编程整理了一个专题培训了一下,确保大家再看到“线程”这两个字时身体都会一激凌,绝对不会忘掉线程安全问题。

    对于卡住、崩溃等问题,加入了守护进程与工作进程定时通信发送心跳包,一旦几分钟没有发送,便认为可能是工作进程卡住了而将其杀掉重启。这是时间紧迫而作出的一个权宜策略,目的是减少项目点催促的压力,待解决积压的问题后再深入定位。

    变量名,锁操作,代码注释等等,主要是使大家养成一个良好的习惯,用习惯来减少问题。

 

(3) 新员工培训。

    刚开始时候我们也像之前那样,做个PPT,然后订个会议室带大家去从头到尾讲一下。可是后来发现这样效果不太好,大家听一听有个印象,如果回去之后没有立即用到,不出一星期基本上又忘了,即使做笔记也是一样,只能记住这期间常用的。

    后来回想起来我之前初中毕业时候去一个电脑学校学五笔打字。老师在上面讲解完原理和示例,每个学生面前有一台电脑,可以按照老师的示例自己直接上手操作,当时感觉这种效果不错,可以拿来试试。 

    于是讲解我们的软件流程的时候,我们下载了一个共享屏幕的软件desktopShare,在工位上讲解和演示操作教大家搭建一个webservice环境用来跑代码,让大家跟着搭建环境,直接让代码跑起来然后debug ,一步步地讲解代码流程,而且能够清楚地看到每一步产生的效果以及最终成功的现象,整个的软件就活了。后来有小组成员跟我说当时跟着这个步骤一步步跑通的时候,之前觉得很庞杂的系统不再是堆积的几十万行代码,而是我们所关注的一个很小的流程,对整个系统的陌生发怵的感觉就消失了。

    这让我想到了敏捷开发,也是在这样一个能够一直运行的系统上不断迭代的,感觉它们是一个原理,让运行的效果来实时地告诉你——这行得通, 大胆地往前走吧!

     后来培训linux下makefile的编写(不少新人是学Java的没有写过makefile),也是写了几个简单的.cpp文件,带大家一步步地升级makefile,在过程中强调升级的思想和并讲解语法,让大家在看的见的效果中不断进步。 

(4)代码review
    我们给每位新员工都分配了一个简单项目开发的任务,这样既分摊了工作压力,又锻炼了新人,但是这样做的风险也是显而易见的——代码质量不过关。

    因此,对代码质量的把控要求是比较严格的。每个人在代码写完10%之后,就要发送全组review。因为个人的一些习惯性问题,一种缺陷往往能在10%的时候就表现出来,在后续中还会重复地错下去。 所以在10%的时候就指正出来,有利于其之后减少相应的错误。等到代码全部写完测试之后准备交付时,再抄送全组review一次。

    另外,review的过程中特别强调风格要符合编程规范,来减少不必要的出错。

    组内形成代码review的制度,每个交付件发送前必须将代码抄送全组review,每月根据review发现的bug严重程度积分排名,对排名前三的同学予以奖励,形成了不错的技术氛围。


                                                      4 后续升级

    经过两周的奋战,困局算是基本解决了,但事情远未结束。

    如果前面出现问题的原因没有搞清,团队的漏洞没有弥补,那么迟早还会出现这样的问题。必须对原因回溯,亡羊补牢。

    (1)前面那么多期的旧版本已知bug补丁为什么没有合入主线?

    (2) 版本稳定性的问题占据了不小的比例,为什么早点没有一个简单易行的解决或规避方法?

    (3)复制性地开发这么多的交付件,有不少的地方都是重复的,为什么没有更通用的框架和接口?

  •  

    版本未合主线,是闭环意识不强。 之后要求一个版本实施完毕暂时没有bug的话就着手将其合主线,之后解决的bug证明有效性之后检查其他项目是否有类似问题然后当个版本合入,不能拖到下一个版本。

  •  

    版本稳定性问题,比如崩溃、阻塞等没有快速有效统一的解决方法是因为所想不多,在头痛医头、脚痛医脚,机械地应付每一个项目点的问题。之后在软件中加入了守护进程与心跳包,并引导大家出现问题时升级思维去想一些统一的解决方法。

  •  

   重复性的开发是因为架构和升级思维不够,同时也是一种自我满足的心理存在。在后续版本中加入了内存池、线程池、连接池等,并对一些常用接口进行了重构,加强了通用性,减少了代码编写量,自然也减少了出错的可能性。最重要的是,引导大家建立起一种迭代升级意识,同时要不断提升自己的技能水平,不能满足于当下。 软件重构升级的过程中,组长小胖带几名新员工扛住了所有的项目点支持任务,我们才得以集中全部的精力进行重构优化,团队的配合必不可少。 


                                                         5 总结

    一番苦战下来,看似涉及了很多的方面,其实真正贯穿所有方面的只有两个字——习惯。让大家建立好的意识并且形成习惯,编码的习惯,做事的习惯,升级的习惯,闭环的习惯,后面就会自然而然地这样做,否则,再怎么强调也只能是提的时候有效,一不提该怎样就怎样。

 

 

 

 


 题外话

  破局与带我们的技术经理的丰富的团队管理经验是分不开的,下面这个小故事对思路也有一定的启发:

 

     人生最重要的有三个方面:财富、健康、爱(包括人际、爱情和友情)。如果某一方面出现了问题,它就会逐渐地影响到另外两方面,并且螺旋地降低,使三个方面越来越坏。

    比如,如果没有了财富,就会降低在健康方面的支出和爱的方面的花费,而使另外两项降低, 健康和感情不顺,反过来将进一步影响财富的积累。

    要破解这种下降螺旋,就要先找到最坏的那一个环节,集中全部的精力来改善这一环节,那么就会推动它们螺旋上升,逐渐地正面影响另外两个方面,最终使一切步入正轨。

    比如,如果健康出了问题,影响到感情和财富。那么这个时候就要集中精力来改善健康问题,之后财富和感情也会丰富起来。

转载自原文链接, 如需删除请联系管理员。

原文链接:怎样破解项目中的“死亡螺旋”困境,转载请注明来源!

0