【重构】把重构后的代码稳定搞上线

代码重构有两大难点,一个是「考古」,也就是如何快速梳理出代码的原有逻辑,还有一点就是「发布」,如何让新的代码能够稳定的发布到线上,而不产生故障。下面咱们就聊聊我一个朋友的故事,看看他是怎么把代码稳定搞上线的。为了表达更为亲切,你如今就是我那个朋友。

重构代码对不少人来讲,绝对是一件脏活、累活。没有能够大幅度提效的方法,难以沉淀有效的体系化的可复用的技术抓手,对业务来讲没有明显的增量,精力和时间消耗巨大,没有测试用例,也不必定能获得测试的支持,自测很难作到充分,最后开发完了很难上线,主要缘由是惧怕!固然并非咱们不自信,是真的恐惧。javascript

1、你为何不敢发代码?

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

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

image.png

image.png

image.png

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

没有自测用例

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

没有测试同窗跟进

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

没有稳定发布方案

在没有上述保障的前提下,若是你还能硬着头皮上线,就会遇到更大的难题,如何上线?直接全量替换吗?若是线上出问题怎么办?好在前端的回滚是很是迅速的,可是即便再迅速的回滚,从发布完成到发现问题回滚,在提醒用户从新刷新页面,这个过程也足以形成难以估量的后果,尤为是那些高频使用,且极易产生脏数据的场景。这就是没有一个有效的发布方案所致使的常见后果,这个后果还有可能致使你背上故障,这一年加过的班,熬过的夜,掉的头发,什么也换不来,只能催生你换个地方从新作人的念头。canvas

综上因素直接致使开发者极度缺少安全感,一个不敢上线本身代码的程序员,就像半夜被本身一个月大孩子的哭声吵醒,那时那刻你只想装死摸鱼。更况且你的工做每每不是只有重构这一件事,写写新需求他不香吗?就这样你眼看着一个页面重构了两个星期,迟迟不能收尾,你变得愈来愈不自信,愈来愈惧怕了起来,不敢面对那些重构了一半的代码,开始恐惧老板的问题:「重构搞的怎么样了?」,你简直不像个程序员。后端

终于到了年末,你的重构事业还未完成,更可怕的是,这件事还被打上了「承诺型」OKR 的标,因而你痛定思痛,作了个梦。安全

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

2、寻求组织保障

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

3、划分重构页面优先级

你经过细致的研究发现,这些页面中,有 77 个页面是用户使用较多的页面,也是相对比较复杂的页面,剩下的 100 个页面,大部分是给开发用的增删改查页面,用户的使用频率不高。因而你作了以下划分:
image.png
优先级划分好优先级之后,就要对不一样优先级的页面使用不一样的稳定发布策略。运维

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

    • 编辑页面:这样的页面是风险最高的页面,一旦由于后端接口没有作完整的数据校验,就会编辑出脏数据,或者错误的数据被保存,致使线上运行异常,这种后果将是不堪设想的,即便很是短的时间内回滚,也会形成难以挽回的故障,所以必需要像新需求同样测试到位。
    • 展现页面:这样的页面不会影响运行时,不会产生脏数据,是风险相对低一点点的页面,本着不麻烦合做方的原则,毕竟资源有限,可让测试帮你出完整的用例,而后你本身自测,或者多找几个同窗帮你自测。
  • 高频简单页面:自测,固然最好是能绑架几个常常用这个功能的开发,来帮你点点,可是本身测老是会有可能会有遗漏,所以就须要下面的步骤来保证了。
  • 低频运维页面:选择性重构,由于不少页面基本上不会有迭代,且使用频率较低,基本上不须要重构,即便是有新的需求,也能够在作新需求的时候顺便重构下,觉得并不能占用太多时间。

将页面划分完毕后,你会发现重构的工做量下降了不少,由于本着「无需求,勿变动」的原则,不少页面均可以不须要重构。且上述重构完的页面都必须作灰度发布。
undefined

4、单测

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

  • 当下的收益不高。
  • 相比后端接口的单测,前端单测写起来相对复杂。
  • 前端更可能是面向 UI 的编程,但 UI 变更大,难以使用 TDD (测试驱动开发) 的开发模式。
  • 没有写单测的习惯,多是由于单测增长了工做量,且没有写纯函数的意识,不利于测试。
  • 单测的工具难学又难用。

你发现前端不喜欢写单测,有各类各样的缘由,可是当你重构那些复杂页面,尤为是 jQuery 技术栈重构为 React 技术栈的时候,单测真的很是有用。
image.png
好比这里有一个编辑页面,包含两部分:基本信息和运行逻辑,在重构运行逻辑时候,你首先要保证的是重构事后的页面在保存的时候,保存的数据结构必须跟以前的接口参数必须一致,因此在重构运行逻辑这个组件的时候就会有不少数据转换逻辑。
image.png
能够看到为了保证你的新组件不影响保持原有功能,就要保证原始数据经过新组件的一顿操做最终保留了原来的结构,此时你就能够写单测来保证这个过程。

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 ,单测就是垂手可得了。

5、测试用例

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

6、自测

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

7、回归测试

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

8、灰度发布

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

  • 配置用户或者用户组
  • 配置老路由和新路由
  • 配置灰度状态提示
  • 新老页面的自动打点

灰度配置页面,新老动态路由的参数须要保持一致,这样才能把参数传递下去。
image.png
展现灰度提示,并提供一个快速「返回旧版」的按钮,为了更快速解决问题,能够给出开发者联系方式。
image.png
当用户访问老路由的时候,按照灰度配置验证当前用户是否在灰度中,若是在灰度中,则当即跳转到新的路由,并显示灰度提示。若是重构的是页面中的区块,则能够提供灰度命中的方法,在页面调用区块的部分作判断。

灰度策略能够按照如下用户级别分布进行:

  • L1:全部项目开发,测试,设计师,内部运营人员
  • L2:核心用户,创建钉钉群,观察用户反馈,及时解决用户问题。
  • L3:适当加入更多用户,直到全量后,删除灰度策略的配置。

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

image.png

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

9、全量上线

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

10、总结

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

  • Double Check:让其余人参与进来,多一我的就能帮你发现更多问题。重构面前,不要相信本身,相信伙伴。
  • 逻辑无死角:不要还有不懂的代码,不清楚的逻辑,按照程序员的第六感,不肯定的都会出大问题。
  • 集中注意力:重构不能碎片化进行,要集中大块时间来作,并一作到底,否则过个几天,你本身的代码都会不认识。
  • 一跟到底:开发完成不是重点,全量上线,并下掉老的页面才是结束。

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

做者:ES2049 / 黑石
文章可随意转载,但请保留此原文连接。
很是欢迎有激情的你加入 ES2049 Studio,简历请发送至 caijun.hcj@alibaba-inc.com 。
相关文章
相关标签/搜索