如何改善遗留的代码库

作者 | Jacques Mattheij

译者 | aiwhj

在每一个程序员、项目管理员、团队领导的一生中,这都会至少发生一次。原来的程序员早已离职去度假了,给你留下了一坨几百万行屎一样的、勉强支撑公司运行的代码和(如果有的话)跟代码驴头不对马嘴的文档。

你的任务:带领团队摆脱这个混乱的局面。

当你的第一反应(逃命)过去之后,你开始去熟悉这个项目。公司的管理层都在关注着你,所以项目只能成功;然而,看了一遍代码之后却发现失败几乎是不可避免。那么该怎么办呢?

幸运(不幸)的是我已经遇到好几次这种情况了,我和我的小伙伴发现将这坨热气腾腾的屎变成一个健康可维护的项目是一个有丰厚利润的业务。下面这些是我们的一些经验:

备份

在开始做任何事情之前备份与之可能相关的所有文件。这样可以确保不会丢失任何可能会在另外一些地方很重要的信息。一旦修改了其中一些文件,你可能花费一天或者更多天都解决不了这个愚蠢的问题。配置数据通常不受版本控制,所以特别容易受到这方面影响,如果定期备份数据时连带着它一起备份了,还是比较幸运的。所以谨慎总比后悔好,复制所有东西到一个绝对安全的地方并不要轻易碰它,除非这些文件是只读模式。

重要的先决条件:必须确保代码能够在生产环境下构建运行并产出

之前我假设环境已经存在,所以完全丢了这一步,但 Hacker News 的众多网友指出了这一点,并且事实证明他们是对的:第一步是确认你知道在生产环境下运行着什么东西,也意味着你需要在你的设备上构建一个跟生产环境上运行的版本每一个字节都一模一样的版本。如果你找不到实现它的办法,一旦你将它投入生产环境,你很可能会遭遇一些预料之外的糟糕事情。确保每一部分都尽力测试,之后在你足够确信它能够很好的运行的时候将它部署生产环境下。无论它运行的怎么样都要做好能够马上切换回旧版本的准备,确保日志记录下了所有情况,以便于接下来不可避免的 “验尸” 。

冻结数据库

直到你修改代码结束之前尽可能冻结你的数据库,在你已经非常熟悉代码库和遗留代码之后再去修改数据库。在这之前过早的修改数据库的话,你可能会碰到大问题,你会失去让新旧代码和数据库一起构建稳固的基础的能力。保持数据库完全不变,就能比较新的逻辑代码和旧的逻辑代码运行的结果,比较的结果应该跟预期的没有差别。

写测试

在你做任何改变之前,尽可能多的写一些端到端测试和集成测试。确保这些测试能够正确的输出,并测试你对旧的代码运行的各种假设(准备好应对一些意外状况)。这些测试有两个重要的作用:其一,它们能够在早期帮助你抛弃一些错误观念,其二,这些测试在你写新代码替换旧代码的时候也有一定防护作用。

要自动化测试,如果你有 CI 的使用经验可以用它,并确保在你提交代码之后 CI 能够快速的完成所有测试。

日志监控

如果旧设备依然可用,那么添加上监控功能。在一个全新的数据库,为每一个你能想到的事件都添加一个简单的计数器,并且根据这些事件的名字添加一个函数增加这些计数器。用一些额外的代码实现一个带有时间戳的事件日志,你就能大概知道发生多少事件会导致另外一些种类的事件。例如:用户打开 APP 、用户关闭 APP 。如果这两个事件导致后端调用的数量维持长时间的不同,这个数量差就是当前打开的 APP 的数量。如果你发现打开 APP 比关闭 APP 多的时候,你就必须要知道是什么原因导致 APP 关闭了(例如崩溃)。你会发现每一个事件都跟其它的一些事件有许多不同种类的联系,通常情况下你应该尽量维持这些固定的联系,除非在系统上有一个明显的错误。你的目标是减少那些错误的事件,尽可能多的在开始的时候通过使用计数器在调用链中降低到指定的级别。(例如:用户支付应该得到相同数量的支付回调)。

这个简单的技巧可以将每一个后端应用变成一个像真实的簿记系统一样,而像一个真正的簿记系统,所有数字必须匹配,如果它们在某个地方对不上就有问题。

随着时间的推移,这个系统在监控健康方面变得非常宝贵,而且它也是使用源码控制修改系统日志的一个好伙伴,你可以使用它确认 BUG 引入到生产环境的时间,以及对多种计数器造成的影响。

我通常保持每 5 分钟(一小时 12 次)记录一次计数器,但如果你的应用生成了更多或者更少的事件,你应该修改这个时间间隔。所有的计数器公用一个数据表,每一个记录都只是简单的一行。

一次只修改一处

不要陷入在提高代码或者平台可用性的同时添加新特性或者是修复 BUG 的陷阱。这会让你头大,因为你现在必须在每一步操作想好要出什么样的结果,而且会让你之前建立的一些测试失效。

修改平台

如果你决定转移你的应用到另外一个平台,最主要的是跟之前保持一模一样。如果你觉得需要,你可以添加更多的文档和测试,但是不要忘记这一点,所有的业务逻辑和相互依赖要跟从前一样保持不变。

修改架构

接下来处理的是改变应用的结构(如果需要)。这一点上,你可以自由的修改高层的代码,通常是降低模块间的横向联系,这样可以降低代码活动期间对终端用户造成的影响范围。如果旧代码很庞杂,那么现在正是让它模块化的时候,将大段代码分解成众多小的部分,不过不要改变量和数据结构的名字。

Hacker News 的mannykannot

[1]

网友指出,修改高层代码并不总是可行,如果你特别不幸的话,你可能为了改变一些架构必须付出沉重的代价。我赞同这一点也应该在这里加上提示,因此这里有一些补充。我想额外补充的是如果你修改高层代码的时候修改了一点点底层代码,那么试着只修改一个文件或者最坏的情况是只修改一个子系统,尽可能限制修改的范围。否则你可能很难调试刚才所做的更改。

底层代码的重构

现在,你应该非常理解每一个模块的作用了,准备做一些真正的工作吧:重构代码以提高其可维护性并且使代码做好添加新功能的准备。这很可能是项目中最消耗时间的部分,记录你所做的任何操作,在你彻底的记录并且理解模块之前不要对它做任何修改。之后你可以自由的修改变量名、函数名以及数据结构以提高代码的清晰度和统一性,然后请做测试(情况允许的话,包括单元测试)。

修复 bug

现在准备做一些用户可见的修改,战斗的第一步是修复很多积累了几年的 bug。像往常一样,首先证实 bug 仍然存在,然后编写测试并修复这个 bug,你的 CI 和端对端测试应该能避免一些由于不太熟悉或者一些额外的事情而犯的错误。

升级数据库

如果你在一个坚实且可维护的代码库上完成所有工作,你就可以选择更改数据库模式的计划,或者使用不同的完全替换数据库。之前完成的步骤能够帮助你更可靠的修改数据库而不会碰到问题,你可以完全的测试新数据库和新代码,而之前写的所有测试可以确保你顺利的迁移。

按着路线图执行

祝贺你脱离的困境并且可以准备添加新功能了。

任何时候都不要尝试彻底重写

彻底重写是那种注定会失败的项目。一方面,你在一个未知的领域开始,所以你甚至不知道构建什么,另一方面,你会把所有的问题都推到新系统马上就要上线的前一天。非常不幸的是,这也是你失败的时候。假设业务逻辑被发现存在问题,你会得到异样的眼光,那时您会突然明白为什么旧系统会用某种奇怪的方式来工作,最终也会意识到能将旧系统放在一起工作的人也不都是白痴。在那之后。如果你真的想破坏公司(和你自己的声誉),那就重写吧,但如果你是聪明人,你会知道彻底重写系统根本不是一个可选的选择。

所以,替代方法:增量迭代工作

要解开这些线团最快方法是,使用你熟悉的代码中任何的元素(它可能是外部的,也可能是内核模块),试着使用旧的上下文去增量改进。如果旧的构建工具已经不能用了,你将必须使用一些技巧(看下面),但至少当你开始做修改的时候,试着尽力保留已知的工作。那样随着代码库的提升你也对代码的作用更加理解。一个典型的代码提交应该最多两三行。

发布!

每一次的修改都发布到生产环境,即使一些修改不是用户可见的。使用最少的步骤也是很重要的,因为当你缺乏对系统的了解时,有时候只有生产环境能够告诉你问题在哪里。如果你只做了一个很小的修改之后出了问题,会有一些好处:

◈ 很容易弄清楚出了什么问题

◈ 这是一个改进流程的好位置

◈ 你应该马上更新文档展示你的新见解

使用代理的好处

如果你做 web 开发那就谢天谢地吧,可以在旧系统和用户之间加一个代理。这样你能很容易的控制每一个网址哪些请求定向到旧系统,哪些请求定向到新系统,从而更轻松更精确的控制运行的内容以及谁能够看到运行系统。如果你的代理足够的聪明,你可以使用它针对个别 URL 把一定比例的流量发送到新系统,直到你满意为止。如果你的集成测试也能连接到这个接口那就更好了。

是的,但这会花费很多时间!

这就取决于你怎样看待它了。的确,在按照以上步骤优化代码时会有一些重复的工作步骤。但是它确实有效,而这里介绍的任何一个步骤都是假设你对系统的了解比现实要多。我需要保持声誉,也真的不喜欢在工作期间有负面的意外。如果运气好的话,公司系统已经出现问题,或者有可能会严重影响到客户。在这样的情况下,我比较喜欢完全控制整个流程得到好的结果,而不是节省两天或者一星期。如果你更多地是牛仔的做事方式,并且你的老板同意可以接受冒更大的风险,那可能试着冒险一下没有错,但是大多数公司宁愿采取稍微慢一点但更确定的胜利之路。

via:https://jacquesmattheij.com/improving-a-legacy-codebase

作者:Jacques Mattheij

[3]

译者:aiwhj校对:JianqinWang,wxy

本文由LCTT原创编译,Linux中国荣誉推出

LCTT 译者

aiwhj

共计翻译:4篇

贡献时间:28 天

本文来自企鹅号 - Linux中国媒体

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏PHP在线

高并发量网站解决方案

一个小型的网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统架构、性 能的要求都很简单。随...

4658
来自专栏沃趣科技

备份重于一切:远离“Gitlab删库事件”,QBackup是你的最佳选择!

作者简介:孙朝阳 沃趣科技高级产品经理。 案发现场: Gitlab删库事件回顾 Gitlab是大家很熟悉的开源Git代码托管工具,国内公司大多使用社区版自行搭...

3698
来自专栏程序员宝库

号称“开发者神器”的github,到底该怎么用?

GitHub是一个拥有数十亿行代码的网站,每天有数百万开发者聚集在一起,与开源软件进行协作和报告问题。简而言之,它是一个基于Git构建的软件开发人员的平台。

984
来自专栏跨界架构师

分布式系统关注点——「负载均衡」到底该如何实施?

        前面两篇《分布式系统关注点——初识「高可用」》、《分布式系统关注点——仅需这一篇,吃透「负载均衡」妥妥的》看完后,相信大家对实现高可用的思路和负...

1401
来自专栏私有云搭建

腾讯云服务器+可道云kodexplorer打造企业私有云

公有云越来越疲软,企业用户和个人用户对于公有云的接受度也越来越低。企业用户往往转向私有云盘产品,个人用户往往转向了NAS产品,从而来满足自己对于文件共享和管理的...

1.1K4
来自专栏腾讯移动品质中心TMQ的专栏

腾讯TMQ在线沙龙回顾|EP(测试分析+分层自动化测试)实践

答:Hook英文翻译过来就是「钩子」的意思,那我们在什么时候使用这个「钩子」呢?在 Android 操作系统中系统维护着自己的一套事件分发机制。应用程序,包括应...

2172
来自专栏老九学堂

号称“开发者神器”的GitHub,到底该怎么用?

相信这么努力的你 已经置顶了我 老九学堂 你身边的IT导师 提醒:大师兄会员课讲解视频在文末哦。 GitHub是一个拥有数十亿行代码的网站,每天有数百万开发者...

33911
来自专栏Java学习网

开发人员必备技能之一“性能优化”

软件程序的性能问题在设计价段就应该有充分的考虑,根据实际需求制定对应的技术方案和实现方法,比如软件运行后的并发用户数、数据存储量等要求;通常所说的性能优化无非是...

3375
来自专栏互联网技术栈

读《大型网站技术架构》

《大型网站技术架构》是自己接触的第一本架构知识的书籍,还是在14年时买的实体书,前后读了几遍,颇有所得,后来实体书被朋友借走再没归还,也就没再翻过。

1102
来自专栏程序员宝库

号称“开发者神器”的GitHub,到底该怎么用?

GitHub是一个拥有数十亿行代码的网站,每天有数百万开发者聚集在一起,与开源软件进行协作和报告问题。简而言之,它是一个基于Git构建的软件开发人员的平台。

1152

扫码关注云+社区

领取腾讯云代金券