聊聊“重构”

上次记录了之前sd重构的操作。这次聊聊对重构的一些想法。

往期文章

灵魂的拷问

项目从demo发展到有一定数量用户,老板问

给2个星期时间重构,能够支撑以后千万DAU吗

对于这种问题,一开始我是拒绝的。。。这类问题在过去工作中也遇到不少,值得吐槽一下。

量级思维

给位数级别的DAU,能跑起来就是了。 10W级别,加缓存、优化sql、多部署几个容器。 100W级别,双机房走起。 1000W级别,双数据中心在路上了。 10000W级别,……

越往上的量级,问题难度、解决方案的复杂度、消耗的人力时间物力更是指数级别增长。 幻想2个星期就能解决几个量级之后的问题,就缺乏工程师的量级思维了。

重构是持续进展的行为

每次新增代码都受到需求复杂度、工期、设计水平、编码质量等因素的限制,可能或多或少地带来潜在的问题。随着业务发展,系统不停迭代,问题不断累积,量变到质量,导致系统越来越难理解、性能问题突出、不能快速响应新需求。重构是持续进展的行为,是一项长期投资,在每次迭代中对已有代码进行优化,绝对不是集中折腾2个星期,以后就完事。

何时开始,何时结束

曾经有过几次这样的经历:在一个专门的重构分支,几个人大搞特搞,折腾两三个星期甚至更久,把能跑的系统改的面目全非,然后和并行的业务一合并就各种问题,最后经历痛苦的合并修复、甚至回滚。 这种惨痛的重构经历,始于系统积累了过多问题,又想一步到位都解决,一旦开干又停不下来,同时业务又在原有未重构代码的分支上持续迭代,业务分支和重构分支差异巨大。

好的重构实践,随时都能开始,随时都能结束。 重构是持续进展的行为,在每一次业务迭代都可以进行:消除代码的bad smell,修正之前仓促设计的不足,修复某处潜在的性能隐患……每次迭代花点时间小步子快速重构,随时都能开始,随时都能结束,才能够最大限度使得重构和业务迭代保持一致的步伐,让重构为快速响应业务发展赋能。

抓大放小,量化结果

作为技术leader、业务leader,看到这样大方向的重构效果,内心是爽爽的:

  • 页面载入时间从xxx缩短到yyy
  • 接口响应时间从xxx下降到yyy
  • 存储成本每个月节省xxx
  • app用电量减少xxx
  • 以前做一个这样的功能要xxx天,现在只需要yyy天就搞定

相反,花了1个星期埋头重构,然后告诉老板“这段代码写的啰嗦,我精简了”、“这些命名不规范,全部refactor了”、“这个模块设计耦合度高,我改成低耦合了”……通常会被老板和业务喷死。

这些小的重构优化,是日常做的事情,是《搬砖工的自我修养》。 换位思考,集中式的重构,会占用原本做业务开发的时间。技术leader、业务leader都有压力,需要数据表明投入大量时间做这个重构是值得的。因此,这个阶段的重构要抓大放小,解决核心的问题,并且量化结果。

业务驱动

在个位数DAU的时候做1000W DAU系统的改造。 在用户行为稀少,连模型都跑不起来的时候做实时推荐改造。 …… 诚然,这些技术问题有难度,做出来成就感很强。 但是业务并不买单,做的事情在对方看来对现在业务发展没有帮助。到头来只是感动了自己,别人不买账。 更尴尬的是,业务量真正上来了,之前假想情况下折腾的架构重构却不管用。

业务驱动是这种复杂重构最好的动力。

why not working

Martin Fowled在《重构》这本书给出了“重构”的定义:

重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。 重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。

重点是在不改变软件可观察行为的前提下进行重构。 但现实经常是改着改着系统就跑不动了,尤其是重构经验不足的同学。

直接修改旧的代码

这是最常见到的危险操作。直接在旧的可以运行的代码上做修改重构,这时候的代码没有经过充分验证,属于不稳定、甚至不可用状态。如果流程不规范,这样的代码被部署到线上,就有可能出故障。 更好的实践是,旧的代码不要改变,新增一个方法、类、模块、服务等来承载重构后的代码。然后逐步小批量切换验证,直到最后全部都切换到新代码,线上稳定后再清除旧代码。

关于测试

怎么知道重构后的代码是否改变软件可观察行为? 当然是测试验证,尤其是单元测试。现实是,业务驱动的技术团队,单元测试从来是可遇而不可求。根据我之前的经验,设计和实现单元测试的时间,是单纯开发功能的1到3倍。告诉业务要写单元测试,而不能开发新功能,那是不可能的事情。 没有单元测试的重构是危险的,更加危险的是回归测试也没有的重构。通知测试,改过代码,需要回归验证,不要过于自信给自己挖坑。

Built with Hugo
Theme Stack designed by Jimmy