译者 | 刘汪洋
审校 | 重楼⠀
在审查某个软件解决方案时,你是否感到其复杂性超出了实际需求?在对该架构或方法进行批评之前,请仔细考虑其可能的深远影响。
“过度架构”常常被用来形容某些软件解决方案的设计、抽象、实现或部署存在不必要的复杂性。这种批评通常暗示着这些方面难以理解、不易维护,甚至可能是错误的。然而,这类指责往往缺乏上下文或具体的支持性论述,但却容易被人们轻信和接受。
那么,为一个解决方案贴上“过度架构”的标签究竟意味着什么?
通常情况下,这种标签被视为软件工程师规避责任的一种“万能借口”,用来解释因工作量超出预期或项目时间线延误而产生的问题。更令人遗憾的是,管理层往往会毫不怀疑地接受这一解释,特别是在做出相关决策的工程师缺席时,这种解释更容易显得有说服力。
确实,最初实施的架构可能与当前的技术栈、业务假设或抽象存在分歧,但你是否有足够的依据来明确判断它是“过度架构”?公司自实施以来是否经历了业务目标或技术方向的变化?当初在设计时是否考虑了某些非功能性需求?是否有任何提交信息能够提供进一步的背景支持?
最关键的问题是,你的担忧真的能准确定义“过度架构”,还是其实并没有那么严重?或许我们都不能轻易下结论。
软件工程师负责开发软件
借用哈姆雷特的经典独白:
维护,还是不维护,这是个问题:是心甘情愿地忍受荒唐实现带来的打击, 还是拿起武器对抗无尽的麻烦并从头开始,用一张白纸重新设计。
虽然哈姆雷特的独白原意在探讨生死问题,这一段改编则成为了对软件开发过程的隐喻。软件工程师往往更倾向于编写新代码,而不愿意长期维护他人编写的代码。
当工程师被分配到维护任务时,无论是何种形式,他们通常会寻找各种方式来重新设计、重构甚至重写代码,以使工作更加有趣。如果代码的变更频率过高、圈复杂度过大,或者代码已经难以维护,那么这种决策无疑是有其合理性的。尽管这可能会令产品负责人和Scrum Master感到不快,但有时,历史遗留问题已无法忽视。
然而,公开称现有代码为“过度架构”可能为你赢得重新设计的机会,但也可能引发更深入的质疑,尤其是在以下几种情况下:
- 工程师不愿深入理解当前的实现
- 工程师不充分了解业务需求或技术需求
- 工程师对现有的设计模式或代码结构持不同意见
- 工程师不喜欢现有代码的风格、格式,甚至编程语言⠀
讽刺的是,重写项目的规模往往会超出预期,因为工程师最终不得不理解现有的解决方案才能重新实现它。尽管声称自己不是在“重复造轮子”,但在没有全面理解的情况下,工程师往往还是得走这条路。你的组织是否真的准备好迎接API优先、微服务、NoSQL等新技术的挑战?
随着时间线的延长,领导层的挫败感增加,其他重要工作被推迟或搁置,而这一切的起因往往是工程师坚称这些改变是必要的。这样的情景屡见不鲜,许多人甚至曾是始作俑者,但通常这些故事的结局并不理想。
最终的问题仍然存在:它真的过度架构了吗?
问题架构
在软件工程领域,架构类型繁多,如企业架构、系统架构、应用架构、软件架构、云架构和集成架构等。更为复杂的是,不同的组织根据其内部需求对这些架构学科进行了独特的定义,使得明确划分每种架构的职责和界限变得困难。由于不同的软件架构在不同的组织和上下文中可能有不同的定义和职责。因此,直接将它们进行比较是不合理的,因为它们之间的差异太大,就像不能简单地将它们进行直接比较。
尽管如此,我们依然可以尝试定义“问题”架构。我认为有三种基本类型的架构可能会存在问题。
1. 不同架构
“不同架构”指的是在解决方案中对于如何处理非功能性需求存在分歧的情况。比如,针对一个面向云的解决方案,究竟应该采用云原生还是云无关的架构?稳定性和可靠性是否比吞吐量和性能更重要?资源选择的标准是成本、能力,还是两者兼顾?对于基础架构的任何担忧或抱怨,都必须与成功解决方案所需的重要非功能性需求相平衡。你可能不同意这些需求的优先级,但只要这些需求得到了满足,解决方案便是有效的,无论个人的偏好如何。 即使你认同非功能性需求,不同的工程师也可能会提出不同的解决方案:同步与异步、面向对象的继承与组合、功能或 CRUD 的 API 端点、SQL 与 NoSQL、API 优先与 MVC。
这些选择本质上都是主观的,解决方案并无绝对的对错,关键在于满足需求。 “Hello, World”程序可以通过成千上万种方式实现,每种方式都有其合理性。因此,不同的工程师会以不同的方式解决同一个问题。这种差异并非错误,更不一定是“过度架构”。如果非功能性需求明确并且得到了实现,即使你不同意某些设计和实现,这仍可能是一个成功的架构。
2. 错误架构
识别“错误架构”通常需要深入分析从概念到部署的整个过程,以找出其中的缺陷。这类架构常表现为需求定义不清、代码质量差、项目执行不力、时间表不现实,以及架构不合理。这些问题往往相互交织,难以单独解决。具体而言,错误架构的特点通常包括:
- 未能满足非功能性需求,甚至根本没有明确需求。
- 实现所需的技术技能和背景在组织内部不存在。
- 架构需要构建组织核心能力之外的组件,特别是当已有组件能够满足需求时。
- 部署的解决方案不稳定,需要定期维护以避免中断。
- 维护和扩展严重依赖于某些不可替代的个人。⠀
如果你曾参与过失败的项目,可能会对这些特征感同身受。有时,你可能会发现,所谓的“错误架构”实际上根本没有经过严谨的架构设计。更重要的是,这类项目及其最终解决方案通常难以挽救,与其相关的一切努力往往都是时间的浪费。
3. 过度架构
是否存在真正的“过度架构”情况?答案是肯定的。某些设计方案可能过于复杂,超出了实际需求。例如,一个开关的设计可能显得过于繁琐,大多数人会认为这是不必要的复杂化,但鲁布·戈德伯格和创客们可能持有不同的看法。以下经历可能是“过度架构”的典型案例;当然,也可能是错误架构,或者只是一种完全不同的架构。
问题陈述
在我担任独立软件顾问期间,曾被聘请为一位刚离职员工的替补。这位前员工设计并(大部分)实现了一个自定义框架,用以支持公司第一个真正的Web应用程序。然而,留给团队的只有少量示例实现和几乎没有文档(设计文档、使用说明等),剩下的工程师们只能试图拼凑这些碎片。为了更好地理解那段时期的背景,早期的Web开发没有真正的标准或最佳实践,开源软件还处于萌芽阶段。用户电脑的性能非常有限,难以运行复杂的浏览器端脚本。浏览器供应商对HTML标准的解释也各不相同,这是Internet Explorer主导市场的时期,与现在大不相同。我的任务是掌握这个框架,理解其内部运作,并为后续的应用程序开发制定路线图。
理解与内化
从宏观层面看,这一任务似乎相对简单:通过Java Servlet应用程序(框架)生成HTML,处理请求、响应和导航。然而,随着深入了解,问题逐渐浮现。页面之间紧密耦合,任何微小的改动都会影响到相邻页面。页面直接生成HTML,这使得在整个应用程序中保持一致性极为困难。要改变框架的默认行为,需要找到对应的钩子,而这些钩子往往数量众多且混乱不堪。尽管最初的Servlet引擎工作正常,但一旦考虑更换(如使用Tomcat以提高性能),问题就会接踵而至。这些复杂问题使得项目面临巨大挑战,成功的希望渺茫,因此必须做出艰难的决定。
决策时刻
我的结论是:现有框架勉强能够满足当前需求,但不适合未来开发。继续使用这个框架将使剩余的工程师不堪重负,项目可能永远无法完成。经理展现出深刻的洞察力,同意及时止损。最终,我们设计了一个更简单、更聚焦的框架,三周内工程师们就能够在工作模型上开始应用开发,并在两个月后成功演示了成果。最终,该框架非常成功,被用于多个应用程序,直到退役。
最终结论
将原始框架视为“过度架构”的原因包括:
- 非功能性需求的缺失:缺乏对设计和实现的必要约束。
- 自负驱动的设计:为了展示个人才华,过度追求复杂性。
- 过于复杂:难以实施、维护和支持。
- 无竞争优势:架构的复杂性并未带来竞争优势,反而大大延长了项目的上市时间。⠀
虽然这种情况也可以被称为错误架构或不同的架构,但这只是语义上的差别。无论如何,我们在改变方向前进行了充分的尽职调查,通过客观的分析,而非简单地将其贴上“过度架构”的标签,最终做出了正确的决策。
最后的思考
软件解决方案的架构形式多种多样,受到诸多外部因素的影响,如业务需求、开发需求、技术团队的经验与技能、可用技术以及组织的成熟度等。这种多样性是正常且必要的。然而,许多时候,一个架构方案会在缺乏对其背景和动机的深入理解的情况下被轻易否定。尽管在某些情况下,重新实现部分或全部解决方案确实有其正当理由,但有时这种动机可能源于工程师的个人偏好或私心。
作为负责任的软件工程师,我们应该为那些对组织最有利的方案进行辩护,而不仅仅是为技术或个人兴趣服务。这并不是说技术需求不重要,而是我们应该能够为所提出的技术方案提供充分的理由和论据。 这一过程要求我们进行深入的调查,详细记录,清晰叙述,并合理辩护。简单地将某个方案贴上“过度架构”的标签远远不够。我们必须超越简单的批评,提出经过深思熟虑和充分验证的判断,确保每个决定都是在组织的整体利益下做出的。
译者介绍
刘汪洋,51CTO社区编辑,昵称:明明如月,一个拥有 5 年开发经验的某大厂高级 Java 工程师,拥有多个主流技术博客平台博客专家称号,博客阅读量 400W+,粉丝 3W+。2022 年腾讯云优秀创作者,2022 年阿里云技术社区最受欢迎技术电子书 TOP 10 《性能优化方法论》作者,慕课网:剖析《阿里巴巴 Java 开发手册》、深度解读《Effective Java》 技术专栏作者。
原文标题:Over-Architected? Maybe, Maybe Not,作者:Scott Sosna