版本 | 日期 | 备注 |
---|---|---|
1.0 | 2022.8.3 | 文章首发 |
1.1 | 2022.8.10 | 根据反馈改进内容 |
1.2 | 2022.9.11 | 修改标题 |
0.前言
最近接了几个项目,代码质量参差不齐。本想按照以往的实践建议每个patch都做review,但往往dead line当头。我会想,我如果对这次patch做了review、以及长期做review能给业务带来什么价值。接着本文,我还想讨论其他技巧在coding中也能够明显带来价值。
注意,这里的价值指的是可量化的价值。常见的说法可能是有利于系统长期的可维护性、稳定性等,但无法给出相关指标。但我们需要知道,数据思维要优于经验沉淀
。
1. 时期最重要
时期很重要,根据不同的时期,我们会选择不同的生活,也会选择不同的投资策略。技术也是,之前听王志猛的分享,给出了一个很接地气的技术发展阶段:
- 能用
- 好用
- 卓越
在商业模式或者业务价值被验证之前,最好是追求速度。这就像你饿着肚子要去打猎,你不会在意这把武器有多漂亮,而是它能不能帮助到你打到猎物避免饿死。这就是能用的阶段。
CodeReview往往发生在好用和卓越的阶段。这个时候项目的价值已经被验证,需要配合业务发展进一步的迭代。这里也可以拆分细说:
- 好用即完全满足业务需求。
- 卓越即在完全满足业务需求的基础上,能够用更少的成本。无论是从业务还是技术的角度做到行业顶尖。
而CodeReview的价值就是从好用开始产生的,这个时候往往关注如何稳定的迭代业务。而到了卓越时期,CodeReview显得更加重要——这个时候承载的业务往往是有体量的,需要更加注意业务的稳定性和连续性;同时团队规模也在扩大,需要进一步关注降本增效。
2. CodeReview的双刃剑
我自己是参与过很多CodeReview的,在ZStack每个patch都要找相应的maintaner review以后才可以合入主干,总体Review的粒度也很细。就对个人而言,我很多的Coding技巧就是在那个时候学来或者收到启发的。但这具有一定的成本——我举个例子:比如有一个功能开发了3天,测试测了3天也测过了,现在进行主干合入,maintaner看了半小时,提了10个comment需要改进。那么开发就需要去修改并测试(花了1天),并再次配合测试进行测试(测试花了2天),最后主干合入,那么就比原来的6人天多出了3人天,50%的人天增长。
或许我们可以聪明点,搞个develop分支:那么就是开发提交代码,review后再次修改并自测,然后再提交合入,交给测试测试。但总体来说,还是带来了研发“额外的成本”,maintaner往往是业务的主要角色,他的时间也很宝贵。
在沃趣时,项目迭代火热时一天有10多个patch需要我review,于是我一天都在review,后来慢慢的把不同的项目、模块拆给不同的负责人,这种情况才好了点。
假如你正在考虑引入CodeReview,并且的确是在好用到卓越的过程中。那么我觉得首先要和上层达成共识,对于这额外产生的人力成本如何进行成本计算。当初在ZStack CodeReview由CEO张鑫直推,因此我不清楚那时是如何显式量化CodeReview带来的价值。但据我所知,一些大厂都有相应的研发效能洞察平台,会根据大量指标来衡量研发效能,本文谈到相关方法得出的指标可能只是其中的九牛一毛,考虑到控制篇幅,不再展开。
而对于引入的CodeReview里的实践规则来说,我认为重点是关注宏观上的设计。举个例子,假如我们写一个复杂的业务平台,而不是简单的CRUD业务(比如图书馆借阅系统),用“经典”的三层架构,那么无论你的代码细节规范抠的多好(减少if else技巧,每个函数不能超过x行之类的)最终都是难以迭代的。而当我们借用了整洁架构的思想后——无论是DDD还是六边形架构或者是SOLID、设计模式、经典编程思想,我们会发现相比那些糟糕的小细节,保持宏观的整洁才是最重要的。
令人难过的是,很多自动化检测工具都是关注微观的细节,而能够关注宏观整洁的工具少之又少。这是可以理解的,不同项目对于宏观整洁的要求是不同的,众口难调。
举例:我们的住处往往区域明确,厨房里摆着餐具,卧室里有床铺。即使厨房里的锅或者铲子放的位置有点乱,也不是难以接受——这就是宏观上的整洁,微观上的小糟糕;而如果我们厨房里叠着一个干净的床单,卧室里有一个锅,就算他们摆放得再整齐,这也违反了宏观的整洁,即使在微观上来说他们很整洁。
另外一个简单暴力的手段则是白盒测试的覆盖率,尤其是单元测试。能够轻易编写单元测试的代码相对来说比较松耦合,因此将白盒测试的覆盖率作为review的考察重点是个不错的选择。
3. 如果不写代码就可以不Review了
正如小标题所说,既然review代码代价这么大,那干脆不写代码了。比如说我们可以用DSL。
这种做法其实非常常见,做过大数据的都知道MapReduce或者Strom,编写它们要关注大量的细节,比如数据如何shuffle,如何保证可靠性。而如今我们用FlinkSQL则完全不用关注这些底层的细节,我们只需关注数据从哪来,怎么做业务处理,然后落哪儿去。这就是老生常谈的声明式编程,而不是面向过程编程。
同样的思想,也体现在K8S的声明式API上,写好配置声明然后提交,就等资源创建就完事了。倘若你创建一组资源,需要按照其依赖先后调用,并关注里面的状态细节,这样的易用性显然是差了很多的——就像OpenStack,你要创建VM前有一系列的事要先准备好。
低代码平台也是同样的思路,通关拖拽的方式形成UI相关的代码,甚至可以定义一些简单的逻辑在里面。这样形成的代码自然不用Review,要做优化也是低代码平台里的代码解释器来做。
以这种方式来考虑业务价值,其实是非常显而易见的:让一个普通的开发去写一段SQL可能只要10分钟,而用代码写一个对应的数据处理任务可能要半小时起。
4. 写好代码并非一无是处
这么说起来,追求完全干净整洁的代码对于业务来说似乎没有那么重要。从业务角度来说,是这样的。但总有一些核心的代码、经常被大家使用库、框架需要干净整洁的代码与API设计,它们往往位于项目的底层,但却默默的支撑着在上面发展的业务。这就需要CleanCode,哪怕少传一个参数,少一行代码,普惠的则是所有的使用者——这种价值往往体现在降低成本上。
5. 小结
本文讨论了笔者在coding质量上走过的路,针对CodeReview剖析了引入时间以及重点关注项。并指出了更容易被量化价值的声明式编程是一个不错的选择。
针对绝大多数的业务代码,需根据时期来权衡CodeReview的程度。上面承载的业务就像我们出去打猎时的武器一样,商业战场就像一个丛林。如果我们无法用武器打到猎物——获取商业上的成功,那么我们迟早被饿死。至少得在我们不被饿死的情况下,再考虑武器的顺手程度,甚至那天心情好还可以雕个花在上面,让它像个艺术品一样。
追求代码的极致并非是无用的。那些核心库,框架就需要这些技巧,因为把它们写好的同时,就已经能给上层的业务带来更多的价值了。