跳转到内容

Big Ball of Mud:真实环境下的架构

关于软件架构,我们并不缺书,也不缺文章。

我们知道一套“干净的架构”可以是什么样子。我们知道分层、Ports and Adapters、业务切分、模块化 Monolith、微服务、Event-driven Architecture、Domain-driven Design,以及数量多到几乎无法完整列举的 Pattern 和原则。

在纸面上,软件架构常常从一张白纸开始。

现实里,它更常从一个 Repository 开始。

而这个 Repository 已经十年了。

它经历过几代开发者、不同的架构风格、不同的 Framework、时间压力、组织重组、战略调整、被终止的产品、比下一轮维护更重要的 Deadline,以及那些“以后再处理”却再也没有被碰过的临时方案。当然,也少不了那个原本只打算过渡一下、结果已经在线运行七年的解决方案。

这个系列讨论的,就是这样的系统。

不是架构应该长什么样。

而是当它已经不再是那个样子时,会发生什么。

Brian Foote 和 Joseph Yoder 早在 1997 年就描述了 Big Ball of Mud。他们的观察非常冷静:软件架构领域投入了大量精力研究秩序良好的架构风格,但现实中极其常见的一种形态,恰恰是缺少清晰可辨结构的系统。

Big Ball of Mud 并不等于旧软件。

它也不自动等于 Monolith。

一个大型 Monolith 完全可以拥有优秀的结构:清晰的业务边界、一致的依赖方向,以及反复使用且能够解释的解决原则。

Big Ball of Mud 真正丢失的是另一件东西:

能够被识别的内部结构。

边界开始模糊。职责相互重叠。原本局部的决策逐渐扩散。过去不存在的依赖一点点长出来。Shared Service 越来越“Shared”。例外变成规则。到最后,关于“哪里可以安全修改”的知识,不再主要存在于架构里,而是存在于少数人的脑子里。

这通常不会在某个瞬间轰然发生。

Big Ball of Mud 大多是一点一点形成的。

也正因为如此,它才危险。

我职业生涯中的大部分工作,都不是从 Greenfield 开始的。

粗略回头看,我深入参与过的系统里,大概有 90% 已经以某种形式发生了架构侵蚀。

这当然不是统计数据。只是我个人的样本,而且很可能有明显偏差。尤其近几年,我经常是在项目已经出现问题之后才被拉进去。一个总在救火时才被叫来的人,本来就会看到比平均水平更多的火灾。

即便如此,我越来越难把这些经历全部解释成个人偶遇。

至少,一些研究给出了值得重视的信号。

Besker、Martini 和 Bosch 研究了 Technical Debt 会让开发者损失多少工作时间。在他们的实证研究中,开发者平均报告大约 23% 的工作时间因 Technical Debt 而损失。额外测试和对既有代码的额外分析,是其中的重要成本来源。

Besker、Ghanbari、Martini 和 Bosch 的后续研究则专门关注人的一面。Technical Debt 与缺乏进展感和时间浪费相关;研究结果表明,它可能损害开发者士气,而主动管理 Technical Debt 则可能产生相反效果。

这一点值得注意。

因为谈 Technical Debt 时,我们通常谈钱。

或者 Story Point。

或者 Lead Time。

很少有人认真讨论:每天必须在这种系统里工作,会对人造成什么影响。

我在医疗环境和 Public Sector 中见过尤其严重的系统。结合各种交流和报告,很容易产生一种印象:某些行业似乎特别容易积累 Legacy。

但我没有找到一个严肃可信的“最糟行业排行榜”。

我也不喜欢为了让一个有意思的故事显得更有分量,就给它配上一组编出来的统计数字。

能够被证实的事实已经足够明显。

一项针对三个真实 Automotive 软件系统的实证研究发现,Feature 之间存在高度互联。不到 12% 的架构组件承载了超过 90% 的 Feature Dependency。与此同时,开发者并没有意识到其中很大一部分依赖关系。

在 Public Sector,这类问题甚至被更直接地记录下来。

英国政府在 2025 年估计,中央政府部门大约 28% 的系统仍属于 Legacy Technology。在部分 NHS 机构和警方机构中,根据组织不同,报告比例大约从 10% 一直到 60% 甚至 70%。

美国 Government Accountability Office 在 2025 年检查了联邦机构的 69 个 Legacy System。其中最关键的 11 个系统已有约 23 到 60 年历史,每年合计花费约 7.54 亿美元用于运行和维护。多个系统仍依赖过时的编程语言、不再受支持的硬件或软件,或者存在已知安全漏洞。

这并不意味着每一个 Legacy System 都是 Big Ball of Mud。

这个区别很重要。

但它说明,我们讨论的不是软件开发里的罕见边缘现象。

我们不断构建新系统。

与此同时,行业里相当一部分工作仍然是在让旧系统继续活着。

下面这个 Graph 来自我曾经参与过的一个真实 Frontend。

为了公开发布,名称已经全部匿名化,并被替换成虚构的 ACME Factory Domain。但结构本身没有改变:Node、依赖关系、规模和 Topology 都来自原始分析结果。

先不要试图理解它。

随便抓住一个节点,拖动一下。

Interaktiven Dependency Graph öffnen ↗

随便拖动一个节点。不要只看哪些节点跟着移动,也观察松手之后,这个系统还会继续反应多久。

现在对我来说,这张图最有意思的已经不是它有多大。

而是它如何运动。

几乎无论你抓哪个节点,运动都会传播到整个系统。松手以后,Graph 也不会立刻安静下来。它会持续震荡很长一段时间。

当然,从技术上说,这首先只是 D3 Force Simulation 的物理行为。D3 正在重新排列大量彼此连接的 Node。

但如果要给“在一个高度耦合系统里工作”找一幅视觉隐喻,我很难想到比这更合适的图。

一次修改可以在局部完成。

它的影响却未必局部结束。

你今天修改一个 Service。

代码可以编译。测试通过。Feature 能用。

几天后,另一个完全不同的地方出现了问题。

发现问题的人也许根本不知道之前那次修改。做修改的人此时也不再会自然地把两件事联系起来。原因和结果不只在结构上相距很远,在时间上也已经分开。

系统仍在震荡。

到了这里,只谈 Clean Code 对我来说已经不够了。

在一个严重侵蚀的系统里,上升的不只是维护成本。

开发需要装进脑中的 Context 也会越来越大。

一次修改需要理解越来越多的系统区域。开发者花更多时间分析,修改更加谨慎,Review 更困难,测试范围更大,问题也更难定位。

与此同时,知识开始集中。

某些人仍然理解特定区域。

于是大家越来越频繁地去找这些人。

他们帮助同事,分析 Side Effect,处理 Production Issue,承担 Review,解释历史决策。同时还被期待去解决那些结构问题——而他们之所以一直被需要,恰恰就是因为这些问题一直没有解决。

于是一个循环形成了:

架构越有问题,组织越依赖少数知识持有者。

组织越依赖这些人,他们就越没有时间真正解决让自己成为瓶颈的问题。

而一旦其中一个人离开,消失的不只是开发产能。

还包括架构知识。

我见过这样的系统会对人造成什么影响。

我曾在一个项目的两年内经历 12 次离职。两位开发负责人离开。三位与我直接合作的开发者因为 Burnout 无法继续工作。

我见过有人被这样的系统逼到绝望。

而我自己也在那个项目里失败过。

失败的不是找到更好的技术设计。

而是在组织、理解、优先级和阻力面前没有推动成功。

到某个阶段,只在技术上正确已经不够了。

Big Ball of Mud 不是一张 Refactoring Ticket

Section titled “Big Ball of Mud 不是一张 Refactoring Ticket”

也正因为如此,我不想在这个系列里假装,只要加几条架构规则,就能修好一个 Big Ball of Mud。

当然,我们会详细讨论技术上的行动方式。

会讨论如何重新让边界可见,如何分析依赖,哪里可能形成切口,什么时候适合 Strangler Approach,什么时候应该先稳定再改造,以及怎样限制风险,让变化重新能够局部发生。

但这些会放在后面。

因为首先要面对一个更不舒服的事实:

Big Ball of Mud 从来不只是技术问题。

它的架构是在多年里与组织共同演化出来的。

其中包含优先级、团队结构、预算决策、时间压力、人员流动、Release Process、缺失的知识、职责边界以及政治性决策。

所以,如果只修改代码,很多时候你只改变了整个系统的一部分。

而且有时还是较小的那一部分。

Technical Debt 之所以叫 Debt,并不是偶然。

负债本身并不一定不理性。

今天为了速度选择一个务实方案,并接受未来更高的成本,完全可能是正确决定。

真正的问题出现在:没人再知道债务有多大。

没人知道是谁借的。

没有还款计划。

而且不断借新债,只为了支付旧债的利息。

经济后果一开始往往看起来只是开发问题:

Feature 越来越慢。

测试越来越贵。

故障分析越来越久。

Release 风险越来越高。

但到某个阶段,它们会变成公司问题。

Modernization 贵到一次次被推迟。新产品必须继续兼容旧结构。安全风险无法局部消除。一次业务变化需要启动大型技术项目。

系统开始替公司做决定。

而不是公司决定系统应该怎样变化。

因此,这个系列同样关心 Repository 另一侧发生了什么。

当没人能够可靠判断一次修改会产生哪些影响时,团队会发生什么?

当错误无法再清晰归属于某个区域时,职责会发生什么?

当新开发者需要几个月才能知道哪里可以安全工作时,Onboarding 会发生什么?

当资深开发者的经验最终变成永久性的值班职责时,又会发生什么?

如果那些最明确指出结构问题的人,同时也被认为是“让修改变慢、变贵的人”,组织会发生什么?

如果所有人都知道系统在根本上有问题,却没有人还能看到一条现实的出路,又会发生什么?

这些不是 Angular 问题。

这些是组织问题。

这同样属于诚实分析的一部分。

总会有行动选择。

但并不是每一种选择都叫:

把这个系统修好。

你可以尝试改变它。

可以学会与它共存,并限制风险。

可以逐步替换某些区域。

可以重建。

可以接受某些结构不会消失。

作为开发者,也可能有一天决定自己不再愿意成为这个系统的一部分。

哪种决定合理,并不只取决于 Code Quality。

它取决于成本、风险、人、时间、产品战略,尤其取决于组织是否真的允许改变发生。

技术分析可以告诉你问题在哪里。

它无法强迫一个组织愿意解决问题。

因此,这个系列不会是一组恐怖故事。

也不会是一份“从 Big Ball of Mud 到完美架构的 12 个简单步骤”。

我们首先会研究:Big Ball of Mud 到底是什么。

它如何形成。

怎样识别。

为什么 Monolith 并不等于 Big Ball of Mud。

为什么这种系统往往能够惊人地运行很久。

它会产生哪些技术、经济和人的风险。

为什么组织即使已经看见问题,也常常不采取行动。

以及,当你自己正身处其中时,还有哪些现实选择。

其中会有技术工具。

架构 Pattern。

分析方法。

Migration Strategy。

但它们不会排在最前面。

最前面是另一件事:

认真对待这个问题。

不只是为了代码。

也是为了每天必须修改这些代码的人。


Brian Foote, Joseph W. Yoder: Big Ball of Mud. Pattern Languages of Program Design 4, 最初发表于 1997 年。

Terese Besker, Antonio Martini, Jan Bosch: Software Developer Productivity Loss Due to Technical Debt – A Replication and Extension Study Examining Developers’ Development Work. Journal of Systems and Software 156, 2019, pp. 41–61. DOI: 10.1016/j.jss.2019.06.004.

Terese Besker, Hadi Ghanbari, Antonio Martini, Jan Bosch: The Influence of Technical Debt on Software Developer Morale. Journal of Systems and Software 167, 2020, 110586. DOI: 10.1016/j.jss.2020.110586.

Jesper Olsson, Erik Risfelt, Terese Besker, Antonio Martini, Richard Torkar: Measuring Affective States from Technical Debt: A Psychoempirical Software Engineering Experiment. 2020.

Andreas Vogelsang: Feature Dependencies in Automotive Software Systems: Extent, Awareness, and Refactoring. Journal of Systems and Software, 2019. DOI: 10.1016/j.jss.2019.110458.

UK Department for Science, Innovation and Technology / Government Digital Service: State of Digital Government Review. 2025.

U.S. Government Accountability Office: Information Technology: Agencies Need to Plan for Modernizing Critical Decades-Old Legacy Systems. GAO-25-107795, July 2025.