如何带领平均入职2.6个月的新人,完成一次系统重构?
一、背景
我们旧版首页系统属于集中化设计:「全能类」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)功能,实现线上实验的动态分析。
不论在什么背景下,我们都想让首页变成一支铁军,为首页负责,让工程架构更优化,工程师更强大!
原文发布时间为:2018-07-19
本文作者:杨宏志
本文来自云栖社区合作伙伴“DBAplus社群”,了解相关信息可以关注“DBAplus社群”。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
什么是缓存击穿?
缓存击穿、缓存并发和缓存雪崩是常见的由于并发量大而导致的缓存问题,本节讲解其产生原因和解决方案。 缓存击穿通常是由恶意攻击或者无意造成的;缓存并发是由设计不足造成的;缓存雪崩是由缓存同时失效造成的,三种问题都比较典型,也是难以防范和解决的。本节给出通用的解决方案,以供在缓存设计的过程中参考和使用。 缓存击穿 缓存击穿指的是使用不存在的key进行大量的高并发查询,这导致缓存无法命中,每次请求都要击穿到后端数据库系统进行查询,使数据库压力过大,甚至使数据库服务被压死。 我们通常将空值缓存起来,再次接收到同样的查询请求时,若命中缓存并且值为空,就会直接返回,不会透传到数据库,避免缓存击穿。当然,有时恶意袭击者可以猜到我们使用了这种方案,每次都会使用不同的参数来查询,这就需要我们对输入的参数进行过滤,例如,如果我们使用ID进行查询,则可以对ID的格式进行分析,如果不符合产生ID的规则,就直接拒绝,或者在ID上放入时间信息,根据时间信息判断ID是否合法,或者是否是我们曾经生成的ID,这样可以拦截一定的无效请求。 当然,每个设计人员都应该对服务的可用性和健壮性负责,应该建设健壮的服务,让我们的服务...
- 下一篇
DNS服务-了解篇
简介 DNS是用来名字解析的,名字解析成IP地址,IP地址解析成名字,正反操作,有服务器端和客户端即 S/C DNS是应用层协议,基于UDP/53、TCP/53端口,缺一不可 分为正向解析和反向解析/递归查询、迭代查询 注意:正反向解析是两个不同的名称空间,是两棵不同的解析树 名称解析:主机名解析 把一种名称转换为另一种名称的过程 根据用户提供的名称,去查询解析库,以得到另一种名称 hosts:文本文件 用户自定义了对应的解析列表即要解析的FQDN与IP地址对应关系 有时候hosts很好的解决了DNS服务器访问不了的情况 如:某个域所在的权威DNS服务器出问题了,网站图片不显示等,这时候在hosts文本里写上对应的FQDN和IP地址就可。 备注:FQDN也就是我们所说的网址,如www.xxx.com 如果在本机定义了hosts文本则优先本列表,本列表没有再去DNS库查询 递归查询 用户向第一个DNS服务发请求,DNS收到后,如果它这有直接的结果就直接给你,如果没有就会向根DNS服务询问,层层询问直到问道结果返回给你,负责到底 迭代查询 上述过程中去问根,根给你推荐别人,你去问它,...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS关闭SELinux安全模块
- CentOS7设置SWAP分区,小内存服务器的救世主
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- 设置Eclipse缩进为4个空格,增强代码规范
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2全家桶,快速入门学习开发网站教程
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长