大泥球

不久前,我偶然发现了一点关于泥球图案的论文.

这是一篇有趣的文章。在这篇博客帖子中,我补充了一些我自己的想法,关于我发现遗漏、误导性解释或完全不同意的事情。公平地说,这篇论文已有17年历史。我只是在读完它后才注意到这一点,尽管它确实解释了为什么某些流行词,如“敏捷”和“scrum”没有被使用,尽管作者们清楚地描述了相同的概念。

不知道大泥球模式是关于什么的?您可以检查维基百科文章或者简单地阅读报纸上的引言:

虽然很多人都将注意力集中在高级软件架构模式上,但实际上,什么是实际的标准软件架构却很少讨论。本文研究了最常用的架构:大泥球。一个大泥球是一个随意的,甚至是随意的,结构化的系统。它的组织,如果可以这么说的话,更多地是由权宜之计而非设计决定的。然而,它的持续流行不能仅仅表明人们普遍忽视了建筑。

“大泥球”是一个结构杂乱无章、杂乱无序、邋遢邋遢的、管道式和电线式的意大利式丛林。这些系统显示出不受监管的增长和反复的权宜之计的明显迹象。信息在系统的远程元素之间杂乱无章地共享,经常到几乎所有重要信息都成为全局信息或重复信息的程度。该系统的总体结构可能从未得到很好的定义。如果是这样,它可能已经被侵蚀得面目全非了。有一点架构敏感性的程序员避开了这些泥潭。只有那些对建筑学漠不关心,也许对修补这些溃败堤坝上的洞这一日常琐事的惰性感到满意的人,才会满足于在这种系统上工作。

如果您能够区分好的代码和意大利面代码,并且已经在软件开发领域工作了一段时间,那么您无疑也会得出这样的结论:这确实是最普遍的“架构模式”。本文概述了一些有趣的因素,并对其原因和影响进行了一些类比。作为一名软件工艺倡导者,我的主要兴趣是更好地理解这些,以便更好地处理它们。

建筑通常被视为一种奢侈或装饰,或是对那些不关心底线的镀金百合花爱好者的放纵追求。

这与我迄今为止所观察到的我谦逊的职业生涯产生了很大的共鸣。事实上,多年来我一直对软件设计和架构持这种态度。我几乎是出于偶然。这种态度的普遍性使我们很难意识到它是短视的。教育也可能在一定程度上受到指责。至少我得到的那个完全没有强调好设计的重要性。

在我看来,许多人似乎认为,当一个人需要在设计一个系统时投入脑力,以后也需要同样的努力来理解它。虽然通过缺乏经验,或者当一个人试图混淆一个系统的时候,这当然是可能的,当一个有经验的人追求好的设计时,情况恰恰相反。在设计中投入的精力是使其简单化,而不是复杂化。一个经过深思熟虑的设计将比一堆没有考虑其组织的代码更简单。

事实上,不成熟的体系结构在不断增长的系统中可能是一个优势,因为数据和功能
可以迁移到系统中的自然位置,而不受人为建筑限制。
过早的体系结构可能比没有体系结构更危险,因为未经验证的体系结构假设反过来
变成阻碍进化和实验的紧身衣。

我当然明白这里的观点,并同意架构约束对项目来说可能非常危险。我在这一段中绝对反对的是,成熟的体系结构包含这些约束,而不成熟的体系架构不包含这些约束。架构的主要任务之一是不要做出选择,并尽可能长时间地延迟它们。

示例:架构决策是抽象应用程序的存储机制。现在可以在不创建完整MySQL实现或其他东西的情况下开发它。随着存储被抽象化,您可以使用一些简单的内存数据来开发大多数应用程序。然后,当您对项目和实际需求有了更多的经验后,您可以决定在实际实现中使用什么技术。您将能够在不同的上下文中使用不同的实现。与此相比,Big Ball of Mud方法没有使用抽象。在这种情况下,代码直接绑定了实现,您不仅决定使用特定技术,还以这样一种方式绑定到它,从而大大限制了项目的演进选项。

建筑是一种关于未来的假设,它认为随后的变化将局限于该建筑所包含的设计空间的那一部分

这与前面的报价基本一致。建筑无疑是关于未来的假设。做好架构需要平衡许多力量和可能性。如前所述,我们的主要目标之一是保持自己的选择余地。虽然人们可以猜测未来并区分可能和不可能,但没有人能够预测。一个好的建筑师没有做到的是把所有的钱都押在一个特定的赌注上,除非无法合理避免。

“大泥潭”可能被认为是一种反模式,因为我们的目的是展示在破坏建筑的力量面前的被动是如何导致泥潭的。然而,它不可否认的受欢迎程度导致了一个必然的结论,即它本身就是一种模式。对于在软件开发环境中生成工作系统的问题,它无疑是一个普遍的、反复出现的解决方案。当人们面对上述各种力量时,这似乎是一条阻力最小的道路。

阻力最小的路径。

某篇论文提到,创建好的体系结构需要努力,就像所有降低熵的东西一样。在许多情况下,这种努力是值得的。

然而,这不是最快的途径。编写初始代码的阻力很小,尽管您会很快通过技术债务、调试和尝试找出前一段时间编写的代码实际打算实现的目标来支付费用。似乎许多开发人员没有意识到这些痛苦主要是由于他们所走的“捷径”造成的,并且在很大程度上是可以避免的。

谈到软件架构,形式遵循功能。系统架构元素的独特身份通常在代码工作后才开始出现。

领域经验是任何框架设计工作的基本要素。在最好的情况下,很难尝试遵循前端加载、自顶向下的设计过程。在不了解领域的架构需求的情况下,这样的尝试即使不算鲁莽,也为时过早。通常,在生命周期的早期获得领域经验的唯一方法是从其他人那里雇佣以前在某个领域工作过的人。

领域驱动设计提倡通过领域设计者咨询领域专家的知识处理过程对模型进行迭代细化。

事实上,一些工程师在学习如何驾驭这些泥沼以及如何引导其他人通过泥沼方面尤其熟练。随着时间的推移,架构和技能之间的这种共生关系可能会改变组织本身的特征,因为沼泽指南比架构师更有价值。根据康威定律[科普利1995年],建筑师徒劳地离开,而掌握了他们在图像中构建的系统的模糊细节的工程师占了上风。[Foote&Yoder 1997]甚至观察到,由于难以理解和更改,难以捉摸的代码实际上可能比优秀的代码更有生存优势。这种优势可以扩展到那些能够找到绕过此类代码的方法的程序员。

这肯定会让我感到有趣,并在很大程度上解释了为什么一些组织中最“资深”的技术人员对软件设计的基本知识一无所知。然后,他们会因为自己的开发伙伴制造的混乱而受到尊敬。

在系统演化的原型和扩展阶段,基于白盒继承的代码借用和轻松的封装方法是常见的。后来,随着系统经验的积累,体系结构领域的纹理变得清晰可见,更耐用的黑盒组件开始出现。换句话说,如果系统最初看起来像一个大泥球,那也没关系,至少在你更清楚之前是这样。

我绝对不同意这一点。除非您正在创建一个原型(不会投入生产)或类似的东西,否则您的系统看起来像一个大泥球是不可能的。一切都应该从一开始就闪亮而完美吗?不,当然不是。这是不可能的。只要情况得到控制,在某些地方制造一点混乱通常是可以的。(管理技术债务.)当您的代码成为一个大烂摊子时,技术债务将不再受控制,您将面临严重的问题。从大泥球到稳健的设计是非常困难的。因此,建议从一个大泥球开始是好的,这是垃圾。

它们也可以随着逐渐维护和压力增长影响成熟系统的结构而出现。一旦一个系统开始运行,鼓励其增长的一个好方法就是保持其运行。我们必须注意,这种逐步修复的过程不会腐蚀其结构,否则可能会产生一大团泥浆。

是的,我们应该保持警惕,不要让代码腐烂到让系统变成一个大烂泥球的地步。持续的改革和遵循所谓的“童子军规则”是解决这个问题的一个重要部分。

[Foote&Opdyke 1995]中的原型阶段和扩展阶段模式都强调,在做出持久的建筑承诺之前,一段时间的探索和实验通常是有益的。

我同意这一点,尽管人们应该记住,架构的目标不是把事情固定下来,然后做出以后很难更改的决策。

时间,或者说时间的缺乏,往往是驱使程序员编写THROWAWAWAY代码的决定性力量。花时间编写一个适当的、经过深思熟虑的、有充分文档的程序可能需要更多的时间来解决问题,或者需要更多的时间来解决问题。

THROWAWAY代码通常是作为重用其他人更复杂的代码的替代方法编写的。当最后期限迫在眉睫时,你可以自己编写一个草率的程序,这一点比学习和掌握其他人的库或框架的未知成本更重要。

编写原型代码并没有什么错,只是为了看看某些东西是如何工作的,或者是为了测试某个特定的方法是否有效。

然而,原型应该被视为原型,而不是作为非原型部署。这对开发人员来说可能是一件容易的事情,尽管这显然也是不负责任的。你给公司带来了巨大的负债,或者把它交给了你的客户。随着维护成本飙升,进一步的开发需要很长时间,以及客户转向更少缺陷的软件,这种责任可能会让他们花费大量资金。由于您的客户无法了解代码的真实状态,也无法意识到接下来必然会发生什么,这使得情况更加复杂。

其他形式的一次性代码包括卡塔斯这是一种为开发人员精心设计的结构化实践形式,以及“spike”,即在编写真正的交易之前,您通常会编写一些代码来探索一些API。

与重写它们相比,让它们保持在空中所需的能量要少得多。

在这里我想补充一点,改写《大泥球》可能是个坏主意。除非所讨论的系统非常小,否则这样做可能不现实。在处理此类遗留系统时,建议采用一种迭代过程,即通过分解、测试等方式来改进系统。

总体规划往往僵化、误导和过时。用户的需求随时间而变化。

如果“总体规划”指的是前期的大设计,那么是的,我同意。如前所述,关于设计和架构,防止过早地做出决策是至关重要的。客户一开始并不知道他们真正需要什么,所以迭代方法是合乎逻辑的。

成功的软件吸引了更广泛的受众,反过来又可以对其提出更广泛的需求。这些新需求可能会与原始设计的细节背道而驰。尽管如此,它们经常可以被解决,但代价是要跨越现有架构假设的粒度。

这再次将建筑描绘成僵化的源泉。的确,有时人们会发现一个特定的设计不起作用,需要对其进行改进或删除。为了使这个不可避免的过程尽可能简单,应该编写干净、设计良好的代码。如果你有一堆乱七八糟的意大利面,那么它会变得更加僵硬,并且无法应对不断变化的需求。事实上,一个好的领域模型往往会随着时间的推移变得越来越强大,为企业带来许多宝贵的机会。

当设计师面临着选择是从头开始构建优雅的东西,还是破坏现有系统的架构以快速解决问题时,架构通常会失败。事实上,这是系统演化的自然阶段[Foote&Opdyke 1995]。这可能被认为是混乱的厨房阶段,在此期间,系统的各个部分散落在柜台上,等待最终的清理。危险在于清理工作从未完成。有了真正的厨房,卫生委员会最终会介入。唉,就软件而言,很少有相应的机构来监管如此肮脏的情况。不受控制的增长最终可能是一种恶性力量。

当然,有时系统的某些部分会变得有些混乱。有时,仅仅复制粘贴某个东西,或者在不属于它的地方添加一个方法,都是有实际意义的。有时——不是大多数时候。如果你有一个完善的系统,具有高内聚性和低耦合性,那么与缺乏可感知的设计相比,这种情况会更少。人们应该注意在哪里做这件事。如果它不在抽象的后面,那么腐烂很容易传播到系统的其余部分。同样明智的做法是考虑到谁在开发代码库,尤其是他们的经验和态度。没有经验或纪律严明的人很容易看不到或不关心他们正在创建与应该首先清理的东西的绑定。

当进行持续的清洁和重构时,混乱会被控制在很小的范围内。如果警惕性不够,它们很容易在整个代码库中造成严重问题。

维护需求已经积累,但检修是不明智的,因为您可能会破坏系统。有时可能有理由对一个系统进行大修,但这样做通常充满了风险。一旦系统恢复,很难从大量修改中辨别出哪些可能导致了新的问题。因此,尽一切努力维护软件并使其继续运行。让它继续工作。

如前所述,我认为大改写确实是个坏主意。无论如何,我们都不应该让一个系统恶化为一个大泥球。当需要处理遗留系统时,建议采用谨慎的逐步方法。首先测试相关部分,然后慢慢重构修改代码的艺术迈克尔·费瑟斯(Michael Feathers)。

如果你不能轻易地消除混乱,至少要把它封锁起来。这样可以把混乱限制在一个固定的区域,让它远离视线,并可以为额外的重构创造条件。人们经常会构建一个FA J ADE[Gamma et al.1995]来给在RUG下扫过的不愉快带来一张和谐的“漂亮的脸”。

这是一种非常有效和务实的方法。为了打破依赖关系并能够使用干净的接口,我经常创建新的接口(语言结构),然后制作一些简单的实现,作为旧代码的适配器。

在使用这项技术时,我收到了一些人的投诉,他们说“你并没有真正解决问题”。事实上,混乱还没有消失。这仅仅是这样做的第一步,这既减少了混乱造成的损害,又能进一步清理。

其次,体育场试图为棒球和足球观众提供一个廉价的通用解决方案,这损害了两者的需求。这里可能有关于意外需求和设计通用组件的经验教训吗?

这里肯定有一个教训是的。然而,问题不在于一般性。这个例子违反了单一责任原则,也违反了糟糕的接口隔离,给我的印象更深刻。不同级别的代码应该做一件事,并且做得很好。这适用于函数、类、组件和系统。

这也让我想起了YAGNI。我经常看到人们通过添加对“可能需要的”东西的支持,使功能级别上的东西比它们需要的更通用。当然,在有些情况下,这是有意义的,因为有些事情以后很难改变。然而,在其他许多情况下,只需要处理一些任意的东西,或者接受一个从未通过的可选参数。这样一来,基本上很难发现死代码,而人们往往没有意识到这是死代码,然后做疯狂的学生来支持它。

变化:尽管软件是一种高度可塑性的媒介,如富尔顿县体育场,但新的需求有时会以某种方式跨越系统的架构假设,使其几乎不可能适应。在这种情况下,完全重写可能是唯一的答案。

只有当你的架构糟透了。不断变化的需求可能会导致一些东西被废弃,新的东西被编写,旧的东西被重新安排。如果需要全面重写,您可能需要考虑解雇您的“架构师”。

我对这份文件的评论到此结束。我们去清理一些代码🙂

关于的3个想法“大泥球”

留下回复

该网站使用Akismet来减少垃圾邮件。了解如何处理您的评论数据.