译:软件复杂性的两个根本原因

原文:https://pressupinc.com/blog/2014/05/root-causes-software-complexity/
作者:David Hayes
译者:ChatGPT 4 Turbo

除了几个值得注意的例外,软件制造者讨厌处理现有的代码。当然,他们很乐意拥有解决问题的工具,但如果让他们选择一堆“基本解决”了一个现有问题的代码,或者从头开始的机会,许多人会盲目选择从头开始。

易于使用的软件是直接的、易于理解的、易于添加新功能和能力的,并且很难被破坏。而且很少有软件具备这些所有的品质。

选择从零开始的原因是,大多数现有的软件并不是很好。不是因为它没有为业务服务,或者不是功能性的,而是因为它缺乏使其易于使用的品质。易于使用的软件是直接的、易于理解的、易于添加新功能和能力的,并且很难被破坏。而且很少有软件具备这些所有的品质。

世界上大多数正在使用的软件在这些领域中至少在一个方面明显欠缺。我会挑一个我们在 Press Up 非常熟悉的例子:WordPress。它的使用非常广泛,执行许多网络发布职责,和其他任何东西一样好,如果不是更好。由于其广泛使用“钩子”,它相对容易扩展(一旦你理解了整个基于事件的模型)。但它并不容易理解——它是一个结构不清晰的混乱,缺乏合理性或其结构的意义。一个函数可能会也可能不会在其名称或功能中使用特定的动词和惯例。函数调用的参数并不总是容易猜测的。并且,在许多情况下,自定义的难以理解的函数调用其他自定义的难以理解的函数,深入到很大的深度。WordPress 因此有点名声——这种抱怨经常被简化为“意大利面代码”——但对于任何现有的软件项目来说,如果只是以不同的方式,类似的问题并不少见。

固有复杂性:你正在解决的问题之难

这就是固有复杂性:软件试图简化并改进的实际问题的难度。

软件项目变得难以处理且不那么有趣的部分原因是,它们正在解决的问题本身就很难处理且不好玩。这就是固有复杂性:软件试图简化并改进的实际问题的难度。

所有软件都有一些固有或本质复杂性,这就是为什么要制造这个软件的原因。如果所讨论的任务如此简单以至于一个人可以不假思索地完成它,那么固有复杂性就很低,编写相应的软件也可能相对容易。但是,如果一个任务与某个如此复杂的领域相关,以至于整个行业都存在其周边,你可以确信那里有很多固有的复杂性。无论哪种情况,软件需要解决的关键问题都是问题中的“固有”复杂性。

american-taxes-1040

典型例子是具有高内在复杂性的领域:金融监管合规。因为两个庞大而薪酬颇丰的职业 —— 律师和会计师 —— 与此密切相关,我们可以断定这里有高度的固有复杂性。即使是拥有良好记录的专业会计,要解决美国个人纳税申报也会觉得困难。而未经培训的个人遇到这个问题时,理所当然会感到不知所措。整个领域是一个充满规则、例外以及特殊情况下的例外之例外的混乱局面。我们谈论的是一个具有高固有复杂性的问题。这里的软件很可能也必然具有高度的复杂性,尤其是与“简单”的网页发布相比。(作为实践者,我不得不承认那个贬低的笑话:网页发布系统实际上就是一种将一串串字符串巧妙结合在一起的方式)。

偶发复杂性:当程序员让自己的生活变得更糟

偶发复杂性是指软件中的一切困难,但实际上并非必要的。

我想我最初是从 J. B. Rainsberger 那里听到偶发和固有复杂性框架的。在解释它时,他说了类似这样的话:“固有复杂性是因为问题本身很难,偶发复杂性是因为我们【软件制造者】工作不力。”这或许说得有点直白,但它有一种简单(痛苦的)准确性。偶发复杂性是指软件中的一切困难,但实际上并非必要的。

软件中的所有复杂性都可以准确地视为属于这种固有与偶发二元对立中的一方。拿出你软件中所有糟糕的东西,减去那些因为是困难问题而糟糕的部分,你就剩下了这个项目因为过去工作人员的错误而积累的(无论是小问题还是大难题,我就让你选择)偶发复杂性。(虽然这个类比有点崩溃,但是这种偶发复杂性与“技术债务”的概念大致相当。)

偶发或 偶然复杂性 的许多可能原因超出了这篇引言的范围。但为了快速列举一些我最近一直在思考的偶发复杂性的原因:

  • 选择错误的工具。 有时候程序员,或许是急于开始,或太过热爱某个特定工具,会用螺丝刀去敲钉子。也许是一个三页的网站导致某人使用了 Ruby on Rails,或者是某人坚持要用 Objective C 构建 RESTful 网络 API,无论如何,并不是工具不好,只是它被应用在了错误的问题上。
  • 选择错误的抽象。 这是一个微妙但还是相当容易犯的错误。有时你会发现一些软件完全是关于特定词汇的 —— 比如图书馆记录 —— 但是它们却是使用了一些其他的词汇编写的,比如内容管理系统中的“帖子”数据类型。确实有一个抽象概念,但它并不正确,而且由于需要调和这两个概念之间的差异,所有的事情变得更加困难。
  • 不选择任何抽象。 起初,这看起来可能微妙,但其实和上面的案例相似。这里的区别在于,有人创建了一个活动日志系统,但是从未创建过任何形式的“活动”概念,也没有聚合成一个“日志”。相反,他们有一个非常字面的 CRUD(创建、读取、更新、删除)应用程序,完全没有任何抽象。数据库调用隐藏了所有业务概念,因此系统很难推理。没有任何有用的高层概念的抽象,所有行为都是用低级的代码表达的,而且做出改变需要同时考虑多个抽象层面,这是相当困难的。

这些不是导致附带复杂性的唯一原因 —— 在你创建软件时未能确保所有元素的质量,以及未能隔离软件中的独立单元是两个立即浮现的巨大问题领域。但是附带复杂性的要点是,无论它看起来如何,它都不是解决问题严格需要的。

那么,软件复杂性对我意味着什么?

显然,软件制造者,和其他人一样,是会犯错误的人类。任何软件都几乎肯定会出现一些附带复杂性。但最小化这一点是一个强大的概念,需要理解并考虑在构建良好的系统和软件的追求中,这些系统和软件不仅仅是在交付时工作,而是能够为客户和合作伙伴长期工作。理解你的软件之所以难以理解是因为它必须如此,还是因为它没有高效构建,这是通向能够定期交付出色且寿命长的结果的有力的一步。