软件开发中的 11 个系统思维定律

英文原文:11 Laws of The System Thinking in Software Development

“我会更加努力地工作。”
—— 一匹名叫博克瑟(Boxer)的马(出自乔治·奥威尔的《动物农庄》)

彼得·圣吉(Peter Senge)在其著作《第五项修炼》中提出的系统思维定律,同样适用于软件开发领域。以下是这 11 条定律及其在软件开发中的具体体现。

1. 今日的问题源于昨日的解决方案(Today's problems come from yesterday's solutions)

当我们解决问题时,往往会感到高兴,却经常不考虑后果。令人意外的是,我们提出的解决方案可能会产生反作用,从而带来新的问题。

  • 奖励机制的副作用:作为对取得巨大成功的团队的奖励,公司决定为团队中的少数骨干成员发放奖金并晋升职位。这导致团队其他成员感到不公平且丧失积极性,最终使团队成员关系紧张,后续项目难以取得成功。
  • 紧急需求的干扰:项目经理频繁要求开发者修复新 Bug 或处理客户的紧急需求,开发者虽尽力满足,但过于频繁地分散精力妨碍了他们完成迭代过程中的主要任务,导致项目进展缓慢。

2. 用力越大,系统的反作用力也越大(The harder you push, the harder the system pushes back)

当事情进展不如愿时,我们常固执地坚持原有方法,没有停下来思考更好的替代方案,而是“义无反顾”地向前冲。有时候虽然解决了眼前问题,却往往深陷于其他问题之中。

  • 加班的恶性循环:当系统远未完成时,经理通常不断催促员工加班加点并要求按时完成。这导致系统 Bug 数量持续增加及整体质量急剧下降,进而造成更多延误,最终需要做更多的工作来部署系统。
  • 架构的盲目扩展:为了满足新系统要求,开发者贸然对原有系统架构进行扩展,但死板陈旧的方法已不能满足新需求。他们忙于此事而没时间停下来仔细分析并改变方法,导致系统质量下降。

3. 福兮祸之所伏(Behavior grows better before it grows worse)

短期的解决方案会带来短暂的休息和状况的暂时改善,但不会从根本上解决问题。这些问题终究会使情况变得更糟。

  • 营销与质量的脱节:公司为顾客提供丰厚优惠并投入巨资宣传,吸引很多人购买软件。但顾客购买后很不满意,因为软件无法使用且不可靠。
  • 不可能完成的承诺:管理层承诺,如果开发团队能够按时完成系统开发,公司将提供巨额奖金。团队开始努力工作,但很快意识到这是不可能实现的。于是开发者变得悲观并丧失动力。

4. 最容易出去的方法往往会导致返回来(The easy way out usually leads back in)

我们在生活中学到的一些解决方案能够帮助我们轻易地获得成功。我们总是试图把它们强加到任何情形上,而忽略了特殊的背景以及相关人员。

  • 敏捷实践的强行落地:开发者还没有准备好接受结对编程或者测试驱动开发这样的实践时,敏捷教练强行实现完全的极限编程(XP)。这会给任何敏捷方法带来压力、冲突以及负面影响。
  • 设计模式的滥用:开发者把设计模式应用到任何地方,这是徒劳的,而且这会让系统变得复杂。

5. 治疗带来的结果可能会比疾病导致后果更严重(The cure can be worse than the disease)

有些熟知的方法可能会更危险。例如,在编程的时候喝啤酒,来减轻不切实际的任务期限带来的压力。

  • 外包核心功能:由于不信任全职开发者,一家公司雇佣了大量的承包商来开发核心功能。结果系统不具有概念完整性,自己公司的开发者看不懂且无法修改,导致公司员工也不了解相关领域的知识、解释以及概念。
  • 复制粘贴式开发:开发者会走捷径,拷贝相似功能的代码来赶进度,并争取尽快发行第一个版本。他们一开始进展迅速,但是代码最终会变成「大泥球」(Big Ball of Mud,指系统结构混乱不清)。

6. 欲速则不达(Faster is slower)

当我们看到成功的曙光,我们会全力以赴,不再小心谨慎。然而,最优增长速率通常会比可能的最快增长速率要慢得多。

  • 人手增加的负担:经理们往往为已经成功的项目增加很多人手,但总体进展就会变慢,因为交流成本增加,以及团队成员之间失去默契。
  • 缺乏重构的隐患:在没有对代码进行合理重构及改善的情况下,开发者快速地为系统添加新的功能,会使系统变得难懂,而且难以修改。

7. 在时间和空间上,因果并不密切相关(Cause and effect are not closely related in time and space)

我们善于为出现的困难寻找原因,即使这些原因很牵强,并且远非是真正的根本原因。

  • 拒绝需求变更的代价:为了按时完成系统,开发团队不再接受来自客户的需求改变。因此,客户对发行的软件不满意。
  • 过度文档化的束缚:实时系统历经坎坷之后,管理层迫使开发者同意,在给系统做出任何修改之前撰写详细的技术说明。结果开发者失去了为系统做出任何改进的动力,并且开始拖延。

8. 微小的改变可以产生明显的效果,但这种杠杆效应最大的地方往往也最不明显(Small changes can produce big results-but the areas of highest leverage are often the least obvious)

像改变公司政策、愿景或者广告用语这样显而易见并且关系重大的解决方案往往不起作用。相反,小而普通,但持续的改变却会带来大不相同的效果。

  • 持续的客户交流:开发者每天都与客户进行交流,并且做出大部分决定。因此,能够更好地理解客户的需求、做出更好的决定并且给出最优的解决方案。
  • 自动化单元测试:开发者为系统的每项功能设计自动化单元测试。因此,设计更灵活、人们更自信、系统在每次修改之后都能得到完全的测试。

9. 鱼与熊掌可以兼得,但不是同时兼得(You can have your cake and eat it too – but not at once)

我们经常会面对刻板的“非此即彼”选择。如果我们改变一下自己的观点及系统规则,这些选择有时并不会使我们进退两难。

  • 范围与成本的平衡:经验丰富的项目经理知道增加系统特性的数量与削减时间和开支不可兼得。然而,如果我们完善想法、寻找合适的人才并且避免过度开发,这也是可能做到的。
  • 架构模式的融合:开发者认为他们应该要么采用事务脚本,要么采用域模型体系架构模式。然而,复合域中的高性能解决方案可以将两者结合,以得到最佳性能。

10. 把一头大象分两半不会得到两头大象(Dividing an elephant in half does not produce two small elephants)

无法整体了解系统,往往会做出次优决定。

  • 错误的评估指标:项目经理往往通过生成的代码量和迭代过程中实现的功能数来评估开发者。而开发者往往会生成大量无用代码。
  • 破坏性的 Bug 奖金:管理层承诺,每发现一处系统 Bug,测试者将得到 5 美元。测试者对跟开发者合作不再感兴趣,并且不再试图消除产生 Bug 的根本因素。团队之间良好而且高效的关系不复存在。

11. 无可非议(There is no blame)

我们喜欢归咎于客观条件,或对别人指指点点,甚至对此深信不疑。但是,我们自己以及问题的原因都是系统的一部分。

  • 个人英雄主义的局限:今天早上团队没有发布系统完全是乔的过错。即使项目经理亲切地为其提供了免费的啤酒、T 恤以及披萨,他也没能在一晚上的时间内修复所有的缺陷。
  • 用户需求的本质:人们不会使用一个公司优秀的 Web 2.0 社会化应用,用户喜欢简单实用的东西,并且不会感激你辛勤工作的成果。

然后呢?

以上 11 条系统思维定律表明,我们提出的所有解决方案都会产生一定的后果,有时非常严重并出乎意料。我们周围的系统本就那样,我们不应苛责它们,而是要从中学习。要掌握系统思维方式并控制这些系统,我们需要做到如下几点:

  1. 要明白我们是在跟什么样的系统打交道,是人或是软件;
  2. 有意识地学习相互关系、因果链;
  3. 把系统看做一个整体,并且视其为其他系统的一部分。

系统思维方面有很多挑战,通过获取并且利用有关系统工作方式的知识,我们可以战胜其中的很多挑战。但是,大部分严峻挑战是我们人类与之相冲突的本性。我们的激情、感情以及本能可以轻易改变我们理智、条理分明的思维方式。掌握系统思维方式的第一步就是要学习如何跟自己合作。

后话

在软件开发过程中,你有(或缺乏)哪些系统思维的使用经验?

编者注:原文作者 Andriy Solovey 从事软件开发已有 15 年,做过开发人员、软件经理和系统架构师。关注构建优质、可靠和可用的软件。

说明

本文原文发表于 2007 年,部分案例(如 Web 2.0 应用、特定奖金制度等)具有当时的时代背景。尽管技术环境已发生变化,但系统思维的核心定律在当今软件开发中依然具有深刻的指导意义。