浅谈遗留代码的重构

背景

《重构》诞生至今有近17个年头了,日常开发中大家谈到重构,要么非常随意,认为重构就是改代码;要么非常谨慎,把重构描述成焦油坑,像瘟神一样敬而远之。针对最具挑战性的遗留代码重构,有哪些需要注意的呢?

谈论任何事情,都该有它的上下文。本文谈论的技术背景是大型通信类产品,对于互联网产品不一定适用。另外,本文也不会涉及重构技术,有兴趣读者可以读《重构》或者《Effective Refactoring in C++》。

遗留代码重构决策表

遗留代码的重构属于《重构与收拾屋子》提到的“大扫除”或“装修”场景。对遗留代码进行重构,很容易形成“吃力不讨好”的局面,究其原因,我们先回顾下重构的目的:

  • 提高可理解性
  • 降低修改成本

这两点,无论从可验证性,还是可被度量角度都比较困难。如果项目仅以短期结果度量,重构成果很难自证明。再加之改动较大,可能引入一系列不确定因素,无功还有过,自然吃力不讨好。所以我们在进行遗留代码重构时要充分考虑收益和风险,收益尽量考虑可被验证、被度量要素,风险充分考虑成本、时间、范围等项目关注要素。在一个工程师话语权不是那么大的公司,这一点尤为重要。

基于上述场景分析,定义了遗留代码重构决策表:

refactor-decision-list

从重构带来的收益和风险两个维度,综合考量、打分,给出一个简单、可度量、易被执行的决策表。下面我们逐一分析下每条决策项:

收益

  1. 性能瓶颈

看到这条,你一定很不解:一些经验也告诉我们,软件的扩展性,常会牺牲一些性能;再看看《重构》书中一段描述:

为了让软件易于理解,你常会做出一些使程序运行变慢的修改

而更好的可读性及好的扩展性,恰是重构追求的,岂不是自相矛盾?

关于性能优化,我会在另一篇文章中详细阐述,我们先看结果,重构会给我们带来如下在性能方面的改善:

  • 结构良好的代码,在性能分析时有更细的粒度,更容易发现性能瓶颈
  • 逻辑清晰的软件,更容易反映软件业务本质,而清楚我们真正要解决的问题,对性能往往有意想不到的提升
  • 对软件结构的调整,使得对象及对象之间的关系更合理,可以大量减少内存浪费
  • 多核、分布式场景下,性能的瓶颈往往不是计算本身,而是不合理的调度,对软件结构的调整,可以从根本上解除该部分约束

另外,性能优化成果很容易被度量。

  1. 高危、高频故障

看到这条,你又开始不解了,重构是“在不改变软件可观察行为的前提下”进行的,而故障本身就是软件在特定场景下的错误行为,所以重构是改变不了故障本身的。那对高危、高频故障模块,重构的价值在哪里呢?

  • 某模块故障总是消灭一波,又来一波,攻不死,杀不完,一方面,说明该模块需求变化还是很频繁的,另一方面,说明模块设计出了问题,要么是逻辑混乱,要么是内部耦合太大,这些都可以通过重构来消除。
  • 重构的一个目的是“提高可理解性”,逻辑清晰、整洁的代码,使故障就像白墙上的苍蝇,很容易发现,解决。
  • 重构的另一个目的是“降低修改成本”,软件容易修改,需要软件遵循开放封闭原则,修改代码不影响原有功能,也就避免了增加功能、修改故障引入的新问题。
  • 故障数是一个容易度量的指标,效果很容易可视化。
  1. 新功能扩展困难

软件之所以需要设计,而不仅仅实现功能,一方面可以被复用;另一方面容易增加功能。新增功能困难,并非是无法增加功能,而是,增加功能需要改动很多代码,从而带来更多风险,更大维护成本。

重构通过对软件内部结构的调整,不断消除重复,局部化影响,使得新增功能对原有功能影响尽量小。

  1. 代码逻辑混乱,可读性差

编写易读、易理解的代码,并不像说的那么容易,因为它是反直觉的,它产生的价值不是对当下的自己,而是以后的自己或者其他人,需要换位思考。

简单分享下自己对编码认识的几个阶段:

  1. 实现功能,追求性能
  2. 考虑扩展性,增加功能比较容易
  3. 考虑易理解,维护代码比较容易
  4. 考虑易复用,除了自己,期望他人也可以用

重构对代码易理解性带来的收益:

  • 对代码重构的过程,是对代码所表述业务逻辑再理解的过程。
  • 易理解的代码,更容易发现业务本质
  1. 人员能力提升

这里的人员能力提升包括两个方面:

  1. 业务能力提升。重构过程中是对业务逻辑再理解的过程,通过一层层抽丝剥茧,我们也更了解业务本身。
  2. 技术能力提升。无论是重构到Clean Code,还是重构到模式,我们的抽象能力、设计能力会伴随着这个过程逐渐提升。

风险

任何一件事,当我们看到收益的同时,应该评估它带来的风险。对于遗留代码的重构,在动工之前,我们需要回答如下问题:

  • 重构的主要目标是什么?因为在重构过程中,难免会遇到抉择和舍弃,如果没想清楚我们的主要目标,容易摇摆不定或者迷失了方向。
  • 重构的范围是什么?重构最容易掉入的一个陷阱就是,重构范围越来越大,大到无法收手。
  • 重构的计划是什么?虽然重构过程中,有太多的不确定因素,极端场景下,重构的结果给当初认为的完全不一样,但我们确实需要一个时间盒,在它的约束下,我们更容易集中精力达成我们预期的目标。
  • 重构真的必要吗?有没有低成本的替代方案?虽然我们鼓励用技术解决问题,但生活中的确存在很多在研发来看很重要,从商业角度“然并卵”的事。

想清楚上面的问题后,继续考虑如下维度:

  1. 人员支撑情况

人是重构的核心资源,靠谱的人才能做出靠谱的产品。一方面,重构的质量、完成的速度依赖人,另一方面,重构过后代码的维护及架构的演进也依赖人。需考虑如下几个方面:

  • 重构要求不能改变软件的外部行为,我们还期望通过重构可以简化设计,缩小业务与实现之间的Gap,这就需要有熟悉业务人员。你可能会说:“业务全在代码里了,自己看不就行了”,说的没错,只是太累了
  • 严格按照重构手法,基本可以做到重构前后业务逻辑的一致,这就需要至少有人熟悉重构技法。
  • 高效率来自专注,如果不能全身心投入,或者任务不断切换,结果往往劳力又劳心。
  • 团队中有Tech Lead,不但可以帮助提升团队重构技能,在团队产生技术争执时,还可以进行裁决。
  • QA是团队交付产品质量的最后一道防线,如果重构过程中,能不断得到对重构质量的反馈,可以大大降低重构带来的风险。
  1. 重构周期

每个产品都有版本计划及市场使命。如果产品即将退市,对它进行的重构,无疑是没有任何意义的,因为重构后的软件已经没有上场表演的机会。重构需要根据市场需求和重构时间,选择能切入的时机。比较有效的一个方法是Small Step重构,把重构任务进行拆解,切分到一个个迭代中增量完成。

遥遥无期的重构,由于项目看不到短期收益,容易动摇支持重构的决心;另外,在重构期间,可能还不断有新功能加入,为了做到可以替代原有产品,在重构同时,还需要不断追赶这些功能,巨大的压力,容易使团队身心疲惫。

  1. 代码度量数据

平均圈复杂度、函数平均行数、代码总行数、重复度等代码度量,可以作为是否进行重构的参考,也是预估重构周期的一个重要指标。另外,重构过程中,在CI部署代码度量检查,可以看到代码复杂度不断下降,提升坚持重构的信心。

  1. 自动化测试包围情况

保证重构“不改变软件可观察行为”最有效的举措,就是待重构代码已经有大量自动化测试用例包围。考虑如下情况:

  • 测试用例最好是基于业务进行拆分,并且覆盖场景比较全面
  • 测试框架支持不同平台,可以减少重构对平台环境的依赖,自由选择
  • 如果已有测试用例执行速度较快,可以保证重构有更好的节奏感。

如果测试用例覆盖场景较少,不推荐补充完所有场景测试用例后再进行重构。一个推荐的做法是,按照重构计划,先补充某个场景用例,然后对其进行重构,交付后继续进行下一个场景,循环迭代,直到所有场景都完成。

另外,在CI中部署分支覆盖率监控工具,可以感知到分支覆盖情况逐渐变好,在代码重构完成同时,也交付了一份自动化测试用例(当然,分支覆盖率仅能保证分支被跑到,并不能保证逻辑正确)。

遗留代码重构决策表(Excel版)

下载地址:
https://github.com/liyongshun/refactor/blob/master/refactor_decision_list.xlsx

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容