太惨了,系统重构后出了线上事故,背了C绩效,不堪回首的两个月

18,049 阅读16分钟

重构系统后,得到了血的教训

4 年前的一次线上事故,把三个月加班加点攒下的苦劳全部败光。

2020年的春节前,我重新构建的系统刚刚上线不到一个月,就迎来了一年中最重要的促销期。届时订单量将达到全年的最高峰。然而,我没有料到,今年最后一个重大事故竟然是由我引起的。

早上刚到公司,就有人找我说:“运营部门反馈,一个批量发券任务突然停止了,发券数量一直没变化。”我立刻感到头皮发麻,我立即打开电脑开始排查问题。很快,越来越多的人围在了我周围,其中包括我的组长和上级主管,可能还有我不认识的更高级别的领导……

我开始变得非常烦躁,因为我发现问题出在批量任务调用下游超时,但任务却没有捕获异常,导致任务异常终止。而这部分正是我刚重构的。之前的代码一切正常,我后悔当初没有充分理解原代码的深义,盲目删除了旧代码……

在这种情况下,手动重试可以继续执行,但我无法确定继续执行是否会导致重复发券。因为在重构后,我没有测试过百万级别的发券任务。老板让我快速止损,但我还在后悔和紧张的情绪中,语气不太好地回答:“不行,之前发的券冻不了。” 说完后我就后悔了,无意中得罪了领导……而旁边还有那么多人……

为什么这件事这么紧急呢?因为公司依靠这批优惠券进行促销。没有优惠券,今年的营销计划肯定泡汤。后来通过各种办法给用户补发了券。整个营销计划全面打乱,至少要延迟3个小时。

那一年虽然非常忙碌、辛苦和劳累,但绩效结果很差。今天我与大家分享当年重构所带来的得与失。

0. 前因

事情这样的,三个月前我被调去负责另一个系统,这个系统我完全不熟悉。之前系统留下了一个重构任务,但是前任负责人被调走了,于是责任留在了我的肩上。现在想想我当时至少犯了几个错误

  • 刚接手系统,还不熟悉的时候,就去进行重构
  • 重构方案极其复杂,妄图一次重构完,没有分期
  • 没有调研业界的实现方案,想当然去重构
  • 没有仔细进行重构方案评审,没有得到其他人的有效建议
  • 人力不足,我既需要项目管理进度管理,又需要全力写代码
  • 上下游团队没有单独出人力协助我重构,需要我改动上下游系统。
  • 被产品和领导牵着鼻子走,完全按照别人的想法重构,事后看他们的想法不正确

接下来和大家聊聊,正确的重构节奏应该是什么

1. 先了解业务再重构,否则以退为进,拒绝重构

重构是一件极其有挑战的事情。推动者需要知道重构前系统的痛点和现状。否则就好比医生不了解病人有什么病,就给病人操刀做手术一样荒谬

在我接手系统前,前任研发和领导已经定下了重构计划(但没具体方案)。他们想让我接手后就立即重构,可以看到他们也并不专业,的确我们的团队就是一个草台班子。

当时的傻呵呵接下来这件事,当时真应该拒绝,和领导说明清楚:我需要时间熟悉系统,现在的我还没有能力进行正确的重构。 可以先和领导定下里程碑,自己先花时间梳理代码逻辑,梳理系统架构,在做几个需求以后,找到系统的痛点后再进行重构。

这叫以退为进。

2. 目标驱动和问题驱动

重构分为问题驱动和目标驱动,两者存在不同的做法。

2.1 问题驱动

问题驱动是说:系统已经暴露了具体的问题,这些问题指向系统存在痛点,非常痛。于是针对这个明确的问题进行重构。

可以认为 问题驱动适用的场景

  1. 比较紧急,系统已经暴露问题,需要理解重构优化。
  2. 问题明确,痛点明确,目标收益明确,重构思路也十分明确。

2.2 目标驱动

目标驱动不同,他需要全面的分析系统现状,梳理已知的问题、发掘未知的风险。找到原因后进行归纳整理,得出重构的目标,确定重构收益,设计重构方案。

适用场景

  1. 紧急程度低,可以慢慢分期重构。
  2. 系统的痛点有很多,但是并不明确,需要花时间梳理。
  3. 重构目标和重构方案也不明确。

所以目标驱动型 往往第一步是梳理现状,进行痛点分析才能明确目标。

3. 全面分析系统痛点和现状

系统重构要付出的巨大成本和风险,只有在全面分析系统的不足和痛点后,才能对症下药找到最全最优最合理的解决方案,才能取得最大的重构收益,平衡掉重构的成本和风险,做到最大的正向价值的重构,所以需要充分彻底地梳理现状和分析痛点。

3.1 正确分析现状

一个经得起推敲的解决方案一定是有条理、有层次、有逻辑性的。

首先分析问题前,要先问自己:如何全面地分析问题,分析问题时应该抓哪些重点。抓全面和抓重点 至少要选中1个。

一个好的解决方案在分析问题时,一定是两者兼顾的,既要全面分析,又要突出重点。(号称“全面的解决所有问题” 一定是吹嘘,容易受到读者的挑战和质疑。)

3.2 如何全面地分析问题

全面分析问题 有两个方法,要素分析法和因果分析法是最常用的两个方法。

问题驱动适合使用 因果分析法 梳理现状

3.2.1 因果分析法

因果分析法首先提出最表象的问题,然后逐层排查原因、逐层追问,直至问题的根源。如果因果关系是正确的,就能保证这个问题分析的足够全面。

一般线上问题复盘时都喜欢使用 5 Why分析法,即打破砂锅问到底,不停的追问,直至找到最根本的原因。举个栗子

  1. 为什么线上一直接口失败? 因为调用下游一直超时,所以我方接口一直失败。
  2. 为什么下游一直超时? 因为有个用户的优惠券太多了,所以查券超时了。
  3. 为什么优惠券太多就会接口超时?因为用户优惠券索引存在缺失,无法直接命中目标券。
  4. 为什么用户的优惠券那么多?因为用户大量购买会员,然后退掉,再购买,所以积攒的优惠券非常多。
  5. 为什么用户可以频繁买退会员,这合理吗?不合理,业务上应该限制用户此种行为。

如果这样分析一个具体问题,问题的根本原因和解决方案不就呼之欲出了吗? 会有人说你分析问题不够全面吗?

因果分析可以保证全面性,但容易陷入到细节之中,细节太多,无法得出关键的结论。读者就会感受分析乱糟糟的,看不懂。

因果分析法应该注意提问适可而止,不要过于深入。(详细的技术方案阶段再深入讨论细节)越是复杂的项目,要保证方案的层次性。如何保证层次性,适可而止地提问。

我举个例子,如何保证会员售后场景资金安全。

  1. 会员售后资金安全是指什么? 要确保一笔会员售后行为用户和平台均无资金损失。用户得到资金赔付,那么用户无资损;平台冻结了用户资产,那么平台无资损。同时确保资金的正确性、被冻结资产的正确性。
  2. 如何保证平台不出现资损?确保资产都能被冻结,在资金被明确冻结后,根据冻结结果正确计算资金,赔付给用户。
  3. 如何保证用户不出现资损?确保用户的资产被冻结后,正确的金额赔付到用户。

于是售后的资金安全问题被分为两个方向,防用户资损,防平台资损。

接下来分析两个方向的子流程 如何保证正确性,一层层把问题拆解下去。于是流程上的逻辑变为了因果逻辑。只要保证各个环节都是符合预期的,那么最终结果一定是正确的。

因果分析最强大之处是 让读者感受逻辑性,可以不经意之间让读者认可作者:“问题分析过程的逻辑性很强”。

3.2.2 要素分析法

因果分析法中的每一步都存在因果关系,递进式的分析问题,要素分析法的各个部分不存在因果和逻辑关系,而是并列关系,各个要素组成一个整体。

要素分析法如何识别出要素呢?1)一句话总结系统 2)使用名词法抽离出要素。

如分析会员售后系统:售后发起人在某个售后渠道对某个售后对象发起售后,未通过售后校验规则则被拒绝,否则系统受理售后,用户资产会被冻结,用户得到赔付。

通过名词法,识别出关键要素包括 售后发起人、售后渠道、校验规则、售后对象、资产冻结、资金赔付。

最后一步逐个要素分析,该要素存在哪几种情况,对系统模型的影响,对系统流程的影响,对上下游系统的影响。

要素分析法可以全面覆盖所有的业务场景。可以让人细致、条理地理解系统的业务逻辑,梳理系统实现方案。

3.3 如何重点分析问题,突出哪些重点

经常听过一句话,“你没有抓住重点”。抓重点一定是带有经验性的,在抓重点之前,应该全面分析,也就是要素分析法、因果分析法等把影响系统的要素和逻辑罗列出来。

哪些是重点问题呢? 如果不懂,可以去问领导,“咱们的系统重点应该保障啥”

  1. 交易系统的资金安全方向
  2. 高并发系统的高可用稳定性方向
  3. 复杂业务系统的可扩展性方向
  4. 公司底层基建系统重视稳定性和业务接入的便捷性

以上都是各自场景的重中之重。 再不济 可以从需求交付上分析,需求交付的质量和速度。

3.3.4 大型重构的原因不能是:代码可读性差

代码存在坏味道可以作为微小重构的原因。在大型重构项目中,不要使用这种原因。 "可读性差?是不是你不熟悉代码?" 别人会这样挑战你。

4. 分析痛点

全面和重点分析后,应该得出系统的 “病因”,此时需要进行归纳和排序。

  1. 归纳:把众多的问题归纳为几大类问题。归纳可以分两种情况,一个是全面归纳,一个重点归纳。
    • 全面归纳可以将问题分为 模型上的问题、流程上的问题、上下游接口协议的问题。 大部分问题都能属于这三个方面
    • 重点归纳,根据项目特点归纳重点原因。如流程扩展性差、平台化场景多业务未隔离、资金安全、系统性能存在瓶颈、系统迭代效率低、系统重复性建设等等。
  2. 排序:确定最重要的几类问题。如资金安全的问题一定优先级更高。

5. 明确重构目标和带来的收益

系统现状和痛点分析后,此时你作为系统的主治医生,对系统的病因已经了如指掌,接下来你将明确治疗目标。

痛点反着说就是目标,在进行适当的润色即可。如痛点是系统存在资损风险,目标可以是:制定上下游整体数据一致性规范,识别并消除系统资损风险,建立防资损兜底机制,保障资金安全。

6. 向领导汇报重构方案的价值并获得认可

重构方案一定要充分向领导汇报,因为重构是一件风险极大、费时费力容易不讨好的事情,重构开始后,需要投入巨大的人力,往往还需要兄弟团队配合,如果不是领导力挺你重构,没有资源投入,你很难安全地完成这件事。

既然决定要重构,则一定要充分沟通,让领导意识到重构方案的价值。避免自己冒着巨大风险、付出巨大努力和辛苦后,得到一个不好不坏的结果。那真的是白费力气了。

很多时候自己的想法并不是最好的,及时汇报方案,也容易得到领导和同事的指导。众人拾柴火焰高。千万不要自以为是,听不见别人的意见。

天时、地利、人和缺一不可。除了自己的人和,还需要领导和公司的天时地利帮助,才能成事。

7. 必须进行业界调研和对标

一定要进行业界调研,参考其他业界其他公司或者公司内其他部门的实现方案,可以给自己提供很多灵感。

我个人的经验是,在自己没有成为该领域专家前,一定要参考其他人的系统设计经验。可以避免自己走弯路。

8. 和领导申请充分的人力和时间资源

重构系统涉及到上下游系统,一个人搞不定的,要向领导寻求帮助,让上下游同事一起干这件事。让熟悉系统的人跟自己一起做,拉更多的人入伙,多个人一起承担重任,这种组织上的安排,只能由领导出面解决。

假如别的同事经常打扰你,总让你确认这件事,确认那件事,总让你帮忙梳理文档,你愿意配合吗? 每个人都很忙,没人愿意长期给你干活。

让领导帮忙成立重构小组!然后你可以给每个人都分派任务,比自己独自硬扛,成功概率大很多。

虽然重构的目标不明确,但你可以尝试明确每个人的责任,设置短期的里程碑。例如前三天梳理整理资料,每天开早会, push大家干活。

8.1 申请足够的外部支援,产品和其他兄弟团队

除了本组内的人力资源,也要向领导申请外部上下游团队、产品团队一起介入。领导不给批怎么办?慢慢磨,好好说。

9. 重大的重构要分期重构

没有完不成的事情,只要资源充裕,任何事情都是有希望的。当你面临棘手的问题时,除了打起12分的精气神,还要多想想和领导申请资源啊!

最重要的包括人力资源、时间资源。如果空口白牙就要人,可能比较困难。

这需要你在调研阶段深入思考,预想到系统的挑战点,把任务细分,越细越好,然后拿着排期表找领导,要人、要时间。如果人和时间都不给!可以多试几次,软磨硬泡也是好办法!

此外还有别的办法,例如 ”偷工减料"。你可以和领导沟通,方案中哪些内容不重要,是否可以砍掉。”既然你不给人,砍掉不重要的部分,减少工作量,总可以吧"

重大的项目重构一定要进行分期。第一期应该抓住重点部分,把不相关的、次级问题放到二三期进行。信用卡可以分期付款,技术重构当然也可以分期优化!

10. 要有专门的人进行项目管理,协调进度,申请资源

大的项目重构涉及到很多项目管理的内容,如果负责人既需要全力写代码,又需要协调排期、协调进度、协调资源,那么就没有充分的精力开发代码,势必降低代码质量,引发后期更严重的质量风险,可能使项目功败垂成。

反过来,要有专门的人只对代码质量负责,不关心项目管理,全力开发代码。

11. 重构方案越简洁越好,不要刻意追求复杂

汇报材料高大上,实现方案短平快

项目重构风险大容易引出一堆线上问题,如果技术方案也复杂,那么更容易出问题。

一定要想办法,把实现方案做的简单。这样有3个好处;

  1. 降低实现难度,减少上线风险。
  2. 缩短开发周期,尽快落地。时间越长,容易疏漏出问题。
  3. 小步快跑,把更多的时间放在二期项目和二期方案上。

我重构失败的原因就是:方案过于复杂。本可以不用改系统模型,结果我新建了模型。新建模型要考虑新旧模型双写、失败回滚,这花费了我大量的时间,导致其他方向时间不足,出现了代码质量问题。

12. 总结

项目重构并不是简单的事情,要足够警惕和重视,我总结的 10 条血泪经验,希望大家不要犯我的错误。

  • 要花时间梳理系统现状和痛点分析,否则宁可不做。
  • 既要全面分析问题,也要抓重点。
  • 明确重构的目标和收益,不要抓错重点,确定错误的目标。
  • 要充分汇报重构方案,取得领导的认可。
  • 重构方案要尽量简洁,不要刻意追求复杂。
  • 一定要进行业界调研,否则一定会走弯路。
  • 要和领导协调足够的人力、时间和外部资源
  • 重大的项目重构,可以分期,不要试图一次到位。
  • 要有专门的人进行项目管理、进度管理、协调资源。
  • 要有专门的人集中精力,全力开发代码。