软件工程 20 定律

内容管家 编程开发评论26字数 4706阅读15分41秒阅读模式
软件工程 20 定律

20 条软件工程定律

软件项目为何失败、系统为何腐化、团队为何减速

大多数工程师都是在踩坑中学会这些法则的。当你试图重写某个系统却未能带来预期的改进,当一个项目已经延期、却还想靠增加人手来挽救,结局往往只会让失败来得更快。有时,整个团队会开始围绕某个指标打转,试图操纵它。半年后,才会有人提起一条 1975 年就存在的法则,精准地描述了刚刚发生的一切。

我为此付出的代价是:职业生涯的前半段都在摸着石头过河——大概很多人也有同感。

下面这二十条法则,是我引用最频繁的。虽然还有更多(本篇稍后会提到)。软件开发法则能告诉你正在发生什么、即将发生什么,以及哪些事情无论多努力都注定行不通。有些法则已经有六十年历史了,却依然适用于 2026 年的软件开发,乃至 2036 年——因为它们本质上并非关于软件,而是关于人在时间压力下协作建造东西的规律(说到底,很多法则不过是人性法则)。

这些法则不是告诉你"该怎么做"的规则,它们告诉你"正在发生什么",最终决策仍需你自己来做。这些法则只是帮助你理解现状。

每一条法则之所以入选,是因为我亲眼见证过它发生。我的书涵盖了全部五十六条法则。如果你只想记住二十条,我认为这几条最重要。

一、系统是如何建成的

1. Gall 法则

任何能运行的复杂系统,一定是从一个能运行的简单系统逐步演化而来。

系统在实际运行中的表现,往往与设计文档中的预期不符——很多问题只有在真实用户真正使用后才会暴露。到那时,系统要么能用,要么不能。每一个能运行的复杂系统,都是一步步走过来的。而那些试图从一开始就设计得完美无缺的系统,通常会失败。

这也是为什么大多数从零重写的新版本系统最终都不如预期——团队保留了原来所有的功能,却丢失了让旧系统稳定运行的那些简单设计。

案例:Instagram 的演进。 最初,Instagram 并非一个图片分享平台。它的前身是一款叫 Burbn 的应用,集成了签到、游戏、图片分享等功能。后来创始团队砍掉了所有功能,只保留图片分享,这块被剥到极致的核心最终成了产品。

反例:Google Wave。 它一上线就同时推出了聊天、邮件、论坛和文档编辑器,没人能说清它到底是做什么的。十五个月后,项目终结。

2. KISS 原则(保持简单,笨蛋)

保持简单。任何超出这个要求的,都是额外负担。

KISS 原则提醒我们,简单应该始终是核心目标。如果能用 50 行脚本解决的问题,就不要用 500 行的复杂方案——KISS 倾向于更简单的做法。因为每一行代码都有可能引发错误。

为什么简单如此重要?软件本身的构建就很复杂,还需要被人理解。简单的设计更容易维护:新成员能更快上手,Bug 更容易定位,修改引发的连锁反应也更少。

KISS 原则鼓励开发者抵制那些"自以为聪明"、一口气做太多事情的代码,也不要用牺牲当前复杂度来预先解决未来问题的架构方式。

案例:功能开关系统。 有家创业公司需要一个功能开关(feature-flag)系统,于是决定自建。他们把它做成了一个独立的微服务,带有自己的数据库、缓存、管理界面、WebSocket 通知和 A/B 测试支持。结果引入了大量复杂性,耗费大量开发时间,一旦出问题,影响面也很广。

实际上,他们需要的只是一个 JSON 配置文件——一个下午就能搞定。

3. Conway 定律

组织设计出的系统,会忠实地反映其内部的沟通结构。

团队组织方式会直接影响系统架构。如果四个团队协作开发一个项目,最终产品很可能也是四个模块。如果前端、后端、数据团队之间缺乏沟通,应用就会变成三个相互割裂的子系统。

反过来也成立:可以先确定目标架构,再据此组建团队。亚马逊在 2000 年代初就是这么做的——将系统拆分为由小团队管理的微服务,形成了一种「团队结构 → 系统架构」的倒置策略,这就是所谓的 Inverse Conway Maneuver(逆康威策略)。

4. Hyrum's Law

只要用户足够多,API 的所有可见行为都会成为某人的依赖——无论契约怎么写的。

你写的接口契约并非真正的契约,真正的契约是你的系统实际做了什么,包括那些你从未预料到会被依赖的部分:响应时序、错误信息文本、JSON 键的顺序、哈希值的精确字节……总有人在某处依赖着所有这一切。

这也是成熟系统中向后兼容成本如此之高的原因:你维护的其实不是你设计的 API,而是那个「意外形成」的 API。

案例:经典案例是《模拟城市》。游戏存在一个 use-after-free 漏洞,在 Windows 3.x 上运行正常,因为内存从未真正回收。升级到 Windows 95 后,系统会回收内存,游戏随即崩溃。微软为此在 Windows 95 中内置了专门的内存分配模式——仅在检测到《模拟城市》运行时激活,让这个 bug 继续「正常」工作。

浏览器同样在互联网规模上这么做:Web 开发者基于平台 Quirks 构建的一切,实际上都成了平台的一部分,浏览器无法改动这些特性而不破坏半壁江山。

5. CAP 定理

分布式系统只能同时保证以下两项:一致性(Consistency)、可用性(Availability)、分区容错性(Partition Tolerance)。

网络会故障。在分布式系统中,这不是设计时要考虑的问题,而是必须接受的事实。一旦发生分区,必须二选一:阻止写入以保持数据一致,或者继续服务但允许副本漂移。每款分布式数据库都在做这个选择,只是大多数没有明确告知用户。它们躲在「最终一致」或「高可用」等标签背后,让你在故障时才知道真相。

案例:MongoDB 偏好一致性——发生分区时,部分副本会拒绝写入,直到整个系统恢复。Cassandra 则选择持续响应查询,即使副本之间存在分歧,之后再修复不一致。两者都没有错,只是做出了不同的取舍。

6. Zawinski 定律

每个程序都会扩张到能读取邮件的程度。那些做不到的,会被能做到的取代。

功能蔓延不是过程中的偶发事件,而是过程的本质。当一个工具做得足够好、用户足够喜欢时,他们就会一直用它。产品负责人希望用户保持活跃、留在平台上,于是工具开始承担更多相关任务,久而久之变得缓慢笨重、充满冗余。此时新的竞争者带着更简单的产品入场,做同样的事。App 流行后,又开始堆积更多不必要功能。

案例: Netscape 最初是浏览器,最终演变成包含邮件、新闻、网页编辑器的套件。Firefox 作为精简版问世并迅速流行,但后来也走上了插件和开发者工具链的老路。Slack 最初宣称要「杀死邮件」,如今却有了语音、视频、机器人和应用商店——这类情况在产品缺乏明确北极星指标时几乎不可避免。

二、团队如何失去速度

7. Brooks 定律

向已经延期的软件项目追加人手,只会让它更晚完成。

软件工作难以在团队成员之间拆分。新人加入后需要时间上手,而有经验的工程师必须停下手中工作来带人。如果项目已经落后,追加人手不但无法加速,反而会让情况更糟。

Brooks 说得到位:九个女人也生不出一个月大的婴儿。软件工作亦然——人海战术解决不了进度问题。

案例:我曾担任一个八人团队的负责人,项目总是延期。第一反应是招两名工程师来赶进度。但在招聘期间,有两位成员离开了。奇怪的是,一切反而运转得更顺畅——沟通变简单了,产出反而更高。显然,解决方案是缩减团队规模,而非扩充。

8. Ringelmann 效应

随着团队规模扩大,个人产出递减。

当很多人一起拉绳子时,每个人出的力反而会减少。这既因为协作本身难以顺畅,也因为人们倾向于认为"总有人会去做"。这种现象比多数人想象的更加极端。

数据佐证: 一项 GitHub 大规模研究 直接测量了这一现象。2-5 人团队的开发者在一个月内平均产出约 1850 行代码,而 10 人团队的产出降至 1200 行。50 人以上的团队更是低至 450 行——人均产出暴跌 75%。

这正是小型团队比大型团队交付更快的原因,也是亚马逊"两个披萨原则"(团队规模不超过两个披萨能吃饱的人数)的底层逻辑:它是对抗雷格尔曼效应的防御策略。在如今的 AI 驱动时代,AI 正在大幅提升个人和团队生产力,高效团队的规模比以往更小,这一规律更加显著。

普莱斯定律:少数人完成半数工作

核心观点: 一半的工作实际上由平方根数量的人完成。

在 100 人的团队中,约 10 人完成了一半的核心工作;在 16 人团队中,约 4 人承担了大部分任务。这一规律在所有创意领域都适用。

然而,这并不意味着其他人可有可无。他们负责日常运维、支持性工作(常被称为"粘合工作"),确保一切正常运转。真正的问题是:如果团队中的核心人员离开,整个团队的产出能力将大幅下滑。

典型案例: 马斯克接管 Twitter 后裁减了约 50% 的员工,但网站照常运行——这正是普莱斯定律的预言。但定律没有预测到的是,裁员伤及了信任与安全、SRE 覆盖和事件响应等深度能力。核心员工保住了基本运营,却让组织失去了应对下一个棘手问题的能力。随后 Twitter 又悄悄召回了一部分被裁人员。

三、为何计划总是延期

霍夫施塔特定律:永远比你预期的更长

即使你把霍夫施塔特定律考虑进去,项目依然会比预期耗时更久。

假设你预估某任务需要 4 周,想到自己的估算通常过于乐观,于是翻倍到 8 周以留出余量。结果呢?实际用了 16 周。

下次你学乖了,心想这次按 16 周来估算总该够了吧?错——这次花了 32 周,因为总有意想不到的事情打乱计划,比如意外的集成问题或需求变更。

实践中,霍夫施塔特定律解释了为什么估算留 Padding、历史数据分析、帕金森定律意识等技巧至关重要,但意外依然会不期而至。

经典案例: 柏林勃兰登堡机场项目堪称范本。软件集成涉及 75000 个传感器和 50000 个照明设备,比预期耗时得多。最初计划 18 个月完成,后延长至 30 个月,最终却花了 7 年,耗资 70 亿欧元——是预算的 2.5 倍,延期 9 年才启用。

邓宁-克鲁格效应:能力与自信的错位

核心观点: 你对某领域了解得越少,往往越自信。

这里有个令人不安的事实:做好一件事所需的技能,与评价自己做得有多好的技能是同一项。能力不足的人看不到自己的问题所在,所以高估自己;真正擅长的人却能意识到还有哪些不足,反而低估自己。

常见表现: 当被问及何时能完成时,新手开发者常常给出一口价的精确时间,而资深开发者往往给出一个范围——那句著名的"视情况而定"。新手并非盲目自信,而是尚未意识到自己"不知道什么不知道"(未知未知)。

人们对新技术的热情往往在初期最为高涨,因为那时还没被现实教育过。当前 AI 领域正在重演这一幕:宣称 AI 什么都能做的人,往往是那些并不每天使用它的人,比如管理者。

帕金森定律:时间决定工作量

核心观点: 工作会自动膨胀以填满可用时间。

如果给开发者两周时间完成一项本可以两天搞定的事,结果就是花两周。这并非因为开发者懒惰或拖延——而是人们本能地会填满手头的时间。两周里,开发者可能会制定计划、尝试各种方案、添加不必要的额外任务(过度工程)。但如果截止日期是明天,那多半明天就能完成。

帕金森定律的启示是:给多少时间,就会用掉多少时间。团队应设定清晰、现实的时间限制(截止日期驱动开发)。但管理者必须审慎运用,将帕金森洞见与合理排期结合——压缩时间过多会触发霍夫施塔特定律的反弹,提醒我们即使留足缓冲,工作往往仍比预期更久。

对比案例: 同样的任务,给两个月会变成:一个月原型设计、一个月架构讨论、最后三周打磨没人要求的功能细节;而如果明确一周截止,则一周内交付。

四、度量指标如何扭曲工作

古德哈特定律:被瞄准的指标就失效了

核心观点: 一旦度量指标成为目标,它就不再是好指标。

我们可以用许多方式衡量工作:修复的 bug 数量、事件数、测试覆盖率、团队 velocity 等。一旦开始基于这些指标考核绩效,人们就会专注于让数字好看,而非真正做好工作。

当激励机制与真实目标错位,人们会追逐奖励本身而非我们真正想要的结果。"衡量的指标错了,行为就会跟着错"——这是 Goodhart 法则的核心警示。

tokenmaxxing:AI 时代的新指标游戏

21 世纪初,团队曾被奖励"代码行数"和"PR 数量",结果是开发者开始 copy-paste 而非提取公共逻辑,有人甚至每个 commit 都单独建一个 PR。

如今这个套路有了新名字——tokenmaxxing,即每名工程师消耗的 AI token 数量被当作生产力指标。token 用得越多,仿佛越高效。

Gilb 法则:能量化的就值得去量化

任何需要量化的事物,都能找到某种测量方式来胜过"完全不测量"。

Gilb 法则与 Goodhart 法则一体两面。不是说有指标就是坏事——没有任何指标比糟糕的指标更危险。正如管理大师 Peter Drucker 所言:我们无法改进无法衡量的事物。

开发者生产力向来难以衡量。代码行数、token 消耗量都是糟糕的代理指标。真正有价值的信号来自 DORA 指标——部署频率与变更前置时间,才能反映工程团队的真实效能。

五、高负载下的脆弱点

Knuth 优化原则:过早优化是万恶之源

过早优化是万恶之源。

大多数性能工作开始得太早、选错了位置。团队优化从未成为热点的代码路径,引入永远用不上的复杂度,在可能永远不会遇到的规模问题上消耗时间。

正确做法是:先写能工作的代码,再测性能。有性能问题,工具会告诉你瓶颈在哪;没有,就继续往前走。

早年待过一家初创公司,花了大量时间搭建 Kubernetes 来应对"百万用户",结果产品连 10 个用户都没到。基础设施在为不存在的负载做准备,产品的核心功能还没完成。同事说得对:先让 100 个人真正想要你的产品,再操心百万用户的架构。我们还是延期上线了。

Amdahl 法则:并行化的速度上限

并行化带来的加速受限于必须串行的部分。若工作中有 10% 必须串行执行,无论用多少台计算机,理论最大加速只有 10 倍;若 50% 串行,加速上限就是 2 倍。

这个规律在人员组织上同样适用。当所有决策都需要同一个小组拍板,无论工程师数量多少,团队的交付速度都会被这个瓶颈卡住——人越多,等待队列越长。

水平扩展 Web 流量时,增加更多应用服务器有效,但当所有请求都要经过同一个共享数据库或认证服务时,再横向扩展也无济于事。

AI 编码正在加速开发,但思考、检查、修复错误、协作对齐这些步骤无法并行执行,这决定了最终收益的上限。难怪有的工程师感觉效率提升 10 倍,有的只有 1.2 倍。

Murphy 法则:凡可能出错的事,必出错

软件工程中,Murphy 法则常被用来解释 bug 和生产事故:代码中任何可能出问题的地方(空指针、竞态条件、网络中断)

 
内容管家

发表评论