权衡思维:软件设计没有银弹 — editorial cover photo

软件设计没有银弹:CAP 与自研开源的取舍

软件设计没有银弹只有取舍。用 CAP、缓存一致性、自研 vs 开源几个真实决策把权衡这套思维方式拆开。

2026年3月6日·返回文章列表
文章大纲

做营销平台的时候我们弃用了 Flowable,自己写流程引擎。复盘这个决策,最有价值的不是结论,而是得出结论的过程。整个过程没有一句「业界都这么用」,全是把约束摆出来,然后选一个能接受的代价。这就是权衡。

Fred Brooks 四十年前说过,软件工程没有银弹。这句话被引用到烂,但真正吃透它的人不多。多数人嘴上说没有银弹,手上还在找一个万能方案。这篇想讲明白,权衡到底是什么,怎么做,以及最常见的几种自欺欺人。

权衡的核心,是主动选一个代价

权衡不是和稀泥,不是「各有优缺点」。它是这样一个动作,把所有方案摆出来,看清每个方案要你付出什么,然后主动选一个你能承受的代价。

反面是三种思维。一是追求完美,什么都想要,结果什么都做不好。二是盲目跟风,别人用什么我用什么,不问适不适合自己。三是只看好处不看代价,方案上线才发现坑。这三种的共同点,都是回避「我放弃了什么」这个问题。

Fred Brooks 那句话的潜台词是,每个选择都有代价,差别只在你有没有看清它、主动接受它。看清了,方案哪怕不完美也是好方案。没看清,再流行的方案也是埋雷。

下面用几个最常见的真实权衡把这套走一遍。

CAP,分布式绕不开的第一道坎

PlantUML
正在加载图表…
PlantUML diagram

图:分区是网络世界的客观规律不可选,真正的决策只有一道——分区发生时,要一致性还是要可用性,取决于业务能否容忍不一致。

网络会分区,这是个物理事实,不是设计选择。一旦分区发生,你要在一致性和可用性之间选一个。

金融系统选 CP,宁可拒绝服务也不能让账目错。一次重复扣款就是真金白银的资损,这是业务不能接受的代价。互联网的多数场景选 AP,宁可短暂不一致也不能让服务不可用,因为不可用意味着流量损失和用户流失。

常见的误解是把 CAP 当成「三选二」,好像一致性、可用性、分区容忍三个里挑两个。其实分区不是你能选的,它是网络世界的客观规律。真正的选择只有一道,分区发生的那一刻,你要一致性,还是要可用性。

CAP 不是一句口号,是一道业务题

落到具体系统,CP 还是 AP 取决于业务能不能容忍不一致。账户余额、支付状态、库存数量,这些错一次就是资损,往 CP 靠。商品信息、推荐列表、用户画像,晚几秒更新无伤大雅,往 AP 靠。没有一个通用答案,每个场景都要单独问一遍。

缓存,性能和一致性的天平

PlantUML
正在加载图表…
PlantUML diagram

图:同样的缓存+异步刷新手段,在能容忍延迟的人群规则场景是神技,在不能容忍的账户余额场景是事故——决定对错的是业务对一致性的容忍度。

加缓存几乎是提升读性能最立竿见影的手段,但它会引入一个绕不开的代价,数据延迟。

读缓存为主,快,但有几秒甚至几分钟的不一致窗口。每次都读 DB,准,但慢。这个权衡的判据很直接,业务能容忍多大的不一致窗口。

我做过人群底座的优化,缓存粒度从接口维度细化到单个的人群 ID 维度,命中率从 40% 拉到 99%,上游策略服务从 1300 QPS 扛到 2800 QPS。但这个收益有个前提,我们接受分钟级的数据延迟,用监听 binlog 旁路异步刷新缓存来换吞吐量。

为什么敢接受延迟,因为人群规则更新频率低,运营改一个规则晚一分钟生效,业务上无所谓。但同样的方案放到账户余额上就是灾难,余额晚一分钟更新,可能就重复扣款了。

这就是权衡。同一个手段(缓存 + 异步刷新),在能容忍延迟的场景里是神技,在不能容忍的场景里是事故。决定对错的不是手段本身,是业务对一致性的容忍度。

自研还是用开源,灵活和简单的权衡

PlantUML
正在加载图表…
PlantUML diagram

图:自研或开源的判据不是「想不想灵活」,而是现成方案的设计假设和你的真实约束差多远——差得近用、差得远自研。

这是工程师天天遇到的抉择。通用框架灵活但复杂,专用代码简单但不灵活。

我们的选择是,当一个现成框架的设计假设和你的真实约束严重不匹配时,自研。营销画布要节点级可观测、单画布执行上下文 JSON 能到 20M、C 端每天 4000 万人次访问,这几个约束摆出来,Flowable 这类为 BPM 设计的引擎就不只是「不好用」,而是它的核心假设和我们对不上。强行用,要么阉割自己的需求,要么把框架改得面目全非,两边都难受。

但反过来,如果你的需求就是标准的审批流、节点数有限、流量可控,那就该直接用开源,自研纯属浪费。YAGNI 的原则在这里同样适用,别为想象中的扩展性预先付出复杂度的代价。

判断标准很实际,现成方案的假设和你的约束差多远。差得近,用。差得远,自研。判断的前提是你得诚实面对自己的约束,别把「我希望灵活」当成自研的理由,那是过度设计最常见的借口。

现在 vs 未来,验证前别过度架构

业务早期,最大的风险不是架构不够好,而是业务方向本身就是错的。这时候花几个月搭一套「可扩展架构」,很可能业务一转弯,架构全白搭。

健康的节奏是,业务验证前用最快的速度做出能跑的东西,验证它是不是真有人要。验证成功、规模上来、痛点暴露之后,再投入做架构演进。这是先有真实约束,再有针对性的设计。

反过来,业务还没验证就想清楚三年后的架构,这是把灵活性和可扩展性当成了目的本身。多数想象的未来需求永远不会来,而为它付出的复杂度却实实在在留在了代码里。

权衡要写下来

一个容易被忽略的点,权衡的结论和理由要显式记录,业界叫 ADR(Architecture Decision Record)。

隐式权衡凭直觉选了,说不清为什么,一旦前提变了(比如业务对一致性的要求变了),没人知道该不该重新决策。显式记录把「选了什么、放弃了什么、为什么」写下来,可追溯、可讨论、可在前提变化时重新评估。

这件事的价值不是当下,是三个月后有人问「当时为什么这么选」的时候,你不用靠回忆硬编。

成熟的工程师怎么看待权衡

接受不完美。没有银弹意味着没有无代价的方案,成熟的第一步是放弃寻找完美方案。

理解业务。权衡服务于业务目标,不是纯技术审美。一个技术上更优雅但和业务目标错位的方案,比一个笨拙但贴合业务的方案差得多。

主动选代价。最关键的能力,是在多个不完美的方案里,清楚地说出「我选这个,代价是 X,我接受 X」。能说出这句话,说明你想清楚了。

持续评估。前提会变,今天的权衡明天可能就错了。把权衡当成一次性的决定,是另一种形式的逃避。

软件设计这件事,做到最后比的不是谁会的方案多,是谁更会取舍。方案是学不完的,但把约束摆清楚、把代价看明白、然后做一个能负责的决定,这套能力是可以一直复用的。这也是权衡思维值得反复练的原因。

Continue Reading

关联文档推荐

查看全部
第一性原理:从本质思考技术问题 — vibrant cover for technical blog article复杂度治理:本质复杂度与偶然复杂度 — editorial cover photo技术成长路径:从写代码到做决策 — editorial cover photo
作者:archy.shawn
声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。