控制与复杂性:系统设计中的张力

综合两种广泛的方法,一种基于旨在保持对系统控制的分析性分解,另一种基于抵制分析的复杂系统视角,以及对系统设计的影响。

中文
复制

LLM 进入软件开发之后,无数组织迅速改变了自己的实践方式和组织结构。围绕写新代码的经济账被重新算过,旧方法随之被质疑、被替换、被改作他用。人和 LLM 并不能互换,所以两者所处的动态也截然不同。系统终归是系统,因此无论具体在变的是什么,都有已知的模式可供借鉴,用来给出一些指引和警告。

如果不先退一步,看看你所处的系统在设计时依据的是哪种思维,那你得到的措施和政策很可能是有些前后不一的(是“相互打架”“彼此冲突”的那种不一致,不是“毫无道理”的那种)。所以在这篇文章里,我想通过对比两套思路来讨论我们如何组织系统。 如果不先退一步,看看你所处的系统在设计时依据的是哪种思维,那你得到的措施和政策很可能是有些前后不一的(是“相互打架”“彼此冲突”的那种不一致,不是“毫无道理”的那种)。所以在这篇文章里,我想通过对比两套思路来讨论我们如何组织系统。

第一套是分析式分解,目标是让系统始终处于可控状态;另一套则基于复杂系统的视角,认为系统无法被分析,关注点落在弄清各种交互和机制,以促成理想的涌现行为。

把这两者放在一起比较,一直有助于厘清系统设计中的各种假设和关键要素,在眼下有人提出新型变革的时候,这种比较依然有意义。

两套思路

分析式分解与控制

经典科学、工程学以及许多管理形式的核心理念是:整体可以从它的各个部分来理解。把一件复杂的事物分解得足够细,细到你能透彻、详尽地理解每一个组件,你就应当能知道整体如何运作。这套方法——分析式分解(有时也被称为“笛卡尔-牛顿式”)——在现代生活的无数领域中都值得信赖、稳定可靠。

这种拆分、分析、理解的能力通常也延伸到对随时间变化的因果关系的理解:每个作用都有一个反作用,每个事件都有一个实质性的原因,而这些都可以被追溯、评估或客观检验。由此可以反过来推:如果我们对某个对象了解得足够充分,就能预测它在外力作用下会如何表现。

这是以任何可预测性和可靠性来构建机器与流程的基础。你可以有一个高层目标,以及许多互不相干的零件,把问题拆解,组装经过充分测试、在容差范围内的组件,最终得到一个可用的方案。一个推论是:如果机器中每个零件都各司其职,机器本身就应该运转良好。

这需要驯服一个混乱、无序的世界,并控制各项参数,使变异性能够被限定在边界之内。设计时留出足够的容差和冗余,事情就应该能成。如果不成,我们可以深入进去,把它拆开,弄清哪里坏了,修好它,并因此变得更好。

这种方法无处不在,从信号处理与电信——在那里,有损信息传输通过冗余被检测并纠正——一直到工业质量控制,在那里统计过程可被用来界定生产的可接受边界

它同样存在于人的层面:在人因工程中,诸如工作记忆(典型操作员能在脑中同时保持多少事项)或理想观察者(一个以最优频率监视仪器的理论化的人,我们据此定义“自满”)这样的概念被构造出来,目的就是确保有人参与的系统能让人始终在理想的参数范围内行动。

它在组织层面也清晰可见。官僚流程与层级结构旨在自上而下地维持一致性,使整个集合协调运转。纪律与可读性的机制在发挥作用,以将组织的演化置于控制之下。在更大的尺度上,组织常常试图控制其所处的环境、市场或立法背景。

基本上,通过决定一个流程内部可以接受多大程度的混乱,我们就能在它的外部为他人定义一个更清晰的接口来与之交互。这种抽象创造了一种简化但有效的方式,把复杂的集合归拢为一个可管理的单元。

软件最终成了这种思维方式的一种理想:系统可以用某些语言来写,而这些语言能保证一定程度的、来之不易的确定性。理想情况下,执行结果永远相同,没有磨损,昨天能跑的东西明天照样能跑,在任何地方都一样。在远离一线的地方做出的策略决策,可以在各个层级上被确定性地强制执行。

这意味着系统可以自下而上地由组件搭建,同时与自上而下的意图对齐,从而限制来自加工过程或人类行为的变异性。其理想是一个高度可预测、受控、有竞争力且能及时反应的系统。

复杂性与涌现

问题在于,复杂系统按定义就抗拒分析式拆解。

关于复杂系统有许多相互竞争的描述,有些是行为层面的,有些是结构层面的。它们归根结底都是类似这样一句话:“事物之间的相互关联太多、状态太多,以至于它们变得要么无法表示,要么无法预测,要么无法控制。”

其他关键要素是:这些系统是动态的,深受自身历史的影响,而且是开放的——它们不断变化、不断互动,方式并不尊重清晰的边界。这就造成了一种张力:众多参与者有着各自不同的目标、视角、表征和自由度。等你把系统分析完,它已经变成别的东西了。甚至观察系统本身也会以重要的方式改变它。

换种说法:如果你发现自己对系统的行为感到意外,那么等你弄清楚发生了什么,它已经是一个不同的系统了,而你的策略调整要么滞后,要么又在制造更多反直觉的意外。复杂系统更多是被影响,而不是被控制。

这种动态性催生出鼓励同样动态调整的策略。既然你无法让这些调整变得可预测,干预往往就是小步的、迭代的。反过来说,如果你无法简化想要控制的元素或交互,你可以增加控制行为的多样性,以便持续做出更好的调整。这通常意味着“放一个控制器——人或别的什么——让它拥有足够的内部复杂性,以抵消它所控制之物的复杂性”。用控制论的话说,这是在尝试造出更具适应性和动态性的控制机制。

平衡不是靠让事物保持静止来实现的,而是靠让它们保持运动。

理想的系统具有自我意识和灵活性,能够不断适应并维持自身运转,哪怕面对的挑战越来越大。这个理想能否达成,尚无定论。

它们如何组合(或组合失败)

系统通常从对问题及其潜在解法的受限定义中演化而来,这种定义是可处理且有效的。随着运营的范围和规模越来越全面,试图引导系统的进一步干预带来的回报递减,并且越来越多地产生意料之外的后果。当事情变得纠缠不清时,这些就是复杂系统显现出来的效应。

我见过最多的应对机制是加倍投入:做更多分析、更多分解,把更多精力放在覆盖更多情况的更灵活的自动化上。这反过来改变了成功与失败的性质,制造出有时不那么频繁但更大的事故。这种组合通过尽可能替换掉会坏的部分来实现,有时则纯属偶然。它很少是一个有序的过程。

更少见的机制则试图弄清,以分析和控制为中心的方法我们能舍弃多少,找出哪些根本无法改变,然后从那里向外扩展具有复杂性意识的机制。这远没有那么舒服,因为这种立场要求你放弃自己真的掌控着一切的想法——对一家企业来说,公开承认这一点是极为难堪的处境。

事实上,关于更大规模的事故是否真的可以避免,一直存在争论。例如,Jean-Christophe Le Coze 提供了以下分类

图示展示了关于事故不可预测性的三种理论解释:技术失控(Ellul/Perrow)、易犯错的人类建构(Kuhn、Turner、Weick、Vaughan),以及自组织涌现系统(Ashby、Rasmussen、Snook、Hollnagel)。

  1. “确定性”脉络:技术系统自身的特性(如紧耦合与复杂性)终将挫败一切防止事故的努力。
  2. “认知”分支:关注组织会遭遇“预见失灵”——事故正在酝酿的微弱信号与征兆,不会被权力结构看到或接受,世界观也无法匹配新的挑战,于是事故发生。
  3. “自组织”脉络:把系统视为适应性的,因而将成功与失败都看作系统自组织的结果,考察问题空间与解决方案空间在既有资源下的探索过程。

这些观点并非完全不相容,属于某一类别的作者常常借用其他类别的观点。 但每种视角都带有自己的焦点,即被认为重要、值得纳入考量的东西:控制的结构、系统的历史性、权力结构的动态、系统适应与变化的本质、参与者有限的视角、围绕文化的种种概念,等等。

参与这些争论的许多人,一边说事故无法预测、难以避免,一边仍在寻找能帮助降低事故发生概率的解释。他们审视已知方法的局限,拓展我们应当考虑的范围,加入能揭示新洞见的新视角。

各学科已有大量文献可供研读,帮助我们更好地把握哪些做法行不通(以及在什么情况下行不通),哪些在特定情境下有用。我在这里提出的对立——用于控制的解析式分解,与用于涌现的复杂性——是粗糙的,缺乏细微层次,但但愿正因如此,它才成为一个可用于思考系统变革的工具。

我们做的就是过度简化,而清楚自己要犯的是哪一种错,是有用的。正如 George Box(1976)所说:“既然所有模型都是错的,[我们]就必须警惕它错在要紧之处。”

实践中的两种思路对比

下面用一组略显夸张的例子,展示看待软件相关议题时两种相对典型的视角:分析式拆解(关注控制)与复杂性(刻意把关注点限制在影响上):

议题分析式拆解 / 控制复杂性 / 涌现
培训与教育制定定义清晰的课程、教师与培训师的最佳实践,以及考核机制,以确保表现可预测、学员水平整齐划一。营造鼓励探索、试验与信息交换的环境;提供指导与支持。
安全防止导致失败的不良行为。危害应被遏制或在设计阶段消除,偏离流程或最佳实践即视为风险。培养通向成功的良好行为。找出人们如何弥合流程中的缺口、绕开障碍、从问题中恢复。
正确性软件的行为符合规格说明或 API 的约定。测试通过、功能完整,并在已知边界内运行。用户或客户能够顺利完成任务;目标可以随其需求变化。
可靠性正常运行时间处于可接受范围内,并可通过 SLA、SLO 等验证。压力测试与充分验证可以避免故障。客户不满意,几个 9 都没有意义。软件是否真的能用,也只有上了生产环境才知道。要为恢复和应对意外做好准备。
应对事故的方式用 runbook 定义最佳实践。制定协议与流程,以尽可能高效地调查和分诊问题。为信息清晰和快速诊断而构建。追查故障原因,防止再次发生。意外情况可能需要临场发挥。谁知道会发生什么;要建立应对未知的能力。调查必须关注正常工作,先弄清系统本来是如何运转的。
开发功能理解用户需求以及现有方案的优势与不足,据此判断该做什么、怎么做。把候选功能放到真实场景中试验并不断迭代,才是找出哪些功能可能真正有用的最佳方式。
标准与规范基于可验证的流程与结果,写得毫不含糊,使执行可操作、可扩展、清晰明确。以目标为导向来写,以支持和引导实际执行工作、需要根据现实调整规则的人。

对每个类别而言,所采取的态度会驱使人们选择截然不同的方法和活动,其中一些可能重叠,也可能永远不会——控制成本和错误的驱动力会妨碍实验的有效性或意愿,而对复杂系统如何运作的信念,则可能与通常用来证明问责的各种措施相抵触。

我说这张表是漫画式的,因为在现实世界里,界线往往没有这么清晰,也没有这么表面。比如,一个以控制为中心的层级结构,完全可以让管理者在目标上保持一致,并把权力下放以应对系统复杂性;控制也可以根据掌权者信任谁来强调。集中式控制往往在分析性分解这一侧最有效,但也有一些并非以控制为中心的方法能从中受益。

事实上,许多活动在两种方法中都能用,可以服务于不同的人,甚至同时服务于同一个人:

活动分析性分解 / 控制复杂性 / 涌现
Code Review找出 bug 和缺陷;跟踪并分配责任;确保质量。建立认知,并在团队内部和团队之间提供反馈的空间。
SLO 采用一种组织工具,确保所有团队充分管理其可靠性。一种优先级排序工具,其价值来自让团队讨论并定义什么是可接受的可靠性水平。
重构偿还技术债,降低复杂性,提高可维护性和灵活性,统一所用模式。对抗熵增,根据可获得的新信息或需求变化,让代码库适应不断变化的语境。
混沌工程验证预期的故障情形能被妥善容忍或恢复。以实验为驱动的练习,参与者在其中对系统在故障场景下的行为形成假设,并尝试证实或证伪。
使用平台共享平台可以鼓励良好的架构模式、阻止不良模式,同时为在其上构建的团队抽象掉复杂性。平台为系统提供手段,把共享要素商品化,从而获得规模经济与专业化带来的好处,并通过自助访问解决组织瓶颈。

即使这份清单中的活动 可以 同时服务于分析性拆解和与复杂性对齐的方法,也不意味着它们 一定 会如此。

例如,以控制为中心、旨在消除一切偏离既定规范之处的代码审查方式,可能带有对抗性,甚至引发焦虑或妨碍真正的反馈。有些实践仍可能把自动化与恰当的社会规范结合起来,在不同程度上同时支撑这两种目的。

以我的经验,对于这些活动,如果你想理解它们实际如何展开、又如何有时未能满足某些人的期望,背后所持的立场才是真正重要的。在决定某项活动相对于其他活动的优先级时,这一立场同样重要。如果参与者或利益相关者不认同更高层次的目的和期望结果,那么这些活动被期望如何执行、实际又如何发生,以及它们在整个系统中被赋予的相对重要性,都会出现落差。

当有人想要改变、补充或移除其中某些活动时,值得先问一问:这个改变的本质是什么,它偏向的是哪一种视角。

在不同方法之间翻转

作为一种启发式:当有多种视角可用时,我们可以试着找出最好的那一个(依据某些任意设定的标准),也可以采用一种互补或交叉的方法,尽可能多地用上它们。只挑一个视角,可能导致你去寻找能在特定情境下最大化某一类活动的实现方式——无论是控制还是涌现;而组合的方法则试图确保所选活动能够同时服务于多种属性,作为一种权衡。

有时,你得到的并不是你想要的。一个为控制而设立各种活动的组织,可能发现自己实际上依赖实践者在暗中把它们改作与复杂性对齐的贡献。与此同时,组织的决策者所行使的控制比他们以为的要少,或者把收益错误地归因于自己的举措。于是,当他们调整控制机制、并顺带妨碍了那些隐藏的适应行为时,他们可能失去原本拥有的东西。

反过来,如果活动是为涌现而设,却像为控制而设那样机械地执行,它们就不会带来预期收益,看上去、感觉上都像是无事忙:组织既没有控制,也没有从自适应效应中获益。

对于可靠性或正确性这类宽泛的主题和类别,通常没有明确写下、可供你使用的选择或原则。不过,组织往往有一些大致落在控制—涌现光谱上的通用工具,通常围绕流程设计和执行机制。

如果你面对不喜欢的行为,比如其他团队的人不打招呼就改动你团队负责的敏感代码,你可以采取一些措施,比如与他们沟通、重申归属,并说明预期流程。你可以要求在提交任何变更请求之前先有一份 RFC 文档或工单。你可以依靠代码归属文件,阻止任何未经你同意的意外改动继续推进。你可以把关键代码迁到其他团队无法访问的仓库。

这些都是相对局部的做法,借助直接周边的结构来改变行为、阻止不想要的动作。这些方法可能以很小的代价取得极大效果,但也可能无意中让期望的行为变得更不可能发生。

更接近涌现视角的做法,可能是先弄清是什么驱使其他团队不打招呼就发来这些改动。他们看到了哪些约束和压力,使得当前行为对他们而言是合理的?如果所有人都认同这个流程是理想的良好状态,但它却经常被无视,那么在他们看来什么比它更重要?只有理解了这一点,你才应该去设计干预措施。这类追问——往往借助 Le Coze 此前指出的那些模式——常常会让你拉起一根线头,一路拆解整个组织。没有既有的信任,这可能既耗时又困难,但它可以重塑预期,并且既可能带来重大变革,也同样可能只在上游做小幅干预。

一种组合式做法是:依靠能识别复杂性的方法先获得对局面的广泛理解,再据此设计简单但杠杆率高的检查与屏障,让最少的控制带来高回报。这要求站在复杂性的立场上,不只看系统的结构和目的,还要看它的各个组件与参与者如何互动。一旦这些互动变得更容易理解,分析方法才有望更有效。

这里的风险在于,你可能会面对这样一个系统:它要么显得过于棘手、抗拒复杂性方法,要么对跨领域的干预过于僵硬,结果你又退回到纯粹的局部防御——只是这些防御来得更晚,要达到同样的效果还得付出更多工作。

那么问题就不是哪种做法更好,而是我们如何知道当前的做法已经触及极限,以及到那时该怎么办?

不加审视的系统设计的陷阱

人们一直在改动自己的系统,懂不懂这些知识都一样。他们往往能成功,但并非总是如此,至少不是按计划中的方式成功。知道该看什么,并不意味着你就能做对,但它能提高胜算。

在当下这轮由 LLM 驱动的变动中,这一点或许同样成立。因为技术是新的,设计模式尚未定型,很多人的尝试都带点随意。他们的想法中有不少元素或侧面颇有意思,值得借鉴,但从系统的角度看存在明显的遗漏,而这些遗漏终究要处理。

读技术评论文章时,几乎不可能不碰到主张大范围改动的例子——这里我就不放链接了。这些主张包括:

  • 用各类屏障(测试和自动化检查)取代代码评审,却很少追问这种做法可能衍生出什么角色,也很少追问静态屏障与更具适应性的屏障在性质上可能有何不同。
  • 把软件工作拆成高层规格说明,再在黑箱中翻译成代码,只做外部检查,却不解释规格说明如何覆盖不同的抽象层次、外部检查如何保持可控,以及值得学习的信息应如何在两个方向上跨越这些边界。
  • 要求每个人都变成某种“agent 的管理者”,同时把 agent 置于紧密的控制回路中,却不追问:整体改变委派与控制机制,会在这个类比中失去什么(或至少带来哪些二阶效应)。
  • 只关注系统层面可观测的结果,放弃对内部结构的强制规定,相信系统会自行充分自组织。

如果你在设计系统时以控制为核心——设置屏障(想想瑞士奶酪模型)、进行大量测试、用流程和规程保证最佳实践——那么你同样需要高度重视另一类机制:用来判断控制是否真的有效。这意味着要问这样的问题:

  • 我们怎么知道自己的观察仍然有效,并且捕捉到了正确的信号?
  • 我们怎么知道自己对系统的理解何时开始失准?
  • 为了让事情变得清晰可读,我们的分析遗漏或遮蔽了哪些重要元素?
  • 多大的波动是被容忍的,我们是否正在压制某些必要的波动?
  • 我们优化的那些东西,是否正在别处制造脆弱性?
  • 我们的控制是真实的还是虚幻的?如果它发生变化,我们怎么知道?

调节良好的系统会以某些方式补偿干扰,而这些方式恰恰会掩盖或压制问题积累的信号——技术上如此,文化上也是如此。上面这些问题,目的就是弄清楚:有没有人认真想过,究竟是什么在掩盖这些行为。

当你为涌现而设计——想想自组织、类市场机制,或者把决策权交给掌握本地语境的参与者——另一些问题就会出现:

  • 系统各局部是否在互相掣肘?
  • 目标对齐是否有效?靠什么维持一致性?
  • 放弃可读性,我们牺牲了哪些能力或效率?
  • 我们承受得起失去控制中心式系统的效率吗?什么时候会需要它?
  • 我们如何区分适应与漂移?
  • 什么在保护异议,什么在把信息从系统边缘传递出来?

由于复杂系统视角的方法往往抗拒规定性立场,常常伴随惯性增大或大范围失准的风险。涌现属性将决定成败,但若缺乏审慎思考和主动施加影响,事情可能自行其是,脱离掌控。

每当有人力推以分析性拆解或控制为核心的系统设计,就问他们:凭什么知道自己做的是对的事,以及他们靠什么机制来适应。每当有人力推看似承诺自我调节和无限灵活性的设计,就问他们:如何维持一致性,以及他们依赖哪些条件来获得好结果。每当有人力推从一种切换到另一种,就问:当前行为牵涉到哪些依赖,并考虑由此产生的二阶影响可能是什么。

科技公司常常急于围绕新技术那些被夸大的承诺来重塑自身。把新技术整合进现有工作流,通常要求先改造这些工作流。这类改造往往以减少波动、加强控制为目标,却以跨越子系统边界的方式,打乱了原本处于动态稳定状态的错综交互。

让事情变得可预测的自动化,必然会移除一部分对适应与演化有用的不可预测性。同样,试图让系统的某一部分更具适应性,也可能必然让它变得更不可预测。两者都会对系统其余部分产生连锁影响。

系统在何处、以何种方式从一种运行模式迁移到另一种?哪里需要控制,哪里不需要?我们选择分析并拆解什么,又把什么当作生态系统来对待?

如果这些问题没有答案,我们也就无法很好地回答:我们的系统将如何避免失败、如何取得成功。系统就是系统。它们会继续像系统那样运转,也像系统那样失败。

来源: ferd.ca← 返回首页