改进遗留代码库的推荐路线:11节关于建立可扩展和可维护系统的课程原来的程序员很久以前离开的系统可能正在阳光明媚的地方度假,并且文档(如果有的话)很可能与现有系统不同步.而你的工作就是带领团队走出困境。在经历了逃跑的本能反应后,你开始了解这个项目,公司高层也不会容忍项目失败。但是,就您手头的情况而言,失败是大概率事件。那么如何处理呢?我有幸(或不幸)有过几次类似的经历,我和一小群朋友发现,如果能把这些垃圾代码变成健康可维护的项目,其实是非常值得的。事物。下面是我们总结的一些改进遗留代码库的经验(或军规)。1、数据备份在开始做任何事情之前,都需要备份所有可能相关的内容,以保证无论发生什么情况都不会丢失数据。很难记住每天修改了什么东西,尤其是配置数据很容易出现这种问题,配置通常没有版本控制,如果能定期备份,就可以避免很多麻烦。将所有内容复制到一个非常安全的地方,除非它处于只读模式,否则你永远不要接触。2.一个重要的前提,搭建一个真实的仿真环境。我在之前的文章中漏掉了这一步,假设这个环境已经存在,但是很多HN网友指出了这一点,他们是绝对正确的。第一步是确保您知道现在生产环境中运行的是什么,这意味着您需要能够构建一个与您的真实环境一致的软件版本——与二进制版本相同的软件环境。如果找不到执行此操作的方法,则在将代码提交到生产环境时可能会遇到一些令人讨厌的意外。在您有足够的信心将新代码投入生产之前,请确保在正确的环境中尽可能多地测试新代码。上线时做好随时切换回旧代码的准备,并确保通过日志记录相关重要内容,以便后续排查问题时派上用场。3.冻结数据库修改尽可能冻结数据库修改,直到第一阶段改进完成,直到团队对代码库有了透彻的了解,遗留代码已经被抛弃,才考虑修改数据库结构。在此之前对数据库进行任何修改都可能导致一些严重的问题,并且您无法同时运行旧系统和新代码库。保持DB完全不变,你可以将新的业务逻辑代码与旧的进行比较,如果这一切都按预期工作,应该没有任何区别。4.编写测试在进行任何更改之前,编写尽可能多的端到端和集成测试,以确保这些测试产生正确的输出并涵盖所有潜在情况。这些测试将有两个重要的功能:帮助在早期阶段消除任何误解,另一方面,一旦您开始编写新代码来替换旧代码,这些测试将更好地保护您的系统。自动化所有测试,如果您已经有CI经验,请尽快使用它,并确保您的测试运行速度足够快,以便在每次提交后运行全套测试。5.Instrumentation和Logging如果老平台还能增加Instrumentation,在全新的数据库表中做,为每一个你能想到的事件添加一个简单的计数器,并添加一个单独的函数来做这件事,基于的名字事件来增加这些计数器。这样您就可以使用一些额外的代码行来实现带时间戳的事件日志,并且您将了解有多少事件导致一种事件。一个例子:用户打开应用程序,用户关闭应用程序。如果两个事件应该导致一些后端请求,那么这两个计数器应该长期保持不变,区别在于当前打开的应用程序的数量。如果您看到打开的应用程序多于关闭的应用程序,您就知道应用程序必须以另一种方式结束(例如崩溃)。这个简单的技巧将每个后端应用程序变成一种簿记系统,就像真正的簿记系统一样,所有数字都必须匹配,确保它们在任何地方都可以使用。随着时间的推移,该系统将成为监控健康方面的无价之宝,并将成为源代码控制系统变更日志的绝佳伴侣,您可以在其中确定每个错误引入的时间点,并对各种变更的影响进行计数情况。我通常将这些计数器的分辨率保持为5分钟(因此每小时记录12个日志),但如果您的系统有更少或更多的事件,您可能需要修改此间隔。所有计数器都使用相同的数据库表,因此每个计数器只是该表中的一列。6.一次改变一个点。在添加新功能或修复错误时,不要陷入同时改进代码和代码运行平台的陷阱。这会引起很多麻烦。7.平台变更如果您决定将您的应用程序迁移到另一个平台,请先这样做,但要保持一切功能完全相同。您可以添加更多文档或测试,但不能超出这一点,所有业务逻辑和相互依赖性都应保持原样。8.架构改变接下来要解决的是改变应用程序的架构(如果需要的话)。此时,您可以自由更改代码的高层结构,通常是通过减少模块之间的水平链接数,从而减少与最终用户交互的代码活动范围。如果旧代码本质上是整体式的,那么现在是使其更加模块化的好时机,将大型功能分解为较小的功能,但保留变量和数据结构的名称。HN网友mannykannot指出,架构修改并不总是可行的,如果你特别倒霉,你可能需要非常深入地理解代码才能进行任何架构更改。我同意这一点,所以我会做一个小补充,如果你同时进行高级和低级更改,你需要将它限制为至少一个文件,或者最坏的情况是一个子系统,以限制更改尽可能多的范围。否则,您可能很难调试刚刚所做的更改。9.底层重构至此,你应该对每个模块的功能有了很好的了解,并为实际工作做好准备:重构代码以提高可维护性,使代码能够扩展新的功能。这可能是项目中最耗时的部分,文档需要随之而来。在您完全编写文档介绍并彻底理解模块之前,不要更改模块。这个阶段还可以修改变量和函数的命名,修改数据结构,提高代码的清晰度和一致性。记得添加相关的测试代码(可以根据需要进行单元测试)。10.修复错误既然您已经准备好进行一些最终用户可见的更改,那么要做的第一件事就是修复多年来积累在队列中的错误。像往常一样,先确认bug还在,然后写一个测试修复bug,你的持续集成和端到端测试应该可以帮助你避免由于缺乏理解或一些错误而导致的任何bug和外围问题。11、数据库升级如果以上工作已经完成,你就可以重新拥有一个可靠的、可维护的代码库,你可以选择改变数据库模式甚至更换数据库。以上所有已经完成的工作将帮助您以无负担的方式进行更改而不必担心任何意外,您可以使用新代码和所有测试来测试新数据库以确保您的没有任何问题移民。祝贺路线图,您现在已经走出丛林,准备好实施任何新功能。不要尝试彻底重写彻底重写是一个几乎注定会失败的项目。一方面,你是从未知领域开始的,你甚至不知道要重构什么,另一方面,你也在把所有的问题都推到最后一天,即上线前一天与新系统。可悲的是,那也是你失败的时候。业务逻辑假设最终会被证明是有问题的,此时你会突然明白为什么旧系统会以一种奇怪的方式运行,并最终意识到并非所有将旧系统放在一起运行的人都是白痴。如果你真的想让公司(和你自己的声誉)陷入泥潭,那就去彻底重写,但如果你足够聪明,完全重写系统通常不是一个讨论选项。备选方案:迭代改进解开这些联系的最快方法是从您已经理解的代码开始(它可能是外围的,但也可能是某个核心模块),并在其旧上下文的范围内尝试逐步改进。如果旧的构建工具不再可用,您将不得不使用一些技巧(见下文),但至少在您开始更改时让旧系统尽可能地工作。典型的提交通常只包含几行代码。发布!所有修改尽可能发布到生产环境,即使修改后的代码对最终用户是不可见的,因为当你对系统不够了解时,只有生产环境会告诉你新的修改哪里有问题.如果这个问题只是在一个小的变化之后出现,你会获得几个好处:很容易找出问题所在你会很好地改进你的过程你应该立即更新你的文档以记录获得的新见解明智地使用代理服务器如果你正在重构一个web系统,谢天谢地你可以在最终用户和遗留系统之间部署一个代理服务器。您可以准确控制每个URL的哪些请求进入旧系统,哪些请求路由至新系统,从而更轻松、更精细地控制运行的内容。如果您的代理足够强大,您甚至可以控制将一定比例的流量从某个URL发送到新系统,这样您就可以观看新系统的运行情况。如果您的集成测试也可以连接到此代理,那就太好了。有道理,但是这一切都需要太多时间!那么这取决于你如何看待它。如果您按照这些步骤进行操作,工作量会很大,但它确实有效,而且对流程的任何优化都会让您对整个系统有更深入的了解。我个人在这方面也享有很高的声誉,我真的不希望这样的工作有任何负面问题。有时,如果公司系统已经出现问题,并且可能会影响客户,如果遵循这个流程可能会使事情变得更好,我宁愿完全控制和使用这个流程,也不愿为了节省数天或数周的表面方法。如果你更喜欢牛仔式的做事方式,而且你的老板也同意——那么这也许是一种可以接受的高风险方法,但大多数公司宁愿采取稍微慢一些、更稳健的重构方式。
