如何带领平均入职2.6个月的新人,完成一次系统重构?

杨宏志 2018-07-19 09:53:56
作者介绍

杨宏志知乎首页架构负责人,主要负责首页工程化建设、工程架构优化、性能提升等工作。

知乎专栏:https://zhuanlan.zhihu.com/c_195355141

 

一、背景

 

我们旧版首页系统属于集中化设计:「全能类」FeedBase加main函数。基本上所有功能耦合在一起。每维护一行代码,必须异常谨慎,以免影响其它逻辑。在这里,先把大致背景给大家介绍一下:

  • 系统有6.6万行代码,首页逻辑的学习成本至少3个月;

  • 增加一个新feed类型工作量是7个工作日;

  • 首页扩展一个Tab工作量是约3周;

  • 没有完善的链路跟踪,线上Bug无从下手;

  • 首页目前服务端响应p95高达870ms;

  • 部分逻辑处于无人维护状态;

  • ……

 

二、我们该怎么做

 

其实呢,思路「很」清晰——解耦模块,优化性能。

 

可是怎么去解耦、解耦成什么、如何优化性能?Java/Go/C……重写?

 

2018年2月,为了支持兄弟团队业务,首页输出了我们几乎全部的工程师。事实上,目前首页仅有三个工程师、一个实习生。最资深的员工入职只有6个月,其余基本新入职,平均入职2.6个月,且都没有大型项目维护经验。换句话说,全是新人。

 

以这种现状,我们怎么去重构?

 

大家都知道重构是一个高大上的事,每个人都很积极,每个人都想做好一些事。但大家之前都在做普通业务,面向PM开发侵染了太多底色。似乎快速完成功能、快速上线比什么都重要,似乎只有这样才能体现自己的靠谱。

 

如此情况下,贸然启动重构或重写,后果不堪设想。就好比遮上一群「百米赛跑」的运动员的眼睛,让他们去跑「马拉松」。激情很高,技术过硬,可却不知道跑到哪去,更关键的是他们的技术还不一定适合长跑。

 

我们应该:

  • 指明方向:让大家有意识,我们大致要做成什么,为什么要做成这样;

  • 确定标准:重构不是炫技,不是构造华丽的系统。要面向工程、合作、业务,寻求最合适的设计;

  • 确定方法:让大家的能力有的放矢;

  • 不断促进:鼓励主动设计,尝试错误;紧把关口,让大家熟悉、成长。

 

三、我们做了什么

 

1、指明方向,引导思考
 

 

为什么要重构?大家只是浅层的理解,代码矬了!可为什么矬?为什么会变矬?“矬”体现在哪里?新架构是不是就可以永不变矬?

 

事实上,现在的“”不是因为前人的能力差,而是因为曾经的代码不再适合现在的业务场景。架构没有好坏,只有合适与否。我们要做的不是什么完美的系统,所以绝对不能去炫技,而是应当尽力适配当前的业务场景。

 

我们的目的是:让首页好用一些,让我们的新架构变矬得「慢一些」。

 

继而,还引导让大家思考为什么要在某个地方做打磨,为什么要对某个细节重视,为什么要放弃某个feature就很有必要了。这不是浪费时间,快速的产出只是及格后的加分项,只对作者友好的系统绝对是垃圾。

 

此外,用可见的结果引导标准,进而达成共识。比如,重构后的 ActionCard 模块,清晰的配置化管理、标准的自动化打点、极低的扩展成本等。通过一种好的结果,引导大家来思考为什么要这样,自己做会什么样,自己负责的东西如何达到这些指标。

 

2、确定方法:面向模块分工
 

 

为什么不是面向接口?

 

面向接口开发是当前最成熟的开发模式,双方定义好接口,并行去实现,之后联调。高效的上线,极大程度解耦,更方便的Bug定位……

 

谁来定义接口?

 

新人根本不了解业务逻辑。脱离实际业务,定义出的所有接口都是「华丽的错误」。同时也不可能等所有人都熟悉了业务,才启动重构。即使熟悉业务的我,也没有十足的信心定义「所有模块」的「完美」接口。比如「过滤」逻辑,基础的计算单元近百个,而且里面有各种性能优化的Hack,怎样处理可以易学习易维护易排错,大家都只是有一个初步的认知轮廓。至于兼容到性能优化策略,具体实现成什么样?只能走一步看一步。

 

谁实现接口?只是声明,没有实现,调用方会一直被Block,被调方的逻辑永远不会有真实的流量难。脱离验证,贸然上线,而且所有接口一起上线,任何一个接口出现问题,大家一起回滚:人力相互死锁。

 

面向模块开发怎么做的?

 

比如重构过滤模块的人,只考虑过滤模块怎么搞,其它的逻辑完全不用关心。

 

如果涉及与其它模块的交互:

 

  • a.其它模块调用本模块:重构本模块,顺手改变调用方调用逻辑。

  • b.本模块调用其它模块:保留原接口调用方式,尽力少做改变。(被调用方就绪后,修改接口层,规则同a)

注:如果调用方/被调用方未就绪,可增加「临时」适配层转接原逻辑。

 

比如,新版Feeditem序列化格式与旧版不同;但session模块还未支持协议升级。使用适配层新版协议适配为旧版,保证session正常工作,同时可以用线上流量验证新模块功能。session支持协议升级后,由session维护者下线适配代码。

 

3、小步快跑
 

 

面向模块开发,每个人负责独立的功能,就可以只关注在这个功能。其它模块的维护者不会触及本模块的任何环节,我们就可以随便改动,尽量小的改动,在学习中改动:哪怕某次上线只是干掉了一个Hack、哪怕某次上线只是上了一个「临时」适配层。

 

过滤模块的第一步工作,简单粗暴地把计算环节的if/else等Hack干掉,抽离基础算子,然后再组合。风险基本是零,成本相当低(不依赖太多filter知识),收益却很大(了解到了filter细节,同时清晰化了旧逻辑)。


明确价值,统一意志

 

为什么做重构,怎么能让它变矬得慢些?

 

一个好的系统,应该是面向维护者、使用者友好,且有着较低的学习成本,较低的DeBug成本。

 

如果已经做到成本降低了,就多思考能不能再低一些。在我们的人力、能力范围内,交付最简单的东西,而不是作者写起来舒服、不是为了马上上线。比如,为了方便使用某个feature,搞几层继承。作者写着很爽,但阅读成本无法预估。然后,某个人不爽了再重写一下……试着想想,这样的架构,难道不是设计出来就是为了变矬的?

 

所以我们才要反复引导大家思考,为什么重构,有哪些指标可以证明我们的重构成功了。这些指标就是首页的核心竞争力,也是个人的核心竞争力。去磨炼这些指标,不是浪费时间,而是我们与首页一起走向优秀。

 

不断激励,指明错误

 

由于对业务的不熟悉,以及重构经验的不足。我们经常遇到各种问题,也会提出很多的质疑。比如:「我认为这样设计很好」,「我认为写成这样就行了,不该浪费时间」,更多的可能是「无从下手」。有很多场景,发现苗头不对,要尽早制止。

 

作为负责人要接受尝试错误,但要把握试错的方向。同时要尊重别人的劳动成果,不能一杆子打死,觉得就得按自己说的办。

 

少走弯路的基础还是统一意志。看到好的设计,要借机强化意识,分析它们为什么好,好在促进了某些指标;而对于思路不当的设计,要指明可能的风险点。

 

四、我们做到了什么

 

由于人力有限(兼顾部分产品迭代),重构(耗时2.5月)只完成了一期工作,但收效却不低:

  • 扩展Feed从7d降到了2d (二期完成预计会降到1d);

  • 扩展新Tab从2w降到了3d;

  • 支持新的推广槽位从1d降到了1h;

  • 拉取架构的学习成本从3w降到了3-5d;

  • 支持了召回环节的实时反馈;

  • 支持了拉取日志的实时全链路跟踪, 快速定位bug;

  • 上线通用化卡片,初步实现了发布新Feed类型客户端无需发版;

  • 接入新版ab平台(包括Runtime)功能,实现线上实验的动态分析。

 

不论在什么背景下,我们都想让首页变成一支铁军,为首页负责,让工程架构更优化,工程师更强大!

活动预告