软件系统的熵——软件系统进化论

2021-12-16 10:21:18 浏览数 (1)

生存需要投入更多的思想和精力。宇宙的熵在升高,有序度在降低,像平衡鹏那无边无际的黑翅膀,向存在的一切压下来,压下来。可是低熵体不一样,低熵体的熵还在降低,有序度还在上升,像漆黑海面上升起的磷火,这就是意义,最高层的意义,比乐趣的意义层次要高。要维持这种意义,低熵体就必须存在和延续。——《三体》

大千世界,无奇不有,但大都逃不出规律、哲学的范畴。尽管软件开发几乎不受任何物理定律的约束,熵(entropy)对我们的影响却很大!熵是一个来自物理学的概念,指的是某个系统中的“无序”的总量。遗憾的是,热力学定律保证了宇宙中的熵倾向于最大化!

比如说:宇宙中的任何温度高的物质,总会一直慢慢的趋向于绝对零度,这是不可逆的,它普遍存在于很多领域!再比如说,我把耳机线整理整齐,然后放入口袋。但是,当我拿出来的时候,它又呈现出混乱的局面。自然界的力量总是想让耳机线趋向于混乱状态。

当软件中的无序增长时,程序员们称之为“软件腐烂”(software rot)。

导致软件腐烂的原因有很多。

首要的就是破窗户!

一扇破窗户,只要有那么一段时间不修理,就会渐渐给建筑的居民带来一种废弃感——一种职权部门不关心这座建筑的感觉。于是又一扇窗户破了。人们开始乱扔垃圾...一段时间后,废弃感变成了现实。

管束破窗户、混乱涂画和其他轻微违法事件减少了严重罪案的发生。

不要留着“破窗户”(低劣的设计、错误决策、或是糟糕的代码)不修。发现一个就修理一个。如果没有足够的时间进行适当的修理,就用木板把它钉起来。你可以添加注释、或者显示“未实现”消息,或者用虚设的数据加以替代。采取某种行动防止进一步的损坏,并说明情势处在你的控制之下。整洁、运行良好的系统,一旦窗户破裂,就相当迅速的恶化。还有其他的原因,但是与其他原因相比,置之不理都会更快的加速腐烂的进程。

你也许在想,没有人有时间到处清理项目的所有碎玻璃。如果你继续这么想,你最好计划找一个大型垃圾罐,或是搬到别处去。不要让熵赢得胜利!

也就是说,每个人都有责任去补窗户,发现一个,要么钉起来,要么补上。

名词解释:熵的本质是一个系统“内在的混乱程度”。它在控制论、概率论、数论、天体物理、生命科学等领域都有重要应用,在不同的学科中也有引申出的更为具体的定义,按照数理思维从本质上说,这些具体的引申定义都是相互统一的,熵在这些领域都是十分重要的参量。

软件熵(Software entropy)是指软件的无序程度。软件熵可用来说明软件在经过不断修改后,无序程度提高的现象。

尽管软件开发几乎不受任何物理定律的约束,熵(entropy)对我们的影响却很大。熵是一个来自物理学的概念,指的是某个系统中的 “无序” 的总量,遗憾的是,热力学定律保证了宇宙中的熵倾向于最大化,当软件中的无序增长时,程序员们称之为 “软件腐烂(software rot)”

很多元素可以崔进软件腐烂,其中最重要的一个似乎是开发项目时的心理(或文化),即使你的团队只有你一个人,你开发项目时的心理也可能是非常微妙的事情。尽管制定了最好的计划,拥有最好的开发者,项目在其生命周期中仍可能遭遇毁灭和衰败,而另一些项目,尽管遇到巨大的困难和接连而来的挫折,却成功的击败自然的无序倾向,设法取得相当好的结果。

是什么造成了这样的差异?

在市区,有些建筑漂亮而整洁,而另一些却是破败不堪的 “废弃船只”,为什么?犯罪和城市的衰败,领域的研究者发现一种迷人的触发机制,一种能够很快将整洁、完整和有人居住的建筑变成破败废弃物的机制:破窗户

一扇破窗户,只要有那么一段时间不修理,就会渐渐给建筑的居民带来一种废弃感 ---一种职权部门不关心这座建筑的感觉。于是又一扇窗户破了,人们开始乱扔垃圾,出现了乱涂乱画。严重的结构损坏开始了,在相对较短的一段时间里,建筑就被损毁的超出业主愿意修理的程度,而废弃感变成了现实。

“破窗户理论” 启发了纽约和其他大城市的警察部门,他们对一些轻微的案例严加处理,以防止大案的发生。这起了作用:管束破窗户、乱涂乱画和其他轻微的违法事件减少了严重罪案的发生。

Don't live with Broken Windows

不要容忍破窗户

不要留着 “破窗户”(低劣的设计,错误的决策或是糟糕的代码)不修。发现一个就修一个,如果没有足够的时间进行适当的修理,就用木板把它钉起来。或许你可以把问题的代码加入注释(comment out),或是显示** “未实现”** 消息,或是用虚设的数据(dummy data)加以替代。采取某种行动防止进一步损坏,并说明情势处在你的控制之下。

我们看过整洁、运行良好的系统,一旦窗户开始破裂,就相当于迅速地恶化,还有其他一些因素能够促生软件腐烂,但与其他任何因素相比,置之不理都会更快地加速腐烂的进程。

你也许在想,没有人有时间到处清理项目中的所有破玻璃。如果你继续这么想,你就最好计划找一个大型垃圾罐,或者搬到别处去。不要让熵赢得胜利。

灭火

作为参照,让我们讲述Andy的一个熟人的故事。他是一个富的让人讨厌的富翁,拥有一所完美、漂亮的房子,里面满是无价的古董,艺术品,以及诸如此类的东西。有一天,一幅挂毯离他的卧室壁炉太近了一点,着了火。消防员冲进来救火 -- 和他的房子,但他们拖着粗大、肮脏的消防水管冲进房间门口却停住了 -- 火在咆哮 -- 他们要在前门和着火之间铺上垫子,他们不想弄脏地毯,这的确是一个极端的例子,但我们必须以这样的方式对待软件,一扇破窗户、一段设计低劣的代码、团队必须在整个项目开发过程中加以忍受一项糟糕的管理决策 - 就足以是项目开开始衰败。如果你发现自己在有好些破窗户的项目里工作,会很容易产生这样的想法:“这些代码的其余部分也是垃圾,我只要照着做就行了”。项目在这之前是否一直很好、并没有什么关系:在最初得出 “破窗户理论” 的一项实验中,一辆废弃的汽车放了一个星期,无人理睬,而一旦有一扇窗户被打破,数小时之内车上的设备就被抢夺一空,车也被翻了个底朝天。

按照同样的道理,如果你发现你所在的团队和项目的代码是否漂亮:编写整洁、设计良好,并且很优雅,你就很可能会格外注意不去把它弄脏,就和那些消防员一样,即使火在咆哮(最后预期、发布时间、会展演示等等),你也不会想成为第一个弄脏东西的人。

挑战

通过调查你周边的计算 “环境”,帮助增强你的团队能力,选择两或三扇 “破窗户”,并与你同事讨论问题何在,以及怎样修理它们。

你能否说出某扇窗户何时破的?你的反应是什么?如果它人的决策所致,或者是管理部门的指示,你能做些什么?

熵增定律

熵的概念最早起源于物理学,用于度量一个热力学系统的无序程度。热力学第二定律,又称“熵增定律”,表明了在自然过程中,一个孤立的系统总是从最初的集中、有序的排列状态,趋向于分散、混乱和无序;当熵达到最大时,系统就会处于一种静寂状态。

通俗的讲:系统的熵增过程,就是由原始到死亡的过程。“熵”是“活跃”的反义词,代表负能量。

非生命,比如物质总是向着熵增演化,屋子不收拾会变乱,手机会越来越卡,耳机线会凌乱,热水会慢慢变凉,太阳会不断燃烧衰变……直到宇宙的尽头——热寂。

在软件开发、维护过程中。软件的生命力总是从最初的理想状态,逐步趋向于复杂、混乱和无序状态发展,直到软件不可维护而被迫下线或重构。这种损坏软件质量的因素的逐步增长,叫做软件的熵增现象。

系统复杂性

表象

代码混乱、新人不易上手

代码高度冗余,复用性低,开发效率低

扩展和修改困难,牵一发动全身

业务数据错乱

程序性能低下

系统难以移置

BUG率居高不下

深层原因

变更放大

认知负荷

未知的未知

复杂性的原因

复杂性是由两件事引起的:依赖性和模糊性。

1、依赖关系

依赖关系是软件的基本组成部分,不能完全消除。实际上,我们在软件设计过程中有意引入了依赖性。每次编写新类时,都会围绕该类的 API 创建依赖关系。但是,软件设计的目标之一是减少依赖关系的数量,并使依赖关系保持尽可能简单和明显。

2、模糊性

当重要的信息不明显时,就会发生模糊。

一个简单的例子是一个变量名,它是如此的通用,以至于它没有携带太多有用的信息(例如,时间)。或者,一个变量的文档可能没有指定它的单位,所以找到它的惟一方法是扫描代码,查找使用该变量的位置。

晦涩常常与依赖项相关联,在这种情况下,依赖项的存在并不明显。例如,如果向系统添加了一个新的错误状态,可能需要向一个包含每个状态的字符串消息的表添加一个条目,但是对于查看状态声明的程序员来说,消息表的存在可能并不明显。

不一致性也是造成不透明性的一个主要原因:如果同一个变量名用于两个不同的目的,那么开发人员就无法清楚地知道某个特定变量的目的是什么。

3、依赖性和模糊性的积累

复杂性不是由单个灾难性错误引起的;它堆积成许多小块。单个依赖项或模糊性本身不太可能显着影响软件系统的可维护性。之所以会出现复杂性,是因为随着时间的流逝,成千上万的小依赖性和模糊性逐渐形成。最终,这些小问题太多了,以至于对系统的每次可能更改都会受到其中几个问题的影响。

降低复杂性的方法

1、日常开发留出一点战略规划时间

大多数程序员日常以战术编程的心态来进行软件开发。例如新功能或错误修复。乍一看,这似乎是完全合理的:还有什么比编写有效的代码更重要的呢?但是战术编程几乎不可能产生出良好的系统设计。

与之相对应的是战略规划,成为一名优秀的软件设计师的第一步是要意识到仅工作代码是不够的。尽管代码当然必须工作,但不应将“能跑通的代码”视为主要目标。战略设计的主要目标必须是制作出出色的设计,考虑后续的可维护性及扩展性。

战略性编程需要一种投资心态。尽管前提投入会比战术编程花费更多的时间,但随着系统的迭代,战略编程的优势就开始逐渐显现。

当然既然是投资,就要考虑投入产出比,不应该吹毛求疵,只要发现一点不合理的地方就整体大重构。推荐的方式小步快跑的方式,在日常开发中留出5%-10%的时间来做战略设计。

2、模块的设计

开发一个新模块,如果有不可避免的复杂性。两种设计思路哪个更好:1、应该让模块用户处理复杂性,2、应该在模块内部处理复杂性?如果复杂度与模块提供的功能有关,则第二个答案通常是正确的答案。

作为开发人员,很容易以相反的方式行事:解决简单的问题,然后将困难的问题推给其他人。如果出现不确定如何处理的条件,最简单的方法是引发异常并让调用方处理它。这样的方法短期内会使您的生活更轻松,但它们会加剧复杂性。大多数模块拥有的用户多于开发人员,因此此模块还会有许多人来维护。作为模块开发人员,您应该努力使模块用户的生活尽可能轻松,即使这对您来说意味着额外的工作。另一种更好的方法是,模块具有简单的接口比简单的实现更为重要。

模块是设计应该是深的,最好的模块是那些其接口比其实现简单得多的模块。这样的模块具有两个优点。1、一个简单的接口可以将模块强加于系统其余部分的复杂性降至最低。2、如果以不更改其接口的方式修改了一个模块,则该修改不会影响其他模块。如果模块的接口比其实现简单得多,则可以在不影响其他模块的情况下更改模块的许多方面。

3、如何编写注释

编写注释的原因是,使用编程语言编写的语句无法捕获编写代码时开发人员想到的所有重要信息。注释记录了这些信息,以便后来的开发人员可以轻松地理解和修改代码。注释的指导原则是,注释应描述代码中不明显的内容。

注释的最重要原因之一是抽象,其中包括许多从代码中看不到的信息。抽象的思想是提供一种思考问题的简单方法,但是代码是如此详细,以至于仅通过阅读代码就很难看到抽象。注释可以提供一个更简单,更高级的视图(“调用此方法后,网络流量将被限制为每秒 maxBandwidth 字节”)。即使可以通过阅读代码推断出此信息,我们也不想强迫模块用户这样做:阅读代码很耗时,并且迫使他们考虑很多不需要使用的信息模块。开发人员应该能够理解模块提供的抽象,而无需阅读其外部可见声明以外的任何代码。

4、重视命名

名称是一种抽象形式:名称提供了一种简化的方式来考虑更复杂的基础实体。良好的名字是一种文档形式:它们使代码更易于理解。它们减少了对其他文档的需求,并使检测错误更加容易。相反,名称选择不当会增加代码的复杂性,并造成可能导致错误的歧义和误解。

0 人点赞