05 March 2017

最近公司发生了一些事情,引发了自己的一些思考,这两天复盘了一下,总结一下问题。 先说问题(事故),主要问题有两个,

  • 最近一次上线,由于架构变动过大以及测试不严谨导致上线后问题多多又回滚了;导致服务前后工停了7个小时。
  • 另外一个是一直以来存在的问题:开发出来的功能bug率奇高无比,且总是遗漏了一些需求;导致目标一拖再拖。

针对前一个问题我想说下小公司的运维问题和上线流程。针对后一个问题,我想说说对开发流程方面的看法。

上线事故

针对这次上线事故,总结了下原因主要有下面几点

  • 过度重构,项目重构的过早或者重构内容过大。
  • 做如此重大的重构,并没有经过充分的讨论、评审、验证。
  • 测试不完善,缺少压力测试和单元测试。

过度重构

由于之前项目一直存在一些问题,我们怀疑是性能问题(但其实不一定是性能问题,后面谈到第二个问题的时候我会再回头说我为什么怀疑不是性能问题)。所以我们考虑做服务拆分,使用微服务架构,这样以后也便于各个服务横向扩展,以应付更大的并发;另外随着微服务拆分代码也按业务产分便于代码管理和维护。

这次重构的内容,主要由两方面

  • 1,使用微服务技术,具体采用duboox来做微服务底层框架;
  • 2,由于1的原因,我们需要把IOC容器从nutz更改为Spring(因为dubbo不支持nutz)。

从代码的变动覆盖范围上来说,除了工具类和数据层的model类,其他的基本上全改动了一遍,尤其是是控制层(action),服务层(service)。 架构的变动上由于上了微服务,由之前的两个tomcat(web)项目变成了三个消费者(都是tomcat(web)项目)项目和4个服务者项目,共计7个项目。而且也新增了dubbo依赖的zookeeper组件,缓存组件redis。本来还上了kafka,但是后面去掉了,原因很简单,在不了解的情况下贸然上这么多重量级组件,这不是挖坑,这是挖坟呐。

那么关于要不要重构的问题,这次就不讨论,毕竟已经重构完毕,讨论这个没有意义,后面我会简单提到这次重构的必要性,以后有机会再细写。 先说这次重构的过程,我觉得主要有以下几方面的问题。

  • 架构调研不够充分
    • 这次重构的技术选型是让我们一个技术很好的工程师做的,这个本身没有什么问题,小公司嘛,本来人力资源有限,不可能让所有人都花时间去做选型。关键是选型之后,没有团队一起开会讨论,分析了解这个技术(dubbo)架构的特点等各方面相关内容,比如可维护性、可扩展性、开发流程,以及所需要的支持组件。
    • 由于上面原因其他组员对架构不是特别熟悉以至于后面开发的时候,如果要在本地启动一个项目需要保证三个(producer)项目都正常运行起来;但是由于重构过程中,问题多多,频繁提交代码和重启、导致producer项目很不稳定,而由于大部分人都不熟悉这个架构机制,总是莫名其妙启动不了项目,非常影响开发(测试)效率。
  • 如此重大的重构在这时候进行是否合适,回过头来想想,我是持怀疑态度的。
    • 首先,业务还在进行不停的迭代,时间上并没有很充分的时间预留给重构。
    • 其次,如此重大的重构在公司目前的阶段是不可能得到很多的资源支持的,同样因为公司开发人力有限、业务在快速增长的原因。没有太多时间等重构完善再上线,而且对于公司来说,重构本身也持续了近个把月,需求压了太多,已是极限、
    • 窃以为这个时候如果要重构,最稳妥的方式是迭代式重构;仅仅举个栗子,不一定代表步骤一定要是这样,比如分几步:1,将ioc容器从nutz更改为spring,2,将代码按照业务进行拆分为不同的maven项目,让主项目用依赖的方而不是微服务的方式来组合。3,加入redis组件(其实是重构缓存层),4,上dubbo微服务。如果将这一次重构分为以上四期,我想出问题的可能性会大大降低。

工作流程问题

其实严格说来,我不认为这是个问题,对于人数不多的初创公司来说,能简化的流程多一分都是浪费。其实如果其他两个问题(过度重构和测试不完善的问题)能能做到80分那么我认为这部分即使存在问题也不会影响大局。 先说问题。其实上一部分已经提到过一些,就是如此重大的重构,没有经过团队充分的讨论、评审、验证。 这个过程可以流程少一些,时间短一些,但不能完全没有。如果这部分做得好,起码有以下好处:

  • 对于运维来说,可以提前了解需要新上的软件,可以提前准备测试、生成环境。
    • 可以事先定下来需要上哪些新的组件(zookeeper,redis),有时间提前了解、准备这些组件;后面测试为了搭建新的环境也花了很多时间。
    • 可以让运维人员提前了解服务之间的依赖关系(决定了服务的启动顺序),以及服务对外部的依赖(支付服务的白名单问题浪费了很多时间)。
  • 对于开发人员,可以提前了解到新的开发模式,不至于在开发过程中被一些很基础的问题浪费时间。
    • 可以提前明确要拆分为几个服务,不能重构的过程中突然心血来潮觉得某个服务应该拆分就拆出来了。
    • 了解新的开发模式,不能因为中间某个微服务不能提供正常服务而影响了开发和测试。

测试不完善的问题。

先说结果,这次上线最后失败的原因是因为,有个消息队列,只写了生产者,而没有提供消费者,导致队列一会儿就被填满,导致系统在队列无限等待进入无法响应状态。这个问题如果使用压力测试有针对性的进行压测是很容易看出来的。 请注意这里使用有针对性的压力测试,也就是说发现了问题再有针对性的压测是可以很快解决问题,但是如果要事先进行压力测试进行预防其实是需要花费很多时间整理压力测试的用例的。

当然不能因为说这次问题发生在这里,就觉得其他两个步骤不重要。即使这里不出问题,也许某个时刻也会在其他地方出问题。

接下来说说单元测试的问题,其实写不写单元测试在程序员眼里就跟看哈姆雷特一样,一千个人有一千个说法,但总结起来,无非两派,一派坚决写单元测试,认为写单元测试乃是程序员必备的品德,不写单元测试还好意思自称程序员!?另一派觉觉得写单元测试太浪费时间,尤其是在小公司,十有八九不写单元测试。毕竟加上单元测试,工作量就算没增加一倍,也至少有三分之二吧。

  • 其实,任何事情都有中间做法,单元测试也不例外——针对部分功能写单元测试即可。比如频繁变更的功能、算法复杂的功能;最好写单元测试。因为这些功能容易出错,而且自己不容易测试出来,尤其是有的场景需要各种类型的测试用例数据,仅仅造这些测试数据都够头疼的。那么针对这些功能最好有单元测试,否则功能质量很难保证。
  • 对于一些逻辑简单,不怎么变化的功能,单元测试的可以等有时间再补上(这么说其实一般补不上了,不要悲哀,时间没有完美的事情)。

开发功能bug率高和与需求不符合的问题。

之前有同事反映从测试结果统计来看,bug率非常高;而且经常出现做出来的功能和需求不一致的情况。

对于这两个问题,在我看来都是开发流程的问题。

目前的开发团队是这么组成的,

  • 1个开发leader主要负责和需求部门沟通需求、评估时间、分配任务,以及开发一部分功能,以及负责大部分的系统运维(主要是针对系统出现的一些问题进行定位和解决)
  • 两个专职开发人员根据需求部门出的需求,主要包括word文档和项目管理工具上的需求描述,进行开发。
  • 还有一个开发人员有另外项目的一些工作。

那么从人数来看,其实是极小的团队,原则上不应该出现沟通的问题的。那么是什么问题导致开发人员最初来的东西总是不符合需求呢。 我觉得主要有两方面,一方面是开发人员自身问题,一方面是开发流程也值得商榷。

  • 开发人员不擅沟通,闷头苦干,开发功能的时候没有花太多时间和需求人员进行确认。这是很有问题的。开发人员跟电脑打交道惯了,大都比较孤僻,比较不爱和人打交道,其实是很有问题的。除非有什么流程可以保证你做出的功能和需求匹配并且bug很少,否则多喝需求人员沟通非常有必要。
  • 开发流程上面,一般是开发leader跟产品人员沟通完毕后,把任务分配给组员,针对一些内容进行交代和说明。然后开发人员进行开发。
    • 其一,个人觉得开发人员需要参加需求评审,开发人员应该尽量早的了解需求,以便对需求的上下文有所了解。这样对需求会有更全面的过程,否则就是盲人摸象了。
    • 其二,针对需求上的一些疑问,也可以提前提出来并在会议上达成一致,也会避免需求确定以后,觉得需求不合理私自进行变更或者只和部分人进行商量,并没有周知所有人员导致大家理解上的不一致。
    • 其三,个人觉得开发leader预期开发更多的功能,不如多(组织组员或者自己)进行一些代码review以及功能完成后内部确认。通过代码review可以提早发现一些bug,内部确认可以避免开发出来的功能与需求不一致。虽然这样看似花费更多的时间在开发过程,但是后面运维和bug修复的时间省下来了。长远来看利大于弊。

总结

总的来说,小公司最大的优势就是快:流程少、决策快、执行快。 但是在快的过程中肯定会出现一些问题,那么可以有有选择的流程化,规范化,避免犯一些低级的错误。避免频繁犯错;学习一些大公司的经验。 但是切忌因噎废食,不要照搬大公司的流程,否则到最后,很可能别人的优势没有学到反而学到一堆大公司病。





Fork me on GitHub