1. 前言
距离自己上一篇博文的发布时间都已经过去快2个月时间了,想想自己是不是又进入了过去的怪圈:定下目标 -> 严格执行一段时间 -> 一段时间放羊 -> 无限地放飞自我。
懒惰虽是人类的天性,但其实这段时间的我并没完全放飞自我。恰恰相反,这段时间我正在努力地深度挖掘自己,反思自己。寻找自己可以转化的价值点,重新定位自己,重新审视自己。
眼看2020年即将过去,回首这一年是获得的更多还是失去的更多,应该有个总结。但本文并非是对2020年的总结,而是想和大家聊聊我最近自己在互联网产品代码重构实践中的一些反思。
希望这些能够给尚未经历重构的你有一些启发。
2. 现实团队中的矛盾冲突
做过互联网产品开发的同学往往经历过这样的过程,在一个老板拍板的时间周期内完成一个看似无法完成的功能开发任务。
这种感觉就好像乔布斯在世时的苹果,员工在他的现实扭曲力场的作用下,无限发挥创造力而实现了一个又一个看似不可能的目标。重塑苹果公司产品矩阵,重新提升产品创新与竞争力,为苹果乃至全世界留下了宝贵的财富。
可惜,不是每个人都是乔布斯,也不是每个公司员工都如苹果员工一样,创新创造力爆棚。在上面一个任务面前,一位普通人能做的就是:加班加点干活。
在截止日来临前,往往我们会发现作为普通人的我们,也仿佛有着超强的战斗力。如此艰难的条件下,任务顺利完成,产品功能顺利交付。
但是,不同于表面看到的平静,在产品发布之后的日子里,我们越来越感觉一个现实:那些为了赶工而做出的设计与实现,最后还是会反噬我们自己。让一切回归不断修复缺陷的过程,疲惫而无奈。
而当开发同学与产品提出需要时间来修复这些历史债务时,往往得到的答案是:NO。换位思考一下,确实只能是NO。对于一个产品来说,耗费一段时间来做修复历史债务而在产品功能层面无法体现的开发工作毫无价值。这也难怪产品同学的无情,是产品的前途决定的。
像上面这样的矛盾冲突,几乎每个产品迭代周期内都会出现。开发同学与产品同学来回博弈,在时间无法保证的前提下,最终交付的产品质量日渐下降。而到产品日落西山时,一切就都晚了。
3. 应对之道
为了避免这种“悲剧”,我总会建议产品再三考虑,将非功能性的技术任务也纳入进版本排期中。给到开发充分的时间来进行优化,这不但对产品长期有益,也对团队有益。这些技术任务会成为一种粘合剂,让开发与产品的关系发生微妙的变化,更加趋向缓和与友好的沟通。团队协作效率被大幅提高,沟通成本显著降低,并且产品质量却可以得以极大提高。一举三得的事,现实中却很少会被业务团队的产品所采纳。
在这种情况下,我们只能通过保守的任务估算耗时来弥补优化工作的成本,见微知著地进行“地下工作”。
4. 为什么不进行代码重构
正如之前我写的关于质量的文章(谈谈工程代码质量控制的事)所提到的,质量根本在于代码,而代码核心在于不断地小范围重构优化。说到为什么不进行代码重构,一般开发人员经常会给出的几个回答是:
- 当时时间紧,任务重,也就没想那么多
- 现在运行的好好的,去大规模重构内险也太大了吧。
- 重构又不能作为独立任务安排到版本排期中,不做也罢
- 业务能增长就好,代码写的好坏影响不大,主要保证没明显BUG就好
- 如何重构没有比较好的思路,不知从何做起
最近一直在做的是团队产品EP的后端代码重构层面的事,可以简单的分享一下其中的故事。
5. 我的重构信条
在我这么多年的前后端开发经理来看:
重构,需要随开发过程一直不断地进行。
它只有进行时,没有完成时。
我在最近几年的项目经历中,也在不断实践重构。音视频SDK的抽象结构与层次重构优化,指标系统的业务代码结构优化,以及当前在做的EP产品后端代码业务层次抽象与架构优化等。
实践给到我对重构的反思主要有以下几条:
- 重构可以从把魔法数字变成常量开始。
- 重构可以从优化参数名,方法名开始。
- 重构可以从重复代码逻辑抽取成公共函数开始。
- 重构可以从将不必要的注释转变为可读性良好的函数名开始。
- 重构可以从把类似行为通过方法参数/多态来实现开始。
- 重构可以从把大文件/大函数分解成更小的可复用的函数开始。
- 重构可以从把功能的定义与实现分离设计开始。
- 重构可以从将杂乱无序的过程定义为通用模板函数设计开始。
- 重构可以从将对象继承转变为对象组合方式开始。
- 重构可以从梳理业务逻辑流程并适当划分同步/异步过程开始。
- 重构可以从架构设计与业务架构匹配开始。
- 重构可以从更清晰的架构职责划分与错误定位开始。
从以上的重构反思来看,重构主要关注的领域有:
- 代码的可读性
- 代码的可维护性
- 代码的可测试性
- 代码的错误可定位性
- 代码的可扩展性
- 代码的架构合理性
我对以上领域的整体关注优化级的排序:
可读性 > 可维护性 > 可测试性 > 错误可定位性 > 可扩展性 > 架构合理性
前4者都是代码微观层面的,属于质量控制的关键环节,因此排序较靠前。
后2者属于代码宏观层面,更多关注结构性,这属于当产品业务达到一定规模才会更多的影响产品。如果产品永远达不到那种规模,那么过早地做这些事其实性价比就非常低了。
6. 重构前需要提前掌握的内容
我把重构前需要提前掌握的内容分为两块:
- 面向对象设计,设计模式,数据结构的设计与理解
- 单元测试,用例场景设计及副作用评估与控制
很多时候,我们在重构前需要提前积累一定的编码经验,以及足够的代码设计与实践经验。特别是有关设计模式,数据结构,以及面向对象设计方面的理论知识,否则很容易进入不知从何处着手的境况。
除此之外,重构时还需要特别关注单元测试,代码逻辑覆盖以及可测试性的问题。因此,除基本的开发能力外,还需要重构人员有一定的测试能力,用例场景设计能力,以及重构后的代码兼容性/副作用相关的评估与控制能力。
重构是任何一位开发者代码能力精进之路需要经历的过程,需要大家从理论与实际两方面来刻意练习。让代码更美好,功能缺陷更少,相信是所有开发人员追求的理想,而重构就是实现这一目标的有力工具。
重构+单元测试,让这两件代码优化利器为产品保驾护航,可尽快让开发人员进入代码“心流”状态,获得更大的心理满足与工作成就感。
7. 推荐阅读
本文主要是记录与分享个人的重构心得,以下是过程中看到与阅读过的内容。对重构有兴趣的同学可以针对性地学习,相信它们会对你有所帮忙。当我们都能写出优美的代码时,让自己与团队其他成员爱上code时,那么这一切的付出都会觉得非常值得。
愿大家早日达到这种状态,共勉~
- 重构 改善既有代码的设计 Java语言版
- 代码整洁之道
- 代码精进之路:从码农到工匠
- Google软件测试之道
- 领域驱动设计 软件核心复杂性应对之道
- 阿里巴巴Java开发规范
- 编码规范-Java函数优雅之道