重构遗留软件:从挑战到机遇
本文讲述了我们如何处理一个物流管理系统(OMS)的国际化,以及与新的电子商务平台集成的挑战。该系统开发于2018年,旨在优化一个蓬勃发展的电商的订单准备流程,并与不同的物流运营商高效集成。它使用PHP(Symfony)、MySQL、Socket.io和jQuery构建,涵盖从包装到发货的整个流程,包括订单跟踪、快递员连接、标签生成和订单准备性能指标等功能。
技术债务的累积
多年来,该系统运行良好,但随着业务发展,其局限性日益显现。技术债务尤其令人担忧,它影响了项目的多个层面。在技术基础设施方面,该应用程序运行在过时的框架和基础语言版本上:
- Symfony版本(4.0)并非长期支持(LTS)版本,自2019年1月起停止接收安全更新。
- PHP 7.1也已结束生命周期,系统缺乏关键的安全更新。
除了过时的版本,项目在软件开发的基础方面也存在严重缺陷:
- 缺乏或不足的测试: 缺乏自动化测试(单元测试、集成测试和端到端测试)不仅阻碍了早期错误的发现,还使任何修改都可能危及系统的稳定性。
- 缺乏代码标准: 代码库没有遵循任何已记录的模式或标准,即使有,也与行业最佳实践不符。这使得维护和项目中新开发人员的加入都变得困难。
- 文档不足: 现有文档稀少且经常不完整。这不仅影响了技术开发,还影响了对代码中实现的业务流程的理解。
- 版本控制不完善: Git历史记录缺乏解释,提交粒度粗糙,消息也不遵循任何约定,缺乏对所做更改的背景信息。这使得理解代码的演变和随时间做出的决策变得困难。
技术债务的累积不仅对系统的稳定性和安全性构成威胁,还:
- 降低了新功能的开发速度
- 增加了引入错误的风险
- 增加了新成员加入团队的难度
- 增加了维护成本
- 使问题诊断和解决变得复杂
结构性限制
最初的架构存在耦合问题,严重影响了其灵活性和可扩展性:
- 完全依赖于主电商平台: 该应用程序无法独立运行,所有物流操作都直接依赖于电商平台的数据和流程。这意味着主平台的任何更改都可能破坏系统的功能。
- 共享数据库导致性能问题: 物流应用程序和电商平台使用相同的数据库,这导致性能问题,尤其是在任一应用程序的负载高峰期。此外,此配置使权限管理变得复杂,因为对数据库的任何访问都可能危及其他系统的重要数据。
- 无法独立运行: 该应用程序的设计只能与电商平台一起运行。这不仅限制了其可移植性,还阻碍了在隔离环境中进行测试或迁移到其他平台。其依赖项没有得到适当的封装,任何隔离尝试都需要对整个系统进行大规模且代价高昂的更改,主要类中没有遵守单一责任原则(SRP)。
- 难以实现新功能: 缺乏对开放/封闭原则(OCP)和Liskov替换原则(LSP)的遵守,极大地阻碍了系统的演进。新功能需要修改现有代码,增加了引入回归的风险。此外,模块之间的直接依赖使得遵循依赖倒置原则(DIP)几乎不可能。
这些结构性限制不仅降低了系统的可维护性和可扩展性,还增加了与任何修改或演进相关的风险,使应用程序处于技术上脆弱和战略上易受攻击的状态。
开发管理和战略一致性
最显著的挑战之一不仅是技术性的,也是战略性的。尽管外部开发在功能上是正确的,但在组织方面存在重大局限性:
- 与全球战略脱节: 开发是孤立进行的,没有对公司内部目标和流程的完整了解。这导致了一些功能,尽管技术上是正确的,但并不总是符合业务的实际需求。
- 缺乏战略优先级: 新功能的实施缺乏明确的评估和优先级排序流程。没有质疑一个功能是否真正必要,是否是最佳实现方式,或者是否存在更有效的替代方案。
- 被动式开发 vs. 主动式开发: 开发主要遵循被动模式,解决直接需求,而没有考虑长期影响或与公司其他流程的潜在协同作用。
- 缺乏验证流程: 缺乏结构化的审查和验证流程导致了一些功能,尽管可以运行,但并不总是对最终用户或公司的整体目标提供最佳解决方案。
这种情况在长期内是不可持续的,因为它:
- 导致产品越来越偏离实际需求
- 阻碍了与公司其他系统和流程的集成
- 使关于产品的战略决策变得复杂
- 限制了团队的创新和持续改进能力
基本成本的影响
在本项目中,一个经常被忽视但特别重要的方面是基本成本,我认为这是软件开发中的一个关键概念,它指的是即使不添加新功能或进行改进,维持系统运行所需的最低成本。
在我们的案例中,基本成本包括所有因需要维护过时的框架和语言版本、解决因技术债务累积而导致的紧急事件、管理与其他系统的依赖关系、适应耦合的架构以及对领域知识的了解不足而产生的费用。所有这些都消耗了大量可用资源,直接影响了投资创新和持续改进的能力。
虽然这个因素并不是促使我们决定内部化开发的决定性因素,但在项目的初步诊断中,它具有相当大的影响力。基本成本通常在评估系统的可持续性时被忽略,但在本案例中,它清楚地表明当前的策略在长期内是不可持续的。此外,正如我们在后续文章中将看到的,任何试图维持现有结构的尝试都将随着时间的推移成倍地增加基本成本。
有关基本成本概念及其重要性的更详细解释,建议参考Eduardo Ferro的原始文章。
转折点:新的挑战和战略决策
在任何重构项目中,都可以采用多种策略,并且经常会遇到以下两难选择:绞杀者模式(strangler fig)或彻底重写(big bang rewrite)。
最初的技术决策是在同一个遗留项目中工作,采用绞杀者模式,这种方法包括开发一个新的模块或系统,逐步替换旧系统的部分。这种策略允许我们进行并行更改,降低风险,并在构建更坚实的基础以支持未来功能的同时保持当前功能。
然而,从业务的角度来看,这种选择对现有系统(该系统已投入运行并履行其职能)构成了过高的风险。我们决定避免触碰现有项目,而是开发一个独立的应用程序来满足新的需求。
这种转变导致我们对现有代码库进行了分支,这是一个技术上可行但存在某些缺点的决定:
- 代码库的重复:现在需要维护两个单独的代码库。
- 数据库分离:必须为每个系统复制和调整数据结构。
- 基础设施复制:需要部署独立的服务器并为每个系统保证适当的可观察性。
- 团队的认知负担增加:所有这些重复都需要额外的努力来维护两个系统之间的一致性,从而增加了团队的复杂性和错误风险。
这种方法使我们能够朝着独立的解决方案前进,确保现有系统的稳定性,同时开发一个与新的战略目标相一致的项目。然而,必须详细分析利弊,才能充满信心地进行规划和应对这一挑战。此外,我们与业务层面达成了共识,即不扩展功能,并在完成向新电子商务平台的迁移之前严格控制项目的积压工作。
优点 | 缺点 |
---|---|
在非生产环境中工作,降低生产环境的风险 | 需要暂时维护多个项目 |
自由地从零开始实施新技术和模式 | 在维护方面的努力暂时重复 |
不必担心旧系统的技术限制或依赖关系 | 由于需要在系统之间同步更改,功能的重复可能会长期减缓开发速度 |
能够专注于必要的特性 | 截止日期的风险,因为有两个代码库 |
有机会从一开始就实施最佳实践 | 项目管理的复杂性 |
更容易从一开始就实施测试 | 需要与历史数据保持兼容性 |
灵活地适应新的业务需求 | 初始时间和资源成本更高 |
更好地与公司的整体战略相一致 | 可能暂时丢失非必要的特性 |
结论和后续步骤
决定内部化和重写遗留软件从来都不是一件容易的事,尤其是在该软件能够完成其功能的情况下。“如果它能工作,就不要碰它”这句话将始终存在。然而,有时需要后退一步才能前进两步。
在本系列的后续文章中,我们将探讨我们如何应对这些挑战,我们做出的技术和战略决策,以及我们如何将这些挑战转化为改进和团队发展的机遇。
以上是从软件遗产到战略机遇:起点(一)的详细内容。更多信息请关注PHP中文网其他相关文章!

依赖注入在PHP中通过外部注入方式提供对象依赖,提高代码的可维护性和灵活性。其实现方式包括:1.构造函数注入,2.设值注入,3.接口注入,使用依赖注入可以解耦、提高可测试性和灵活性,但需注意可能增加复杂性和性能开销。

在PHP中实现依赖注入(DI)可以通过手动注入或使用DI容器来完成。1)手动注入通过构造函数传递依赖,如UserService类注入Logger。2)使用DI容器可以自动管理依赖,如Container类管理Logger和UserService。实现DI可以提高代码的灵活性和可测试性,但需要注意过度注入和服务定位器反模式等陷阱。

Thedifferencebetweenunset()andsession_destroy()isthatunset()clearsspecificsessionvariableswhilekeepingthesessionactive,whereassession_destroy()terminatestheentiresession.1)Useunset()toremovespecificsessionvariableswithoutaffectingthesession'soveralls

stickysessensureuserRequestSarerOutedTothesMeServerForsessionDataConsisterency.1)sessionIdentificeAssificationAssigeaSsignAssignSignSuserServerServerSustersusiseCookiesorUrlModifications.2)一致的ententRoutingDirectSsssssubsequeSssubsequeSubsequestrequestSameSameserver.3)loadBellankingDisteributesNebutesneNewuserEreNevuseRe.3)

phpoffersvarioussessionsionsavehandlers:1)文件:默认,简单的ButMayBottLeneckonHigh-trafficsites.2)Memcached:高性能,Idealforsforspeed-Criticalapplications.3)REDIS:redis:similartomemememememcached,withddeddeddedpassistence.4)withddeddedpassistence.4)databases:gelifforcontrati forforcontrati,有用

PHP中的session是用于在服务器端保存用户数据以在多个请求之间保持状态的机制。具体来说,1)session通过session_start()函数启动,并通过$_SESSION超级全局数组存储和读取数据;2)session数据默认存储在服务器的临时文件中,但可通过数据库或内存存储优化;3)使用session可以实现用户登录状态跟踪和购物车管理等功能;4)需要注意session的安全传输和性能优化,以确保应用的安全性和效率。

PHPsessionsstartwithsession_start(),whichgeneratesauniqueIDandcreatesaserverfile;theypersistacrossrequestsandcanbemanuallyendedwithsession_destroy().1)Sessionsbeginwhensession_start()iscalled,creatingauniqueIDandserverfile.2)Theycontinueasdataisloade

绝对会话超时从会话创建时开始计时,闲置会话超时则从用户无操作时开始计时。绝对会话超时适用于需要严格控制会话生命周期的场景,如金融应用;闲置会话超时适合希望用户长时间保持会话活跃的应用,如社交媒体。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

WebStorm Mac版
好用的JavaScript开发工具

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境

禅工作室 13.0.1
功能强大的PHP集成开发环境

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

SublimeText3汉化版
中文版,非常好用