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