
软件工程有一个反直觉的地方:很多问题在 1975 年就有人描述清楚了,但每一代工程师还是要亲自踩一遍。
Brooks 在《人月神话》里写了 Brooks 定律,Gall 写了 Gall 定律,Conway 写了 Conway 定律。这些定律不涉及任何特定语言或框架,它们描述的是人在时间压力下合作构建系统时反复出现的模式。框架换了一代又一代,这些模式没有变。
下面整理的 20 条定律,按作者 Dr. Milan Milanović 的原文分成 6 个主题:系统如何被构建、团队如何失速、计划为什么会跑偏、度量如何扭曲行为、负载下什么会断、如何做出更好的判断。
一、系统如何被构建
1. Gall 定律
一个运转正常的复杂系统,都是从一个运转正常的简单系统演化来的。
从头设计并试图”完整”的系统,通常会失败。真实环境会暴露纸上设计看不见的问题,而这些问题只有在真实用户使用时才会出现。
Instagram 的前身 Burbn 是一个把签到、游戏、照片共享全塞在一起的应用,后来创始人把其他功能全部砍掉,只留下照片共享,这个极简核心才变成了产品。Google Wave 则反过来,一上来就同时包含聊天、邮件、论坛和文档编辑器,15 个月后下线。
2. KISS 原则
保持简单。超出这一点的东西都是额外负担。
用 50 行脚本能解决的问题,就不要设计 500 行的复杂方案。每一行代码都有出错的可能,简单的设计更容易上手、更容易定位 bug、更不容易因为修改产生连锁影响。
一个典型的反面教材:某创业公司需要一个功能开关系统,结果搭建了独立微服务、独立数据库、缓存、管理界面和 WebSocket 通知。他们真正需要的其实只是一个 JSON 配置文件,一个下午就能搞定。
3. Conway 定律
组织设计出来的系统,会映射组织内部的沟通结构。
你的架构图本质上和组织架构图是同一张图。前端、后端、数据团队如果不通气,交付出来的系统三个部分就不会协同工作。在不改变组织结构的情况下重写系统,你得到的还是同一个系统,只不过换了一种语言。
反过来也成立:先定好你想要的架构,再建立能自然产出这种架构的团队,这就是”逆 Conway 操纵”。Amazon 在 2000 年代就这么做了,把系统拆成小型服务,由小团队各自负责。
今天的 AI 组织里也经常出现这个模式:研究团队优化基准指标,产品团队面向真实用户,两边的优化目标不同步,跑分好看的模型和产品体验不一致。
4. Hyrum 定律
只要用户足够多,你 API 的每一个可观察行为最终都会成为某人的依赖,不管契约怎么写。
你维护的不是你设计的 API,而是系统实际表现出来的那个 API,包括你从未预料到会重要的部分:响应时间、错误消息的文本、JSON 键的顺序、哈希值的具体字节。
SimCity 在 Windows 3.x 上有一个释放后仍访问内存的 bug,刚好能运行,因为系统不会真正回收内存。Windows 95 实际回收了内存,SimCity 崩溃了。微软在 Windows 95 里专门为 SimCity 加了一个特殊的内存分配模式。浏览器也在互联网规模上做同样的事,开发者依赖的每一个怪癖都实际上成了平台规格的一部分。
5. CAP 定理
分布式系统只能同时保证以下三项中的两项:一致性、可用性、分区容错性。
网络会分区,这不是需要绕过的设计约束,而是必须接受的现实。分区发生时你只能二选一:阻塞写入以保持数据一致,或者继续服务允许副本数据漂移。MongoDB 偏向一致性,分区时部分副本不接受写入;Cassandra 偏向可用性,继续响应查询,事后再修复不一致。两者都没有错,只是在做不同的取舍。
6. Zawinski 定律
每个程序最终都会扩张到能收发邮件。做不到的那些,会被能做到的取代。
功能蔓延不是开发过程中发生的偶然,它就是开发过程本身。一个工具越受欢迎,产品方就越想把用户留在上面,工具就开始承接越来越多相关任务,然后变慢、变臃肿。
Netscape 从浏览器变成了带邮件、新闻和网页编辑器的套件;Firefox 以精简替代者出现,但后来也加了插件系统和开发者工具链。Slack 是来消灭邮件的,现在有了语音、视频、Bot 和应用目录。
二、团队如何失速
7. Brooks 定律
向一个已经延期的软件项目加人,只会让它更晚交付。
软件工作不像工地搬砖,可以线性拆分。新人加入需要时间上手,而上手的代价是老人要停下手头工作来带他们。如果项目已经延期,再加人只会让事情更糟。
“九个女人不能在一个月内生出一个孩子”——Brooks 的原话。原作者曾带领一个八人团队,始终跟不上进度,计划招两个人来追赶。找人期间,两个人先离职了。结果反而好了:沟通变简单,交付反而变快了。答案是把团队缩小,不是扩大。
8. 林格尔曼效应
团队越大,人均产出越低。
多人拔河时,每个人出的力都不如单独拔时多。一方面是协调成本,另一方面是”有别人在,我少出点力也没关系”的心理。
一项 GitHub 研究量化了这个效应:2-5 人团队的开发者月均提交约 1850 行代码,10 人团队降到 1200 行,50 人以上降到 450 行——人均产出下降了 75%。这也是亚马逊”两张披萨原则”的来源。AI 时代这个规律没有消失,个人与小团队的生产力上限被抬高,但人均效率随团队规模下降的趋势没变。
9. Price 定律
一半的工作由团队人数的平方根完成。
100 人的团队里,大约 10 人承担了一半有价值的工作;16 人的团队里,大约 4 人。这个规律在所有创意领域都成立。
Twitter 被收购后裁员约 50%,网站照常运转,Price 定律预测到了这一点。但定律没能预测到被裁掉的是信任与安全、SRE 值班和事故响应能力。顶尖贡献者让灯继续亮着,但组织处理下一个难题的能力消失了,最后还悄悄请一些离职的人回来了。
三、计划为什么会跑偏
10. Hofstadter 定律
事情总是比你预期的要久,即使你已经把 Hofstadter 定律考虑在内。
你估四周,知道自己总是乐观,于是乘以二变成八周,然后花了十六周。下次你就估十六周,然后花了三十二周,因为未知的东西总会让你措手不及。
柏林勃兰登堡机场的软件集成涉及 75000 个传感器和 50000 个灯具。计划 18 个月完成,延到 30 个月,最终花了 7 年,成本是计划的 2.5 倍,机场晚了 9 年才开业。
11. 邓宁-克鲁格效应
对某件事了解越少,往往越自信。
评判自己做得好不好的能力,和把事情做好的能力是同一种能力——这正是问题所在。新手看不出自己哪里错了,所以觉得自己做得不错;老手能看出很多还没做对的地方,反而觉得自己还不够好。
新手开发者面对工期估算,往往给出精确的数字;有经验的工程师给的是范围,或者”这取决于……”。不是新手更自信,而是他们还不知道自己不知道什么。对新技术最热情的人,往往是最少实际用它的那些人——比如管理者。
12. 帕金森定律
工作会扩展,直到填满所有可用的时间。
给开发者两个月去做一周的任务,他会花一个月做原型探索,一周做架构讨论,最后三周打磨没人要求的细节。给同一个任务一周的截止日期,它就在一周内交付了。
但不要压缩得太极端,否则会遭遇 Hofstadter 定律的反噬。截止日期要清晰、合理,不是越紧越好。
四、度量如何扭曲行为
13. 古德哈特定律
一个度量指标一旦成为目标,就不再是好的度量指标了。
无论你用什么来衡量工作——关闭的 bug 数、测试覆盖率、团队速度——一旦把它作为绩效指标,团队就会把精力放在让数字好看上,而不是把工作做好。
2000 年前后有团队按代码行数奖励开发者,后来又按创建的 PR 数。开发者开始复制粘贴而不是抽取公共逻辑,开始每提交一次就开一个 PR。现代版本是用 AI token 消耗量衡量工程师生产力,消耗更多 token 被当作努力工作的证明。
14. Gilb 定律
任何你需要量化的东西,都可以用某种方式来衡量,哪怕这种方式比完全不测量好一点点。
这是古德哈特定律的另一面。看到度量失真,有人会说”那就什么都不测量”,但这比糟糕的指标还糟糕。不能改进你不测量的东西(Drucker 的名句)。
开发者生产力一直难以测量,代码行数失败了,token 消耗量也在失败,但部署频率和变更前置时间(DORA 指标的核心)作为代理指标,能给你一个有意义的信号。
五、负载下什么会断
15. Knuth 优化原则
过早优化是万恶之源。
大部分性能工作发生得太早,方向也不对。团队优化了那些从未成为瓶颈的路径,引入了不必要的复杂度,浪费时间解决一个可能根本不会到来的规模问题。
正确的顺序:先写能运行的代码,然后检测性能。如果有问题,工具会告诉你在哪里。一家创业公司曾经花大量时间搭建 Kubernetes,理由是”为了百万用户规模”——彼时连 10 个真实用户都没有。
16. Amdahl 定律
并行化带来的加速,受限于必须串行执行的那部分工作。
如果 10% 的工作必须串行,不管你加多少机器,最多只能快 10 倍。如果 50% 串行,最多只能快 2 倍。同样的逻辑适用于团队:如果所有人都要等同一个人点头才能继续,加多少工程师都无济于事,只会让等待队列变长。
AI 让编码速度提升了,但你仍然需要思考、验证、修复错误和协作,这些不可并行的步骤设定了最终收益的上限。这就是为什么有的工程师说 AI 让他快了 10 倍,有的人只快了 1.2 倍。
17. 墨菲定律
凡是可能出错的,最终都会出错。
在软件里,这不是一种悲观情绪,而是设计原则:空指针最终会被触发,竞态条件最终会出现,网络最终会中断,而且通常发生在星期五晚上。
2024 年 7 月 19 日,CrowdStrike 的 Falcon Sensor 配置更新导致 850 万台 Windows 机器蓝屏死机,修复需要逐台登录操作,远程修复无法完成。问题发生在周五早晨,IT 人员不在岗。航空、医院、银行全部受影响。一切能出错的都出错了。
18. Postel 定律
发送时保守,接受时宽容。
你的服务发出 HTTP 响应时,要严格遵循规范格式头。但接收时,即使对方的请求头顺序奇怪或格式不标准,只要能安全解析,就应该处理而不是拒绝连接。浏览器在互联网规模上就是这么做的,网上大部分 HTML 都有语法错误,但浏览器照样渲染。
但要注意边界:过于宽容会让问题永远得不到修正,在安全敏感的代码里,接受任何格式的输入会为攻击者打开大门。宽容不等于纵容,需要判断。
六、如何做出更好的判断
19. Sturgeon 定律
90% 的东西都是垃圾。
大部分写出来的代码用不到,大部分启动的项目交付不了预期价值。这不是坏事,这是创造过程的常态。问题在于,如果假装一切都很好,就会把每个项目当作同等重要来对待,最终一无所获。
WordPress 目录里有大约 57000 个插件,其中超过 34000 个两年以上没更新,约 19% 完全没有活跃安装。少数维护良好的插件支撑了公网上 40% 以上的站点。这就是 Sturgeon 定律的分布。
20. Cunningham 定律
在网上获得正确答案最快的方式,是发布一个错误答案。
在论坛里提问往往没有回应,但如果你贴出一个明显错误的内容,人们会忍不住来纠正你。这个心理可以被有意利用:与其问”怎么做”,不如先提出一个你知道有问题的方案,看看会发生什么。
维基百科就是建立在这个洞察上的:人们纠错的速度远比从零写文章快。这个赌注在文明规模上赢了。
结语
这些定律不会告诉你该怎么做,但会帮你理解正在发生什么。它们经常互相矛盾:Knuth 说不要过早优化,Amdahl 说要找到串行瓶颈并修复它。两者在不同场景下都对,关键是知道什么时候用哪一条。
最后说一件实际的事:这份清单是作者的清单,你的清单会不一样。那些在你的项目里真实发生过的定律,对你来说比任何其他定律都更重要。把它们写下来——每个项目、每次事故、每次重写,一行记录,哪条定律应验了,哪条给了你有用的判断。你自己的列表比这里任何一条都更值钱。