引子
9年前我入职一家公司,团队里都是之前公司的原同事,彼此都很熟,对各人的能力也都很了解。我当时负责整个公司的搜索引擎。上班第一天,我在看之前的遗留代码。原同事过来问我:“你是打算用这个老系统改造还是重写?”我笑了笑说:“我还是重写吧。” 原同事也意会的笑了笑说:“我就知道。”当时我们都多少带着些技术高人一筹的傲气。而我那位同事成长的更快,我们第三次做同事的时候,他整个人更加成熟谦虚,而那时我还在路上。
9年来我再也没有接手可以毫无负担,直接推倒重写的代码。就算有,不搞清楚以前的逻辑和背景,就直接抛掉这些历史包袱是不对的。在修改别人写的代码的时候,我们需要信奉黑格尔的名言:“存在即合理”。一定要弄清楚之前这样编写代码是出于什么样的考虑。
项目背景
这段时间我们团队在修改之前的一个功能。在我接触到这个项目的时候,设计方案已经被讨论了多次,已经到了详细设计的阶段。在我视角需求是这样的:
就是一个查询接口的改造,改造前代码逻辑被前人做复杂了,这次一些从下游拿数据来拼接返回值的逻辑可以改成从下游(数据基础服务)简单取部分数据,另外一部分写死。
听起来是不是很简单。这么一件事,总有也就几百行代码的开发量。有两个团队领导分别做项目经理和技术经理,由领导亲自做的设计方案;我作为团队架构师也被指派亲自负责查询服务模块的开发;一名一直做基础数据服务的同学做基础服务部分的改造;一个同学专门负责白盒测试;一个同学负责黑盒测试;还有一个对之前逻辑了解的同学负责方案评审和投产步骤编写。
能看得出来这个功能重要且有其特殊性。引起了高度的重视。因为这是修改之前几年前编写的几经易手、十分核心且之前没怎么敢改动的代码。
详细方案设计
在别人写的代码上做修改,做详细设计时,第一步要做的是充分评估改动影响;第二步是画流程图梳理改动前后的调用链和数据流,列出修改点;第三步是定好测试关键案例,确保结果的正确性。
评估影响
出现故障,第二要做的是什么呀?是止血。那第一要做的是什么呀?是评估影响。
要开展一个新项目,第一要做的是什么呀?是规划目标。那第二要做的是什么呀?是评估影响。
做方案设计,第一要做的是什么呀?是制定目标。那第二要做的是什么呀?是评估影响。
一言以蔽之,评估影响是在任何行动开始前,除了制定目标之外最重要的事。在很多方案设计中,往往没有将这一步规划到明确的流程中去,草率的实施,是日后出现问题的根源。
具体要怎么做呢?举个例子来说,之前做过很多http接口,常有需求说要在返回值里添加字段。很多刚刚出入编程这一江湖的新人,会觉得添加字段还能有什么影响,15年的老江湖告诉你:大错特错了!
添加字段,首先对容量可能会有影响,需要额外的日志等存储空间,占更多的带宽;其次,下游有可能有校验。所以评估影响重要的一步是要确认影响。和所有的调用方沟通确认,确认没有影响再进行下一步。
逻辑梳理
从这一步做的好坏,我直接可以判断你的高考分数。在本周答辩会上,在对我的提问环节。HR小姐姐说不是单单问我,要问我们在场所有人一个问题:“代码都读过了,为什么有些人还对逻辑不清楚?”其中一个架构师回答到:“就是你上学的时候读鲁迅的书和现在读鲁迅的书的区别。”
其实我想说:“治学三境界了解一下”,但是想想为这句话我要解释两分钟诗词,在述职评分现场,肉眼可见的在拽,岂不是在给自己减分。所以我选择了沉默。这里自己的地盘提一嘴。
晚清国学大师王国维在其不朽之作《人间词话》中曾用形象的比喻提出了治学的三种境界或说是三个过程:
古今之成大事业、大学问者,罔不经过三种之境界:“昨夜西风凋碧树。独上高楼,望尽天涯路。”此第一境界也。“衣带渐宽终不悔,为伊消得人憔悴。”此第二境界也。“众里寻他千百度,蓦然回首,那人却在灯火阑珊处。”此第三境界也。
第一境界表达的本意是高瞻远瞩,立志高远。在读代码这件事上,可以理解为了解基本框架结构和代码基本实现的功能。第二境界是刻苦钻研深入的过程。第三境界是顿悟,了解之前梳理中没有想明白或忽略的细节或问题。
而我们动手改别人代码之前,至少要做到第二境界。一个可用工具就是流程图,将每个步骤对数据做的转换,并标识出每一步数据格式。
最后,总结一下修改点,方便形成测试案例和checklist。
制定测试案例
在评估影响和逻辑梳理时,关键案例其实已经出来了,这个阶段是个整理阶段。同时,也是从另外的视角,看看是否能达到“蓦然回首”的境界,补齐之前逻辑上的疏漏。
以上三步完成之后,就是设计方案评审阶段。千人千问,多视角审视方案,也增进理解。
编写代码
在写代码之初,自认对代码做了深入的分析,加上15年代码编写经验,觉得自己写这段代码岂不是降维打击。结果代码提交之后,真的是被打击了。Code Review同学直接在群里说给我找出来7个问题。开会的时候,其他同学也开玩笑的提了一嘴。就这么被年轻同事弄没了排面,虽说知道格局境界要高,心里也确有不爽。关键是他提的7个问题,他提之前我都有认真思考过,代码是刻意为之。
后来我们就语音沟通了一下这些问题,虽说有些我还是不认同,但是也能明白他提的问题的道理。
有一条,是我新定了一个错误码,我的思考是是这个查询接口非常重要,希望出现问题和其他系统做区别。而这是我们内部错误码,外部错误码没有变,所以不会对外部产生影响。而Code Review的同事说出了我之前没有了解到的信息:他之前为老错误码单独做了监控。我新定义的错误码,监控就不生效了。
另外一条,说我缺少非空判断。这个非空我是加了的,底层加了非空判断。逻辑是没有问题的。但是他觉得代码上层不加,语义上不连贯。我觉得逻辑应该内聚,自己做好的事情不应该让上层来做。这种问题,我统归为风格问题。每个人写文章的思路是不同的,写代码的思路也是不同的。别人觉得那样更好理解,其实换一个人就不这么认为。《有效的Java》这本名著,现在很多理论在被啪啪啪打脸。所以我遇到这种问题的时候都是不愿意纠结的,我Review Code别人代码的时候也从不去纠结别人这种问题,我只说自己的考虑,别人是否接受我都不会因为这个把别人代码打回去。这里Code Review的同事纠结,非要我遵从他的思路,我不同意改,也觉得没有争论的必要,我提出加个注释作为妥协,结束这个争论。
其中最重要的一条,涉及一个日志打印。结构化日志的打印,整个工程用了前人写的一个“轮子”,在jar包里不好改。改了怕影响太大。因为使用的日志,日志涉及其他两个非常重要的功能。这两个功能要借助日志分析,用户来进行自动操作。所以我的处理方式是新定义了一个模板,来确保不影响原有功能。Code Review同事让我将共用模板改一下,不要新建模板,模板多了不好维护。我的担心是上线排期非常紧,老逻辑没有人彻底清楚,之前的测试用例并不完善,所以求小心。而Code Review的同事说没问题的,出了问题他承担。真要出了问题,上面一层层的扛着担子。我也责无旁贷的。不会落到他身上。我也不建议他这样的保证。后来,我自己想了一下,如果用两个模板,两个append同时写一个日志文件,之前也没有这么用过,也有风险,所以还是按照他说的改了。但是开会Diff代码的时候(上线前将上一个版本的代码和这个版本的代码做比较),我开玩笑还提了一嘴,说同事说了“出问题他承担”。其实是隐含的劝诫一下,这句话有些慷他人之慨。其实本质上我同事的意思就是:“我和你一起保证修改的正确性”。用心是非常好的。
最终提的7条每条我们都争论了,那是因为每一条我们两个都真正思考过。这种氛围我觉得是非常好的。
测试
我的代码CodeReview的同事有找出来问题,专业的QA白盒测试和黑盒测试都没有发现问题。这个和我预期的一致。因为在编码阶段,不仅我自己用心了,CodeReview的同事也用心了,没有问题才是正常的。这也应该是编写提交后最普遍的结果。因为一旦问题让测试发现了,那这通常只是冰山一角,底下会隐藏更多的问题。
后记
道、法、术。做任何事情的道理都是一样的,用心是第一位。《山河令》里体现用心的地方很多。其中一项就是留白。
比如温周二人在龙渊阁掉落谷底,面对药人的围攻。周说:“得君为友不枉此生”。温言:“幸得君心似我心。”很多人都知道:“幸得君心似我”心出自《卜算子·我住长江头》,下一句是:“定不负相思意”。在生死存亡之际,下面一句要是说出来,马上共生共死的兄弟情就变质了。所以情商高的周接了一句:“听你念诗我头疼。”面对死亡的坦然乐观,情绪就烘托出来了。
再比如,片尾曲《天涯客》里,一句歌词是“把古道西风瘦马换小桥流水人家。”这也是个留白。《天净沙秋思》之前教儿子写作文的时候,我教过他:“你想把本来可以写100字的作文写成400字可以先罗列一堆景物描写,最后一句才是你真正要表达的内容”。而《天净沙秋思》最后一句是“断肠人在天涯”正好对曲名的天涯客。
编程和其他事情一样,用心是出好作品的关键。
推荐阅读