Skip to content

AI 需要学,你更需要学的 56 条软件工程定律 #434

@mqyqingfeng

Description

@mqyqingfeng

你好,我是冴羽

AI 写代码越来越猛了,但它懂软件工程的基本定律吗?

它不懂。

AI 可以写出语法正确的代码,但它缺少人类程序员用血泪换来的工程经验。

这些经验被总结成了 56 条软件工程定律

AI 需要学,你更需要学。 因为最终拍板决策的,还是你。

今天我把这 56 条定律全部整理出来,告诉你:

哪些是 AI 写代码时最容易踩的坑,哪些能帮你做出更好的工程决策。

1. 架构设计:3 条救命定律

架构设计三大定律

1.1. 康威定律:你的系统就是你的组织结构

**康威定律(Conway‘s Law): **组织设计的系统会反映其自身的沟通结构。

这是我见过最准的定律。

如果你的团队分成前端组、后端组、测试组,那你的系统一定会变成:前端调后端 API,后端写接口,测试写用例。看起来很合理?

**问题来了:**当你需要做一个跨端的功能时,三个组要开会、对接、扯皮。效率直接崩盘。

更好的做法是什么? 按业务线组队——每个小队有前后端和测试,负责一个完整功能。这样沟通成本降到最低。

康威定律告诉我们:先设计组织,再设计系统。 不然你的架构一定会被组织结构拖累。

1.2. 盖尔定律:复杂系统都是从简单系统进化来的

盖尔定律(Gall‘s Law): 能够运行的复杂系统,必然是从能够运行的简单系统演化而来的。

我见过太多团队,一上来就设计超级复杂的架构:微服务、消息队列、分布式事务、服务网格…… 结果半年过去了,系统还没跑起来。

盖尔定律说得很直白:你想一步到位设计复杂系统?不可能成功。

正确的做法是:先做一个能跑的简单版本,然后根据实际需求逐步演化。

  • 不要一开始就上微服务,先做单体应用

  • 不要一开始就做分布式,先做单机版

  • 不要一开始就做高可用,先让功能跑起来

能跑的简单系统,比设计精美的复杂系统有价值 100 倍。

1.3. CAP 定理:分布式系统的不可能三角

CAP 定理(CAP Theorem): 分布式系统只能同时保证一致性、可用性和分区容错性中的两个。

这是分布式系统的铁律:一致性(C)、可用性(A)、分区容错性(P),你只能选两个。

实际上,网络分区(P)是一定会发生的,所以你只能在 C 和 A 之间选:

  • 选 CP(一致性 + 分区容错):牺牲可用性。比如银行转账,宁可系统暂时不可用,也要保证数据一致。

  • 选 AP(可用性 + 分区容错):牺牲一致性。比如社交网络的点赞数,允许短时间内不一致,但系统必须一直可用。

很多新手设计分布式系统时,幻想三个都要。 CAP 定理告诉你:醒醒,不可能。

2. 代码质量:5 条黄金原则

代码质量五大原则

2.1. 童子军规则:让代码比你发现时更好

童子军规则(The Boy Scout Rule): 让代码比你发现时更好。

这是我最喜欢的一条规则。

你不需要大规模重构,只需要:每次改代码时,顺手优化一点点。

  • 看到重复代码?抽个函数

  • 看到烂命名?改个好名字

  • 看到没注释的复杂逻辑?加两行说明

**一年下来,代码质量会有质的飞跃。 **而且不会像大规模重构那样引入新 bug。

2.2. YAGNI 原则:不要写你用不到的代码

**YAGNI 原则(You Aren‘t Gonna Need It): **不要添加当前不需要的功能。

很多程序员喜欢“未雨绸缪”:这个功能现在用不到,但以后可能会用,先写上吧。

结果呢?90% 的“以后可能用”永远不会用。 你写的代码变成了技术债务,增加维护成本,还可能引入 bug。

YAGNI 原则告诉你:只写当前需要的代码,不要猜测未来。

2.3. DRY 原则:不要重复自己

**DRY 原则(Don‘t Repeat Yourself): **每一项知识都必须有单一、明确、权威的表示。

重复代码是万恶之源。

当你复制粘贴代码时,你创造了 两个需要维护的地方。改一个地方,另一个地方忘了改,bug 就来了。

DRY 不只是说代码,还包括配置、文档、业务逻辑。 任何知识都应该只有一个权威来源。

2.4. KISS 原则:保持简单

KISS 原则(Keep It Simple, Stupid): 设计和系统应该尽可能简单。

简单不是简陋,是清晰。

一个好的设计,应该让人一眼就能看懂:这个函数做什么?这个类负责什么?这个模块解决什么问题?

如果你的代码需要长篇注释才能看懂,那说明设计太复杂了。

2.5. 过早优化是万恶之源

**过早优化(Premature Optimization): **过早优化是万恶之源。

这是高德纳(Donald Knuth)说的。

很多程序员喜欢在写代码时就考虑性能:这里用 HashMap 会不会更快?这里用位运算会不会更省内存?

结果呢?代码变得难以理解,但性能提升微乎其微。

正确的做法是:先让代码跑起来,再用性能分析工具找瓶颈,最后针对性优化。 不要凭感觉优化。

3. 团队协作:4 条残酷真相

3.1. 布鲁克斯定律:给延期项目加人只会更晚

布鲁克斯定律(Brooks‘s Law): 给延期的软件项目增加人手只会让它更晚完成。

这是最反直觉的定律之一。

项目延期了,老板说:加人!多招几个开发! 结果项目更晚了。

为什么?

  • 新人需要时间熟悉项目,这段时间他们产出为零

  • 老人要花时间带新人,产出下降

  • 沟通成本指数级增长:3 个人有 3 条沟通路径,10 个人有 45 条

布鲁克斯定律告诉我们:人月不是可以互换的。 9 个女人不能在 1 个月内生出孩子。

3.2. 林格尔曼效应:团队越大,个人越摸鱼

林格尔曼效应(The Ringelmann Effect): 随着团队规模增大,个人生产力会下降。

这是心理学实验证明的:团队人数越多,每个人的努力程度越低。

在 2 人团队中,每个人会拿出 90% 的努力。在 10 人团队中,每个人只拿出 50% 的努力。

为什么? 因为责任分散了:反正有别人在干,我少干点也没事。

所以最好的团队规模是多少? 贝佐斯说:两个披萨能喂饱的团队。 大概 5-8 人。

3.3. 巴士因子:团队的脆弱性指标

**巴士因子(Bus Factor): **失去多少团队成员会让项目陷入严重困境的最小数量。

这是一个残酷的问题:如果你的核心开发被车撞了,项目还能继续吗?

如果答案是“不能”,那你的巴士因子就是 1。这意味着你的团队极其脆弱。

怎么提高巴士因子?

  • 代码审查:让多人了解每块代码

  • 文档:把关键知识写下来

  • 结对编程:知识自然传播

  • 轮岗:让每个人都接触不同模块

巴士因子越高,团队越健康。

3.4. 帕金森定律:工作会填满所有可用时间

帕金森定律(Parkinson‘s Law): 工作会膨胀以填满完成它所需的全部时间。

给你 1 周时间做一个功能,你会用满 1 周。给你 1 个月时间,你会用满 1 个月。

为什么? 因为人会不自觉地放慢节奏、增加细节、过度打磨。

怎么破? 用更短的迭代周期:不要给 1 个月时间,给 1 周时间,逼自己聚焦核心功能。

4. 项目管理:3 条时间定律

4.1. 九十九十定律:最后 10% 的工作需要另一个 90% 的时间

**九十九十定律(The Ninety-Ninety Rule): **前 90% 的代码占用前 90% 的开发时间,剩余 10% 的代码占用另外 90% 的时间。

这是每个程序员都经历过的噩梦。

项目进度到 90% 时,你觉得马上就能上线了。结果:边界情况、异常处理、性能优化、bug 修复…… 又花了跟之前一样长的时间。

所以做项目排期时,永远不要相信“还剩 10%”。 把剩余时间乘以 2 才是真实的。

4.2. 侯世达定律:永远比你预期的更久

**侯世达定律(Hofstadter‘s Law): **完成一件事所花的时间总是比你预期的要长,即使你已经考虑了侯世达定律。

这是一个递归定律。

你预估 1 周,实际用了 2 周。下次你学聪明了,预估 2 周,结果还是延期。

为什么? 因为你永远无法预见所有意外:需求变更、依赖服务出问题、自己生病、突然来的紧急任务……

侯世达定律告诉我们:做排期时,把你的预估再乘以 2-3 倍。 这不是悲观,是现实。

4.3. 古德哈特定律:指标变成目标就失效了

**古德哈特定律(Goodhart‘s Law): **当一个指标成为目标时,它就不再是一个好指标。

公司说:我们要提高代码质量,所以要求代码覆盖率达到 80%。

结果呢?大家开始写无意义的测试,只为了提高覆盖率数字。 代码质量没提高,反而浪费了时间。

古德哈特定律告诉我们:不要把指标当成目标。 指标只是工具,真正的目标是背后的价值。

5. 系统设计:4 条陷阱定律

系统设计四大陷阱

5.1. 抽象泄漏定律:所有抽象都会泄漏

**抽象泄漏定律(The Law of Leaky Abstractions): **所有重要的抽象在某种程度上都是有漏洞的。

你用 ORM 操作数据库,以为不需要懂 SQL 了?错。

当你遇到性能问题时,你必须知道 ORM 生成了什么 SQL,是否有 N+1 查询,索引是否生效。

抽象是为了简化,但它无法完全隐藏底层细节。 关键时刻,你还是要懂底层。

5.2. 特斯勒定律:复杂性守恒

特斯勒定律(Tesler‘s Law): 每个应用都有其固有的不可简化的复杂性,这些复杂性只能转移,不能消除。

你想让用户界面简单?那后端逻辑就会变复杂。

你想让后端简单?那前端或者用户就要承担复杂性。

复杂性不会消失,只会转移。 你的工作是决定:把复杂性放在哪里最合适?

一般来说:让专业的人承担复杂性。 程序员承担技术复杂性,让用户界面保持简单。

5.3. 第二系统效应:第二版往往过度设计

**第二系统效应(Second-System Effect): **小而成功的系统往往会被过度设计、臃肿的替代品所取代。

第一版系统成功了,团队信心爆棚,开始做第二版:这次我们要把所有想法都加进去!

结果:功能臃肿、架构复杂、开发周期长、bug 一堆。 第二版反而失败了。

典型案例:很多创业公司的第二版产品。 第一版简单粗暴但有效,第二版想做大而全,结果死在半路上。

5.4. 分布式计算的 8 个谬误

**分布式计算的谬误(Fallacies of Distributed Computing): **分布式系统新手设计者常犯的八个错误假设。

新手设计分布式系统时,常犯这 8 个错误假设:

  1. 网络是可靠的 → 错,网络会断

  2. 延迟为零 → 错,网络调用很慢

  3. 带宽无限 → 错,带宽有限

  4. 网络是安全的 → 错,会被攻击

  5. 拓扑不变 → 错,服务器会上下线

  6. 只有一个管理员 → 错,分布式系统有多个管理员

  7. 传输成本为零 → 错,传输数据有成本

  8. 网络是同构的 → 错,不同服务用不同技术栈

设计分布式系统时,必须考虑这 8 点。 否则生产环境一定出问题。

分布式计算8个谬误

6. 测试与调试:3 条铁律

6.1. 林纳斯定律:足够多的眼睛,bug 无处藏身

**林纳斯定律(Linus‘s Law): **只要有足够多的眼睛,所有 bug 都是浅显的。

这是开源软件成功的秘密。

一个人写代码,bug 可能藏很久。但如果有 100 个人看代码,bug 很快就会被发现。

这就是为什么代码审查这么重要。 不要一个人闷头写代码,让团队成员互相审查。

6.2. 柯尼汉定律:调试比写代码难一倍

**柯尼汉定律(Kernighan‘s Law): **调试代码的难度是编写代码的两倍。

这是一个残酷的事实。

如果你写代码时用尽了你的智商,那调试时你的智商就不够用了。

所以不要写太聪明的代码。 写简单、清晰、容易理解的代码,这样调试时才不会崩溃。

6.3. 农药悖论:重复的测试会失效

**农药悖论(Pesticide Paradox): **重复运行相同的测试,随着时间推移会变得越来越无效。

就像害虫会对农药产生抗性,代码也会“适应”测试。

你写了一套测试,最初能发现很多 bug。但随着时间推移,这套测试发现的 bug 越来越少。

为什么? 因为你的代码已经针对这些测试进行了优化,新的 bug 出现在测试覆盖不到的地方。

所以要定期更新测试用例,增加新的测试场景。

7. 性能优化:2 条关键定律

7.1. 阿姆达尔定律:并行化的极限

阿姆达尔定律(Amdahl‘s Law): 并行化带来的加速受限于无法并行化的工作部分。

假设你的程序有 90% 可以并行,10% 必须串行。

你用 100 个 CPU 并行执行,理论上应该快 100 倍?错。

因为那 10% 串行部分无法加速,所以最多只能快 10 倍。

阿姆达尔定律告诉我们:优化性能时,先优化串行瓶颈,再考虑并行化。

7.2. 梅特卡夫定律:网络价值与用户数的平方成正比

**梅特卡夫定律(Metcalfe‘s Law): **网络的价值与用户数量的平方成正比。

这不是性能定律,但对产品设计很重要。

一个社交网络有 10 个用户,价值是 100。有 100 个用户,价值是 10,000。

这就是为什么社交产品有“网络效应”。 用户越多,产品越有价值,越能吸引新用户。

8. 认知偏误:5 条思维陷阱

认知偏误五大陷阱

8.1. 邓宁-克鲁格效应:越无知越自信

**邓宁-克鲁格效应(Dunning-Kruger Effect): **你对某事了解越少,往往越自信。

刚学会 React 的新手:React 太简单了,我全懂了!

用了 3 年 React 的老手:React 的坑太多了,我还有很多不懂……

这就是邓宁-克鲁格效应:新手过度自信,专家充满怀疑。

所以当你觉得某个技术“很简单”时,可能只是因为你还没遇到真正的难题。

8.2. 汉隆的剃刀:不要归咎于恶意

**汉隆的剃刀(Hanlon‘s Razor): **能用愚蠢或疏忽解释的,就不要归咎于恶意。

产品经理改需求,你觉得:他是故意整我!

其实很可能只是:他没想清楚。

大部分问题不是因为恶意,而是因为疏忽、误解、信息不对称。

汉隆的剃刀告诉我们:先假设对方是善意的,多沟通,少猜忌。

8.3. 确认偏误:只看想看的信息

**确认偏误(Confirmation Bias): **倾向于偏爱支持我们现有信念或想法的信息。

你觉得 Vue 比 React 好,所以你只看 Vue 的优点,忽略 Vue 的缺点。

你觉得微服务是银弹,所以你只看微服务的成功案例,忽略微服务的复杂性。

确认偏误让我们看不到真相。 对抗它的方法是:主动寻找反对意见,质疑自己的假设。

8.4. 沉没成本谬误:不要为过去的投入买单

**沉没成本谬误(Sunk Cost Fallacy): **因为已经投入了时间或精力而坚持某个选择,即使放弃对你更有利。

你花了 3 个月写一个框架,后来发现有更好的开源方案。但你不想放弃:我都花了 3 个月了,不能白费!

这就是沉没成本谬误。 过去的投入已经无法收回,不应该影响未来的决策。

正确的做法是:评估未来的收益,而不是过去的投入。 如果开源方案更好,就果断切换。

8.5. 帕累托法则:80% 的问题来自 20% 的原因

帕累托法则(Pareto Principle): 80% 的问题来自 20% 的原因。

  • 80% 的 bug 来自 20% 的代码

  • 80% 的性能问题来自 20% 的瓶颈

  • 80% 的用户价值来自 20% 的功能

**帕累托法则告诉我们:找到那关键的 20%,集中火力解决。 **不要平均用力。

帕累托法则80/20

9. 其他重要定律

9.1. 隐姆定律:所有行为都会被依赖

**隐姆定律(Hyrum‘s Law): **当 API 用户足够多时,系统的所有可观察行为都会被某人依赖。

你设计了一个 API,文档说:返回值的顺序不保证。

但用户发现返回值总是按字母顺序排列,于是他们的代码依赖了这个顺序。

某天你改了实现,顺序变了,用户的代码就炸了。

隐姆定律告诉我们:任何可观察的行为,都会被人依赖。 所以改 API 时要格外小心。

9.2. 波斯特尔定律:发送时保守,接收时宽容

**波斯特尔定律(Postel‘s Law): **发送时要保守,接收时要宽容。

这是网络协议设计的黄金法则,也适用于 API 设计。

  • 发送时保守:严格遵守规范,不要发送模糊的数据

  • 接收时宽容:尽量兼容各种输入,不要轻易报错

这样系统才能健壮,才能长期演化。

9.3. 林迪效应:存在越久,越可能继续存在

**林迪效应(The Lindy Effect): **某样东西使用的时间越长,它继续被使用的可能性就越大。

JavaScript 已经存在 28 年了,它还会存在很久。

上周出的新框架?可能明年就没人用了。

林迪效应告诉我们:选技术栈时,优先选经过时间考验的。 不要追新,追稳。

9.4. 坎宁安定律:获得正确答案的最好方法是发布错误答案

**坎宁安定律(Cunningham‘s Law): **在互联网上获得正确答案的最好方法不是提问,而是发布错误答案。

你在论坛问:React 的最佳实践是什么? 没人回答。

你发帖说:React 的最佳实践是全局状态! 一堆人跳出来反驳,告诉你正确答案。

这是互联网的真相:人们更愿意纠正错误,而不是回答问题。

10. AI 和你都该记住的事

这 56 条定律,不是让你死记硬背,而是让你:在用 AI 写代码或做工程决策时,能想起来有前人总结过类似的经验。

AI 不懂这些,但你必须懂:

  • 项目延期时,想起布鲁克斯定律,不要盲目加人

  • 设计架构时,想起盖尔定律,先做简单版本

  • 写代码时,想起 YAGNI 原则,不要过度设计

  • 做排期时,想起侯世达定律,把时间预估翻倍

  • 遇到分歧时,想起汉隆的剃刀,假设对方是善意的

AI 可以写代码,但它不会权衡。 它不知道什么时候该用微服务,什么时候该用单体。它不知道什么时候该优化性能,什么时候该保持简单。它不知道什么时候该重构,什么时候该维持现状。

这些定律是前人用血泪换来的经验,能帮你和 AI 少踩很多坑。

但也要记住:定律不是教条,要结合实际情况灵活运用。 有时候打破定律,反而是正确的选择。

软件工程没有银弹,只有权衡。 这 56 条定律,是帮你做出更好权衡的工具。

AI 在学,你也在学。但最终做决策的,是你。

我是冴羽,10 年笔耕不辍,专注前端领域,更新了 10+ 系列、300+ 篇原创技术文章,翻译过 Svelte、Solid.js、TypeScript 文档,著有小册《Next.js 开发指南》、《Svelte 开发指南》、《Astro 实战指南》。

欢迎围观我的“网页版朋友圈“,关注我的公众号:冴羽(或搜索 yayujs),每天分享前端知识、AI 干货。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions