方案的优雅
从事 IT 工作时,人们很容易把注意力集中在庞大而复杂的方案上。似乎好的方案就一定在那里–庞大的方案、海量的软件、所有最新的设备。我们所做的事情令人兴奋,因此很容易被这种势头裹挟。做有挑战性的大项目很有趣。听到其他 IT 专业人士在做什么、其他公司如何解决难题,以及与那些有大型系统要卖给我们的供应商交谈,这一切都增添了兴奋感,使人很容易丧失对范围和目标的把握。如此司空见惯地看到针对简单问题的庞大而过度的方案,以至于这似乎就是 IT 本该有的样子。
但其实大可不必如此。复杂性是可靠性与安全性的共同敌人。不必要的复杂方案不仅在采购和实施上,而且在维护上都会增加成本,同时通常更慢、更脆弱,并且拥有更大的攻击面,使其更难以理解和保护。简单的,或者更确切地说,优雅的方案才是最佳途径。这并不意味着所有的设计都会是简单的,绝非如此。复杂的设计往往是必需的。IT 绝不是一个缺乏复杂性的领域。事实上,人们常常认为软件开发或许是所有人类活动中最复杂的一项,至少在那些具有一定规模的活动中是如此。一个典型的 IT 部署包含数百万行代码、成百上千种协议、大量相互连接的系统、层层独特的软件配置,以及任何团队都不可能全部掌握的海量设置,而且只有在这之后,我们才加入了成百上千乃至数十万个不可预测、不理性的人类试图使用这些系统所带来的复杂性,而每个人的使用方式还各不相同。毫无疑问,IT 是复杂的。
重要的是要认识到 IT 是复杂的,这一点无法完全避免,但要着力于将方案设计和工程化得尽可能简单、尽可能从容…尽可能优雅。至少在我看来,这一设计理念来自软件工程领域,在那里复杂的代码被视为一种错误,而简单、优美、易于阅读、易于理解的代码才被认为是成功的。能授予一名软件工程师的最高赞誉之一,就是她的代码被评价为优雅。这句名言出自布莱士·帕斯卡(Blaise Pascal)之手是何等贴切——1970 和 1980 年代最流行的编程语言之一正是以他的名字命名的。这句名言(从法语拙劣地翻译过来)是这样说的:“很抱歉我不得不给你写了这么长的一封信,因为我没有时间把它写短。”
设计复杂、繁琐的方案往往比确定什么样的简单途径就足够了要容易得多。无论是因为我们赶时间,还是不知从何处着手调查,优雅始终是一项挑战。行业的趋势是在推动那条更艰难的道路。卖出更多设备符合供应商的利益,这不仅是为了促成最初的交易,他们还知道,设备越多,带来的支持费用也越多;如果售出足够多的全新复杂设备,支持需求就不再呈线性增长,而是开始呈几何级数增长,因为所需的额外支持不仅针对设备或软件本身,还针对系统交互的配置与支持,或额外的定制化。复杂性背后的财务驱动力是巨大的,而且并不止于供应商。IT 专业人士通过管理大量难以无缝移交给另一位 IT 专业人士的硬件和软件,获得了大量的工作保障,或者说是这种保障的错觉。
复杂性常常被如此理所当然地假定、如此被预期,以至于选择方案的过程一开始就把高度的复杂性当作了既定结论,而完全不去考虑一个不那么复杂的方案是否可能就已足够,甚至在复杂性与成本本身这一问题之外它还可能更为优越。复杂性有时甚至完全与某些概念绑定在一起,以至于当我提出一个简单的方案在价格、性能和可靠性上可能胜过复杂方案这一观点时,我确实曾遭遇过怀疑的目光。
夸夸其谈很容易,但什么才是真实世界的例子呢?我如今所见的最佳例子大多与虚拟化相关,无论是涉及存储、云管理层、软件,还是仅仅是虚拟化本身。我经常看到,对某些人而言,仅仅是一段关于虚拟化的对话就会立刻引申出对以下事物的联想:需要联网的共享块存储、昂贵的虚拟化管理软件、众多冗余的虚拟化节点以及复杂的高可用软件–而这些没有一项是虚拟化所固有的,其中大多数也很少是为了支撑实施它们的企业,甚至很少真正符合那些企业的利益。这些概念并非源自业务需求,而主要源自技术上的先入之见。指向复杂性并显得在解决问题是很容易的–复杂性会营造出一种安心感。把许多论点过滤到最后,你听到的会是“它怎么可能不行呢,它这么复杂?”复杂性提供了一种完整、问题已被解决的错觉,但这往往会掩盖一个事实:某个方案实际上可能并不完整,甚至无法正常工作,而其复杂程度让这一点难以被看清。于是,当一个更简单的途径反而更完整并解决了问题、而复杂的途径却没有时,我们的头脑不会轻易接受,因为这感觉如此违反直觉。
一个很好的例子是,我们诉诸于讨论冗余而非可靠性。可靠性难以衡量,冗余则易于量化。一块砖头是高度可靠的,即便它只是单独一块。一块砖头并不需要冗余就能稳固而坚实。它的设计是简单的。你可以用许多根冗余的木棍搭建一个支撑结构,但其可靠性远不及单独一块砖头。如果你以可靠性来谈论–即结构不会失效的概率–那么显然砖头是比若干根木棍更优的选择。但如果你说“可是没有冗余啊,砖头可能会坏掉,而且没有任何东西能取代它的位置”,你听起来就很可笑。然而当谈到计算机和计算机系统时,我们所面对的系统是如此复杂,以至于人们很少能看清自己手里拿的是砖头还是木棍,于是,既然他们无法判断真正重要的可靠性,就转而关注易于量化的冗余,而冗余其实并不重要。整个系统过于复杂,但寻求简单的方案——那个直接切中待解决问题之要害的方案——能够降低复杂性,并最终为我们提供一个好得多的答案。
这一点甚至在 RAID 中也能看到。镜像 RAID 是简单的,无非是一块磁盘或一组磁盘是另一组的精确副本。它如此简单。奇偶校验 RAID 则是复杂的,需要对跨越多个设备的可变条带进行计算,写入时必须编码,而一旦某个设备发生故障还需解码。镜像 RAID 没有这种复杂性,它通过简单、优雅、高度可靠且广为人理解的复制操作解决了磁盘可靠性的问题。奇偶校验 RAID 不必要地复杂,从而变得脆弱。然而在这样做的同时,通过削弱它自身解决其设计初衷所要解决的问题的能力,它却又同时仅仅因为自身的复杂性、而非任何其他因素,显得似乎更可靠了。人的头脑会立即跳到“它复杂,所以更先进,所以更可靠”,但这两步推进没有一步是合乎逻辑的。复杂并不意味着更先进,而先进也并不意味着可靠,但人的头脑本身就是复杂的,极易被误导。
寻找简单性并没有简单的答案。知道复杂性本质上是不好的、但有时又无法避免,这教会我们要保持警觉,然而它并没有教会我们何时该怀疑过度的复杂性。我们必须时刻警惕,始终设法判断是否存在一个更优雅的答案,而不要仅仅因为方案复杂就把复杂性当作正确答案来接受。我们需要质疑所提出的方案,也需要质疑我们自己。“这个方案真的已经达到它应有的简单程度了吗?”“这种复杂性有必要吗?”“这真的需要我之前所假定的那种复杂性吗?”
在我所给出的大多数系统设计建议中,在询问需要解决的业务需求这一步之后,我通常采取的第一个技术性判断步骤,就是质疑复杂性。如果复杂性无法被辩护,那么它很可能就是不必要的,并且正在主动地破坏当初选择它所要达成的目的。
“真的有必要把那些驱动器拆分成许多独立的阵列吗?如果有,这样做的技术依据是什么?”
“对于你所提议的任务,共享存储真的有必要吗?”
“业务真的足以证明使用分布式高可用技术是合理的吗?”
“为什么我们要用一个明天会复杂得多的系统,去取代一个昨天还足够好用的简单系统?到底发生了什么变化,使得一项既能保持简单、又绰绰有余的重大改进不再够用,反而需要数量级更高的复杂性和此前并不合理的更多开支?”
这些只是常见的例子,复杂性存在于我们这个行业的方方面面。去寻找简单。去追求优雅。不要在没有严格审查的情况下接受复杂性。让它经受那句俗话所说的反复拧绞。不要让复杂性在不该出现的地方悄然渗入。不要在拿不准时偏向复杂性–有疑问时,要简单地失败。把一个方案过度简化通常只会导致较小的失败,而把它弄得过度复杂则可能带来严重得多的失败。更稳妥的赌注在于更简单的方案。而且,如果选择了一个简单的方案却被证明不够用,那么增加复杂性要比去除复杂性容易得多。
