# 为什么越菜的软件工程师看问题越简单越高级的软件工程师看问题越复杂? **回答者:** chouheiwa​ ​ **日期:** 未知 --- 0.34 秒的浮点精度误差,28 条人命。这段出事的代码没有任何语法错误,逻辑也很清晰,就是一个每 0.1 秒累加一次的计时器。一个刚入行的新手做 code review,大概率会直接跳过这段,觉得没什么好看的。而一个写了十年代码的老兵盯着这个 0.1,脑子里会自动弹出一个问题:0.1 在二进制里是无限循环小数,累加次数多了误差会不会爆? 1991 年海湾战争,沙特达兰,一枚爱国者导弹[[1]](#ref_1)未能拦截来袭的飞毛腿。根因是系统用 24 位定点寄存器追踪时间,以 0.1 秒为单位累加。0.1 在二进制里是无限循环小数,截断后每个 0.1 秒引入约 **0.000000095 秒**的误差。系统连续运行 100 小时,误差累积到 **0.34 秒**,飞毛腿时速 1676 m/s,0.34 秒就是 500 多米的追踪偏差。导弹飞偏了,28 个美国佬没能回家。 五年后,1996 年,阿丽亚娜 5 号火箭[[2]](#ref_2)首飞爆炸,损失 **5 亿美元**。根因是 64 位浮点数转 16 位有符号整数溢出。程序员从阿丽亚娜 4 号继承了这段代码,认为"这个值不可能溢出",甚至主动关掉了编译器的溢出保护。7 个类似转换里只有 4 个做了保护,剩下 3 个最终炸了火箭。 这两个案例有一个共同特征:**出事的代码,新手看一眼觉得"没问题",老手看一眼觉得"这里可能出事"。** 差别到底在哪? ## 人人都高估自己,但新手错得最离谱 心理学里有个现象:越是在某个领域能力差的人,越倾向于高估自己的水平。1999 年两个康奈尔大学的心理学家 Kruger 和 Dunning[[3]](#ref_3) 做了一系列实验验证了这件事,后来被叫做"达克效应"。这个概念在互联网上传播极广,很多人用一句"菜鸟不知道自己菜"就把这个问题解释完了。 但事情没这么简单。Gignac(2024)[[4]](#ref_4) 在 Intelligence 期刊发表的论文重新建模后发现,达克效应的峰值出现在 **IQ 55 的人群中**,仅占总人口 **0.14%**。到 IQ 70 时过度高估已降至 7 分,覆盖人口 **2.3%**。换句话说,这个效应只影响极小比例的极端人群,对绝大多数软件工程师根本不适用。Magnus & Peresetsky(2022)[[5]](#ref_5) 进一步证明,经典的达克曲线图可以用纯统计模型完美复现,压根不需要任何心理学解释。说白了,那条著名的"自信心曲线"很可能只是均值回归加有界数据[[6]](#ref_6)的统计伪迹。那真正在起作用的是什么? 其实吧,更站得住脚的解释是 Kruger & Dunning 原始论文里一个经常被忽略的洞察:**评估自身能力需要的技能,恰恰就是完成任务本身需要的技能。** 你不会写分布式系统,你也无法判断自己写的分布式系统有多烂。这个道理古人早就讲过,老子说"知不知,尚矣;不知知,病也[[7]](#ref_7)",知道自己不知道,是高明的;不知道却以为自己知道,那才是真正的毛病。孔子说"知之为知之,不知为不知,是知也[[8]](#ref_8)",能准确判断自己知道什么、不知道什么,这本身就是最大的智慧。现代认知科学给这种能力起了个名字,叫元认知[[9]](#ref_9)(metacognition)。Rum & Zolkepli(2018)的研究发现,元认知意识分数与编程成绩的相关系数高达 **r = 0.8226**,在 1% 水平显著。编程活动越困难,程序员越需要元认知控制技能,而低能力程序员恰恰缺乏这种自我评估能力。 ![](img_1.jpg) ![](data:image/svg+xml;utf8,) 这四个阶段跟 Noel Burch 的能力模型[[10]](#ref_10)完全对应,也跟 Dreyfus 技能习得模型[[11]](#ref_11)的五级框架高度一致。国内程序员圈子把它浓缩成了一句话:**从"不知道自己不知道"到"不知道自己知道"。** 这条路,没有捷径。 ## 专家的大脑里到底多装了什么 认知科学家 Gary Klein[[12]](#ref_12) 研究消防队长的决策过程时发现,**87% 的专家决策通过模式匹配完成**,而非逐项比较选项。一个消防队长走进火场,30 秒内就能判断"地板下面有火",不是因为他做了热力学计算,是因为他经历过类似的模式:脚底异常发烫 + 烟雾颜色偏黄 + 火焰声音偏低沉。这些模式已经编码进了他的直觉回路。 软件工程的"火场"也是一样的。一个资深架构师看到系统设计图,能瞬间识别出"这里有单点故障""这个查询在数据量到百万级时会全表扫描""这两个服务之间的循环依赖会导致级联故障"。他靠的不是比新手更聪明,而是脑子里装了几千个失败模式。新手没有这个模式库,所以他看到的只是"功能需求被满足了"。 我自己的经历倒是印证了这一点。写 iOS 快十年,早期做外包驻场银行的时候,拿到一个需求就是"赶紧实现",很少想边界条件。后来做逆向工程的时候被迫养成了一个习惯:拿到一段代码,第一反应是"它在什么条件下会崩",然后才去看它在做什么。说白了就是"失败视角"优先。跟我之前在[数据结构选型那篇文章](https://www.zhihu.com/question/1953056523677852284/answer/2021535782448440355)里讲的一样,理论上的"正确"和工程上的"能跑"之间,隔着一整座冰山。 还有一个吊诡的地方。认知负荷理论[[13]](#ref_13)告诉我们,人类工作记忆容量大约只有 **4 个组块**。新手的工作记忆被语法细节、API 调用方式、框架配置占满了,根本没有剩余容量去考虑并发安全、性能退化或安全漏洞。专家通过多年经验把常见模式压缩成单个认知单元(这叫"组块化"),释放出来的认知空间,全部用来扫描更高维度的风险。结果就是:**同一个登录功能,新手想到"用户名密码校验",专家同时想到 SQL 注入、暴力破解限速、密码哈希算法选择、会话管理、CSRF 防护、日志审计合规。** 新手估 2 天,专家估 2 周,不是专家效率低,是专家看到了 10 倍的工作量。 ![](img_2.jpg) ![](data:image/svg+xml;utf8,) GitHub 上 zakirullin 的 cognitive-load 项目[[14]](#ref_14)把这件事讲得很透彻,获得了 Rob Pike 和 Andrej Karpathy 的推荐。Google Chrome 工程师 Addy Osmani[[15]](#ref_15) 在推荐中提到过一个观察:很多制造复杂性的架构模式,恰恰是以"代码整洁"的名义引入的。新手学了设计模式就到处套用,这种"中级陷阱"在国内程序员圈子里有个精准吐槽:"你蒙中了,就叫正交分解。你没蒙中,就叫过度设计。" ## 本质复杂性是隐形的 写代码写得越久,越能体会 Fred Brooks 1986 年[[16]](#ref_16)提出的那组区分:**本质复杂性**(Essential Complexity)是问题域固有的,你换什么语言、什么框架都消灭不了;**偶然复杂性**(Accidental Complexity)是工具和实现带来的,可以通过技术进步消减。 新手以为自己面对的大部分是偶然复杂性,因为他接触到的痛苦(语法报错、配置出错、编译不过)确实多数属于这一类。但本质复杂性对新手是隐形的。时区处理不是"调个 API"就完事的,IANA 时区数据库[[17]](#ref_17)每年更新 **3-8 次**,因为政府可以随时修改夏令时规则。缓存失效不是"设个 TTL"就解决的,Phil Karlton 说[[18]](#ref_18)计算机科学只有两个真正困难的问题:缓存失效和命名。分布式系统的 exactly-once delivery 在数学上被证明不可能实现[[19]](#ref_19),所有声称做到的消息队列,要么在骗你,要么在用幂等性做 at-least-once 的包装。 Ian Duncan(2025)[[20]](#ref_20) 写过一段特别到位的自我反省:"我自己也犯过这个错:走进一个新代码库,看到复杂的架构,立刻想'这过度设计了吧'。但有些复杂性是业务驱动的,来自真实世界的需求和约束,对新加入系统的人来说,这些约束不是一眼就能看出来的。" Clojure 的创始人 Rich Hickey[[21]](#ref_21) 在 Strange Loop 2011 的演讲 Simple Made Easy 里做了一个关键区分:**"简单"(Simple)是客观的,意味着没有纠缠交织;"容易"(Easy)是主观的,意味着熟悉、顺手。** 新手优化的是"容易",选熟悉的框架、能快速跑起来的方案;专家优化的是"简单",选纠缠最少的方案,即便学习曲线更陡。这个区别,外行看不出来。 ![](img_3.jpg) ![](data:image/svg+xml;utf8,) Peter Deutsch 等人[[22]](#ref_22)在 1990 年代总结的分布式计算八大谬误至今仍是新手最大的盲区。2025 年 Neal Ford & Richards[[23]](#ref_23) 在架构书第二版中又新增了三条:版本控制很容易、补偿性更新总能奏效、可观测性是可选的。新手调用一次 API 看到 200 OK 就以为万事大吉,根本不会想到:**如果响应丢失了但副作用已经发生了怎么办?** Two Generals Problem[[24]](#ref_24)(1975)在数学上证明了通过不可靠通道协调行动是不可能的。Leslie Lamport[[25]](#ref_25) 的名言堪称完美注脚:"分布式系统就是这样一种系统,你甚至不知道存在的某台计算机坏了,也能让你的计算机变得不可用。" ## AI 编码工具:达克效应的类固醇版 2025 年之后这个话题多了一个全新的变量。Andrej Karpathy 造了个词叫 vibe coding[[26]](#ref_26),声称"我 Accept All,不再读 diff"。这个词后来成了 Collins 2025 年度词汇。看起来很美好,但数据讲了一个完全不同的故事。 METR(2025 年 7 月)[[27]](#ref_27)的随机对照实验发现:经验丰富的开发者**主观上认为 AI 让他们快了 20%,但客观测量显示他们实际上慢了 19%**。这个认知偏差本身就是达克效应在 AI 时代的完美再现。Veracode 2026 年的报告[[28]](#ref_28)显示 **45% 的 AI 生成代码引入安全漏洞**。Stack Overflow 2025 调查[[29]](#ref_29)中,AI 工具使用率从 70% 涨到 76%,但正面情绪首次下降,从 77% 降到了 72%。开发者开始意识到:AI 让你写得更快,但不一定让你写得更对。 ![](img_4.jpg) ![](data:image/svg+xml;utf8,) 我每天用 [Claude Code](https://www.zhihu.com/question/2022395025309352665/answer/2022572673213953603) 写代码,它确实帮我省了大量样板代码的工作。但我注意到一个现象:AI 生成的代码跑通 happy path 的概率很高,但防御性编程几乎为零。它不会主动考虑并发竞态、不会预判内存泄漏、不会质疑你的架构决策。说白了,AI 擅长消减偶然复杂性(生成样板代码、处理语法、写标准模式),但对本质复杂性束手无策(理解业务领域、管理系统级架构一致性、识别真实世界约束产生的边界条件)。跟我之前在[底层逻辑那篇文章](https://www.zhihu.com/question/1891616492644172459/answer/2019724170037080744)里讲的一样,AI 时代底层知识的角色变了,从"用来写代码"变成"用来判断代码"。 Addy Osmani 概括得精准:"AI 工具放大你的专业能力。对于缺乏扎实基础的人,AI 可能导致达克效应的类固醇版本,看起来你建了个很棒的东西,直到它崩溃。" Hacker News[[30]](#ref_30) 上有帖子直接叫 "AI is Dunning-Kruger as a Service"。一位用户分享了真实案例:一个初级工程师本该部署一个写好的 Terraform 模块,结果交上来的仓库一团糟,里面多了一堆莫名其妙的 Python 代码。问他"为什么有这些代码?"他说:"我不知道,Claude 弄的。" 2026 年初 Agent 这波热潮把这个问题推到了极端。2 月份最出圈的事故是 Meta 超级智能实验室的 AI 对齐总监 Summer Yue 被自己的 Agent 坑了[[31]](#ref_31):她用开源 Agent 框架 OpenClaw 管理邮箱,结果 Agent 直接批量删除了几百封邮件,她在聊天窗口连打好几次"stop"都没用,最后只能物理断电。她事后承认这是"rookie mistake",因为之前在小规模测试数据上跑得很顺利,就过度自信地直接接入了真实邮箱。一个职位是"AI 对齐总监"的人都会掉进这个坑,其他人只会更容易中招。 其实吧,Agent 的问题比编码工具更深一层。编码工具生成的烂代码好歹还需要人来部署,你有一道人工审核的缓冲。Agent 不一样,它直接操作真实系统,发邮件、改数据库、调 API,一旦判断出错,后果是即时的、不可逆的。CNBC 报道[[32]](#ref_32)了一个饮料制造商的案例:AI 系统因为不认识新换的节日包装标签,把正常产品判定为异常,自动触发了额外生产线,等人发现的时候已经多生产了几十万罐。系统没有 malfunction,它严格执行了程序,只是程序员没预料到包装会变。还有一个更扎心的数据:如果 Agent 每一步的准确率是 **85%**(听起来不低了吧),一个 10 步工作流的整体成功率只有 20%[[33]](#ref_33)。这不是优化问题,是概率的乘法法则。要让 10 步流程整体成功率超过 80%,每一步的准确率得到 **98%** 以上。Gartner 预测[[34]](#ref_34)截至 2026 年 2 月已有 **40%** 的 Agent 项目被暂停或取消。 这就回到了这篇文章的核心论点:**你越觉得一件事简单,越说明你没看到它真正的复杂性。** Agent 让这个判断失误的成本从"代码写烂了"升级到了"系统直接出事了"。 其实吧,初级工程师还不是最极端的情况。现在越来越多完全没写过代码的人,用 AI 搭了个能跑的 demo,就觉得编程也不过如此。他们跳过了"不知道自己不知道"这个阶段,直接进入了"我什么都能做"的幻觉。一个程序员好歹知道"部署""数据库""并发"这些词的存在,非程序员连这些概念都没有,AI 帮他把偶然复杂性清零了,他就真以为复杂性本身不存在。这才是 vibe coding 最危险的地方:它让完全没有工程判断力的人,产出了看起来完整的软件产品。 ## 怎么从"看到简单"走向"看到复杂" React 核心团队成员 Dan Abramov[[35]](#ref_35) 2018 年发了一篇 Things I Don't Know,作为前端领域最受尊敬的工程师之一,公开列出了自己的大量知识盲区:Unix 命令、底层语言、算法("你最多从我这得到一个冒泡排序")、TypeScript、DevOps……他的核心洞察是:"无论你多有经验,你仍然会在感到胜任、感到不够格和感到过度自信之间切换。" HN 上一条高赞评论[[36]](#ref_36)把这个现象提炼为一句话:**"偏执是经验的绝佳指标,过度自信是缺乏学习的绝佳指标。"** Monica Lent[[37]](#ref_37) 写过一篇 "7 Absolute Truths I Unlearned as Junior Developer",其中提到她作为新手会"重写 300 行不熟悉的代码而不是试图理解它"。这种行为背后的认知模式很典型:新手面对不理解的东西,本能反应是推倒重来,因为"理解别人的代码"对他来说比"自己写一套"认知负荷更高。而专家恰恰相反,他倾向于先理解再动手,因为他知道不理解上下文就动手改代码,十次有九次会引入新的问题。 ![](img_5.jpg) ![](data:image/svg+xml;utf8,) 我的个人方法论倒是很朴素,就是化工出身那套[数据结构选型那篇文章](https://www.zhihu.com/question/1953056523677852284/answer/2021535782448440355)。在化学实验室里做实验,出了问题你不知道是哪一步错了,只能一次改一个变量,其他条件全部固定,然后观察结果。写代码调 bug 是一模一样的逻辑。但更深层的意义在于:**控制变量法强迫你承认自己不知道答案。** 它的前提是"我不确定问题在哪里",这本身就是元认知的起点。很多新手面对 bug 的第一反应是"我知道问题在这里",然后凭直觉同时改好几个地方,改完发现问题没解决,也不知道是哪个改动让事情变得更糟了。 中国互联网公司的 [P 级体系](https://www.zhihu.com/question/366212151)里有一个广泛认同的观察:**P6 到 P7 是认知分水岭。** P6 是"熟练的执行者",还稍微缺乏对复杂项目的阅读能力与大局观;P7 要求"不但能干好手上的活,还知道为什么要这么干",能把复杂项目分解到可落地并理解系统级问题。一篇广为流传的晋升分析[[38]](#ref_38)说得很直白:"阿里不缺技术牛逼的人,缺的是能够用牛逼的技术解决问题的人。" 这就是这道题的终极答案。新手看到的是需求,专家看到的是需求背后的约束、约束之间的冲突、以及冲突带来的失败模式。**看到复杂性的能力,本身就是最稀缺的工程能力。** 它不来自聪明,来自踩坑。每一个让你觉得"这很简单"的判断,都值得再多想三十秒。 ## 参考 1. [^](#ref_1_0)爱国者导弹失效事故分析  2. [^](#ref_2_0)阿丽亚娜 5 爆炸事故报告  3. [^](#ref_3_0)邓宁-克鲁格效应介绍  4. [^](#ref_4_0)Dunning-Kruger effect论文  5. [^](#ref_5_0)达克效应可由统计模型复现  6. [^](#ref_6_0)达克效应是统计伪迹分析  7. [^](#ref_7_0)老子:知不知,尚矣;不知知,病也  8. [^](#ref_8_0)孔子:知之为知之,不知为不知,是知也  9. [^](#ref_9_0)Metacognitive Strategies in Teaching and Learning Computer Programming  10. [^](#ref_10_0)能力四阶段模型  11. [^](#ref_11_0)Dreyfus 技能习得模型  12. [^](#ref_12_0)Gary Klein:识别驱动决策法  13. [^](#ref_13_0)软件工程中的认知负荷理论  14. [^](#ref_14_0)zakirullin:cognitive-load  15. [^](#ref_15_0)Addy Osmani:AI 编码工作流  16. [^](#ref_16_0)No Silver Bullet 条目  17. [^](#ref_17_0)Python 时区处理与 IANA 数据库  18. [^](#ref_18_0)Martin Fowler:两个难题  19. [^](#ref_19_0)Exactly-once 不可能性分析  20. [^](#ref_20_0)Ian Duncan:复杂性何为偶然  21. [^](#ref_21_0)Rich Hickey:Simple Made Easy  22. [^](#ref_22_0)分布式计算八大谬误介绍  23. [^](#ref_23_0)Thoughtworks:三条新谬误  24. [^](#ref_24_0)Two Generals' Problem  25. [^](#ref_25_0)Leslie Lamport 介绍  26. [^](#ref_26_0)Vibe coding 条目  27. [^](#ref_27_0)METR:AI 让资深开发者变慢  28. [^](#ref_28_0)Veracode:AI 代码安全风险  29. [^](#ref_29_0)Stack Overflow 2025 AI 调查  30. [^](#ref_30_0)HN:AI is Dunning-Kruger as a Service  31. [^](#ref_31_0)Summer Yue 的 Agent 失控事故  32. [^](#ref_32_0)CNBC:AI 系统业务风险案例  33. [^](#ref_33_0)AI Agent 多步工作流失败率  34. [^](#ref_34_0)Gartner:40% Agent 项目暂停  35. [^](#ref_35_0)Dan Abramov:Things I Don't Know  36. [^](#ref_36_0)HN:偏执是经验的指标  37. [^](#ref_37_0)Monica Lent:新手开发者反思  38. [^](#ref_38_0)阿里职级晋升能力分析