上次记录了之前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倍。告诉业务要写单元测试,而不能开发新功能,那是不可能的事情。
没有单元测试的重构是危险的,更加危险的是回归测试也没有的重构。通知测试,改过代码,需要回归验证,不要过于自信给自己挖坑。