重构到底如何代码?

点击上方蓝色“终端研发部”,选择“设为星标”

学最好的别人,做最好的我们

源 /顶级程序员      文/ 黑石

引言

代码重构有两大难点,一个是「考古」,也就是如何快速梳理出代码的原有逻辑,还有一点就是「发布」,如何让新的代码可以稳定的发布到线上,而不产生故障。下面我们就聊聊我一个朋友的故事,看看他是怎么把代码稳定搞上线的。为了表达更为亲切,你现在就是我那个朋友。重构代码对很多人来说,绝对是一件脏活、累活。没有可以大幅度提效的方法,难以沉淀有效的体系化的可复用的技术抓手,对业务来说没有明显的增量,精力和时间消耗巨大,没有测试用例,也不一定能得到测试的支持,自测很难做到充分,最后开发完了很难上线,主要原因是害怕!当然并不是我们不自信,是真的恐惧。

一、你为什么不敢发代码?

通过代码还原当时完整的产品逻辑太难了

你重构的代码是谁的?鬼知道是谁的!能让你重构的代码大概率不是你写的代码,而且是远古代码,用的是一种过时的技术栈。当然一般情况下,当年的开发、测试、甚至产品早已不见了踪迹,只能在注释的代码里看见了了数语。言语中透露着无奈,用一个程序员的良心提醒着后来人,「小心前面的脏东西」。看了这些话,你只能收回口中马上要吐出的芬芳,默默离开工位,倒点热水。

从此你会发现,注释不仅能够帮你读懂代码,还能有警示作用,告诉你重构代码的同时,记得把 bug 一并改了。你想要通过注释来梳理出原始需求的愿望宣告失败,接下来你只能死磕了,祈祷千万不要漏掉业务逻辑。

没有自测用例

别以为大公司制度完善,测试都有完整的测试用例,现实会狠狠的夹你脑门。频繁的迭代,功能早已面目全非,老的用例根本不可用,更何况根本找不到老的测试用例。没有用例怎么自测呢?全靠个人想象。

没有测试同学跟进

多一个人多一分力量,让一个有经验的测试参与到功能回归中来,无疑会给你的重构事业吃上定心丸,但真实的情况是,测试同学根本不想参与这种脏活累活。他自己手里的需求还测不过来,怎么会把时间奉献给一个前端发起的重构工作上呢。无增量,无抓手,纯体力,他们同样心知肚明。

没有稳定发布方案

在没有上述保障的前提下,如果你还能硬着头皮上线,就会遇到更大的难题,如何上线?直接全量替换吗?如果线上出问题怎么办?好在前端的回滚是非常迅速的,但是即使再迅速的回滚,从发布完成到发现问题回滚,在提醒用户重新刷新页面,这个过程也足以造成难以估量的后果,尤其是那些高频使用,且极易产生脏数据的场景。这就是没有一个有效的发布方案所导致的常见后果,这个后果还有可能导致你背上故障,这一年加过的班,熬过的夜,掉的头发,什么也换不来,只能催生你换个地方重新做人的念头。综上因素直接导致开发者极度缺乏安全感,一个不敢上线自己代码的程序员,就像半夜被自己一个月大孩子的哭声吵醒,那时那刻你只想装死摸鱼。更何况你的工作往往不是只有重构这一件事,写写新需求他不香吗?就这样你眼看着一个页面重构了两个星期,迟迟不能收尾,你变得越来越不自信,越来越害怕了起来,不敢面对那些重构了一半的代码,开始恐惧老板的问题:「重构搞的怎么样了?」,你简直不像个程序员。终于到了年底,你的重构事业还未完成,更可怕的是,这件事还被打上了「承诺型」OKR 的标,于是你痛定思痛,做了个梦。

时间回到年初你刚刚接到重构任务的时候。

二、寻求组织保障

你的重构工作是把 177 个 jQuery 页面用 React 重写一遍。你立马想到,自己一个人一年时间,一定是做不完的,此时此刻,切记不要满口答应,一定实事求是,甚至向着最坏的方向想,让老板充分认识到这项任务的艰巨性,不要抱有太高的期望。最重要的是保证人力的投入,必须有更多的同学一起参与进来,有效的分工才有可能完成这项艰巨的任务。有人参与进来,也只是基础,因为他们极有可能会像上面描述的一样,从兴致勃勃到唯唯诺诺,因此一定要确保时间的投入,必要时把老板也拉进来跟你一起做,老板一旦参与进来,就会更有体感,能体会到大家的不易。接下来,就应了那就老话,「别忘了,你是一个 owner!」做好基础设施建设,让每个同学有趁手的工具,有安全的保障,去除他们的后顾之忧至关重要。因此,你要做下面几件事。

三、划分重构页面优先级

你通过细致的研究发现,这些页面中,有 77 个页面是用户使用较多的页面,也是相对比较复杂的页面,剩下的 100 个页面,大部分是给开发用的增删改查页面,用户的使用频率不高。于是你做了如下划分:

优先级划分好优先级以后,就要对不同优先级的页面使用不同的稳定发布策略。

  • 复杂高频页面:重兵压上,细致还原原始需求,抠代码,拉测试同学一起整理测试用例,按照测试用例自测,测试同学回归所有功能。但其实这部分页面中,也可以分为两种页面:

  • 编辑页面:这样的页面是风险最高的页面,一旦因为后端接口没有做完整的数据校验,就会编辑出脏数据,或者错误的数据被保存,导致线上运行异常,这种后果将是不堪设想的,即使非常短的时间内回滚,也会造成难以挽回的故障,因此必须要像新需求一样测试到位。

  • 展示页面:这样的页面不会影响运行时,不会产生脏数据,是风险相对低一点点的页面,本着不麻烦合作方的原则,毕竟资源有限,可以让测试帮你出完整的用例,然后你自己自测,或者多找几个同学帮你自测。

  • 高频简单页面:自测,当然最好是能绑架几个经常用这个功能的开发,来帮你点点,但是自己测总是会有可能会有遗漏,因此就需要下面的步骤来保证了。

  • 低频运维页面:选择性重构,因为很多页面基本上不会有迭代,且使用频率较低,基本上不需要重构,即使是有新的需求,也可以在做新需求的时候顺便重构下,以为并不能占用太多时间。

将页面划分完毕后,你会发现重构的工作量降低了很多,因为本着「无需求,勿变更」的原则,很多页面都可以不需要重构。且上述重构完的页面都必须做灰度发布。

四、单测

前端不太喜欢写单测,你大概总结了一下,主要有下面几方面的原因:

  • 当下的收益不高。

  • 相比后端接口的单测,前端单测写起来相对复杂。

  • 前端更多是面向 UI 的编程,但 UI 变动大,难以使用 TDD (测试驱动开发) 的开发模式。

  • 没有写单测的习惯,可能是因为单测增加了工作量,且没有写纯函数的意识,不利于测试。

  • 单测的工具难学又难用。

你发现前端不喜欢写单测,有各种各样的原因,但是当你重构那些复杂页面,尤其是 jQuery 技术栈重构为 React 技术栈的时候,单测真的非常有用。

比如这里有一个编辑页面,包含两部分:基本信息和运行逻辑,在重构运行逻辑时候,你首先要保证的是重构过后的页面在保存的时候,保存的数据结构必须跟之前的接口参数必须一致,所以在重构运行逻辑这个组件的时候就会有很多数据转换逻辑。

可以看到为了保证你的新组件不影响保持原有功能,就要保证原始数据通过新组件的一顿操作最终保留了原来的结构,此时你就可以写单测来保证这个过程。

describe('utils', () => {
  it('流程图:转换为提交的数据 transformForm', () => {
    const result = transformForm(canvasData);
    expect(result).toEqual(settingData);
  });

  it('流程图:转换为需要的数据 parseRuleSetData', () => {
    const [result] = parseRuleSetData(settingData, rules);
    expect(result).toEqual(canvasData);
  });

  it('流程图:反复转换 transformForm - parseRuleSetData', () => {
    const [result] = parseRuleSetData(visualSettings, rulesData);
    const newResult = transformForm(result);
    expect(newResult).toEqual(visualSettings);
  });
});

前端单元测试写起来复杂,其实只是 UI 的单测复杂而已,如果你把代码做好了足够的拆分,拆出更多函数,更多 hooks ,单测就是轻而易举了。

五、测试用例

你在的团队,一直测试资源都不是充足,测试用例似乎一直都是一种可遇不可求的东西,尤其是在敏捷开发的趋势下,产品功能变动快,很少有测试会一直去维护那个最初的测试用例,往往是写过用过就再也找不到了。但测试用例在重构这个场景下,真的非常重要,他解决的核心问题是把测试同学拉到重构的质量保障中,一起梳理老的逻辑。这份宝贵的测试用例,可以成为你自测的依据,也可以为你提供对于同一个功能的不同视角,如果你通过代码看到的是实现细节的逻辑,那测试看到的就是整个链路的流程图。很多中后台系统都有管理态和运行态之分,管理态,前端是非常熟悉的,但是运行态,测试往往更加熟悉。

六、自测

拿到测试用例,你就可以自测了,但是这里有个坑,就是如果你完全依赖测试同学给你的测试用例。只要保证测试用例验证通过就行了,这种想法会出大问题,因为负责这块功能的测试可能是个新手,可能并不是一直负责这块功能的测试,他们的测试用例可能只是浮于表面的。所以你需要把通过代码考古发现的测试用例里没有的逻辑,暴露给测试同学,并补充到测试用例里。并且如果发现有一些看不懂的逻辑,就应该搞懂他,那些你不懂的死角,往往上线后就会有大问题,不要心存侥幸。自测非常重要,但是往往你会觉得开发完了,就算是把这个事做完了,然后就去忙别的事了,并没有好好的自测,心想还有测试呢,等他们提问题,我再改吧。这是一种很普遍的程序员心理,其实很难避免,毕竟事情有很多。这个时候你可以找同组的开发同学帮你点一点,先解决那些显而易见的问题,也算是一个认真负责的程序员了,不要让测试同学给你提太多低级 bug。

七、回归测试

能有测试同学帮你做功能的回归测试真是一件可遇而不可求的事,一定要珍惜,拿出你的大块时间配合好。这其中最重要的就是多交流,测试同学也不一定知道所有的逻辑,在做回归测试的时候,就需要开发和测试反复核对每个逻辑死角,弄清楚,才敢上线。当然,能够有测试帮你回归的功能都是极易引起故障的功能,这里就有一个技巧就是如何拉测试参与你的重构中来。像这样重要的功能如果测试知道里面的逻辑,你可以怀着请教的心态去问对方,如果对方并不了解,那你就可以讲给他听,一个负责任的测试,应该都非常想了解自己负责系统的重要模块的来龙去脉。

八、灰度发布

即使你做了再多的测试,都有可能有没有考虑到的遗漏点,这个时候灰度就非常重要了,灰度就必须要有灰度工具才行。重构一般是以页面或者区块为粒度按照人来进行的。所以你的灰度工具必须要包含这些功能:

  • 配置用户或者用户组

  • 配置老路由和新路由

  • 配置灰度状态提示

  • 新老页面的自动打点

灰度配置页面,新老动态路由的参数需要保持一致,这样才能把参数传递下去。

展示灰度提示,并提供一个快速「返回旧版」的按钮,为了更快速解决问题,可以给出开发者联系方式。

当用户访问老路由的时候,按照灰度配置验证当前用户是否在灰度中,如果在灰度中,则立即跳转到新的路由,并显示灰度提示。如果重构的是页面中的区块,则可以提供灰度命中的方法,在页面调用区块的部分做判断。灰度策略可以按照以下用户级别分布进行:

  • L1:所有项目开发,测试,设计师,内部运营人员

  • L2:核心用户,建立钉钉群,观察用户反馈,及时解决用户问题。

  • L3:适当加入更多用户,直到全量后,删除灰度策略的配置。

发布后,注意观察打点数据:

打点的时候需要注意,要按照动态路由来打点,并分成命中灰度的,点击使用旧版的,不在灰度内的三个维度来看数据,同时每天调整灰度用户,这样就能保证页面是有人用的。如果有很多用户使用了返回旧版的功能,那你就得找找这些用户了解下情况了,到底是有 bug 还是交互不舒服,一对一的解决用户问题,在反复去优化你的页面,慢慢扩大用户灰度范围,直到老的路由访问数据 PV 为 0。

九、全量上线

全量上线并不是灰度所有人,而是真正下线老的页面,并删除老的代码,只有到这一步才算重构完成了。

十、总结

经历千难万险,你终于把重构好的页面上线了,经历了这个过程,感慨良多,只求以后再也不要做重构了,好好做需求不香吗?后头看看整个过程,要想重构的页面上线,不仅要下苦功夫,还要克服人性的一些弱点,要做到这几点:

  • Double Check:让其他人参与进来,多一个人就能帮你发现更多问题。重构面前,不要相信自己,相信伙伴。

  • 逻辑无死角:不要还有不懂的代码,不清楚的逻辑,按照程序员的第六感,不确定的都会出大问题。

  • 集中注意力:重构不能碎片化进行,要集中大块时间来做,并一做到底,不然过个几天,你自己的代码都会不认识。

  • 一跟到底:开发完成不是重点,全量上线,并下掉老的页面才是结束。

致敬每一位重构路上的勇士。

BAT等大厂Java面试经验总结 想获取 Java大厂面试题学习资料扫下方二维码回复「BAT」就好了回复 【加群】获取github掘金交流群回复 【电子书】获取2020电子书教程回复 【C】获取全套C语言学习知识手册回复 【Java】获取java相关的视频教程和资料回复 【爬虫】获取SpringCloud相关多的学习资料回复 【Python】即可获得Python基础到进阶的学习教程回复 【idea破解】即可获得intellij idea相关的破解教程回复 【BAT】即可获得intellij idea相关的破解教程关注我gitHub掘金,每天发掘一篇好项目,学习技术不迷路!


回复 【idea激活】即可获得idea的激活方式

回复 【Java】获取java相关的视频教程和资料

回复 【SpringCloud】获取SpringCloud相关多的学习资料

回复 【python】获取全套0基础Python知识手册

回复 【2020】获取2020java相关面试题教程

回复 【加群】即可加入终端研发部相关的技术交流群

阅读更多

为什么HTTPS是安全的

因为BitMap,白白搭进去8台服务器...

《某厂内部SQL大全 》.PDF

字节跳动一面:i++ 是线程安全的吗?

大家好,欢迎加我微信,很高兴认识你!

在华为鸿蒙 OS 上尝鲜,我的第一个“hello world”,起飞!

相信自己,没有做不到的,只有想不到的

在这里获得的不仅仅是技术!


喜欢就给个“在看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值