最近在 Claude 开发者社区里,连 Anthropic 内部的工程师们都在反复聊一个现象:当模型写代码的能力跨过某个临界点之后,如果人类工程师还停留在「怎么把这个函数写出来、怎么把眼前这个报错糊弄过去」的层面上,人就会沦为整个回路里最慢的阻塞点。工程师必须往上升一层,站到更上层的抽象去理解需求、定义边界。
几乎是同一周,我和合伙人 Sammy 复盘线上架构时,他也感慨了一句:「在这个 AI 时代,工程师贵在上层抽象思维。」
这句话听起来像一句温和的行业共识,但如果把它往深处推一步,底下其实是一道相当冷酷的分水岭:在 AI 写代码比人快十倍的时代,写代码本身已经不再是瓶颈。如果顶层的抽象是干净的,Agent 去实现就能以十倍的速度交付复利;如果顶层的概念是乱的,Agent 只会以十倍的速度替你制造技术债。
这篇文章想把「上层抽象」从一句口号拆成一套可操作的工程刀法:当我们面对一个 Bug、一次线上事故、一段冗余流程或一句模糊的需求时,怎么从「修眼前这一个」,走向「让这一类彻底不存在」。
一、为什么「先把眼前这一个修了」是最危险的习惯
过去我们碰到 Bug、报错、测试红了、评审意见,或者有人跑来问「X 怎么办」,本能的第一反应往往是列一张处理清单:出了 A 怎么办,出了 B 怎么办,怎么用最快的速度把眼前这一个报错消掉。
在纯人工编写代码的年代,这种习惯情有可原。因为人写代码太慢、改结构的动静太大,打个临时补丁混过今晚的发版窗口,在短期账面上是划算的。
但到了 Agent 时代,这个习惯变成了毒药。原因很简单:只修眼前这一个,等于在结构上默认它还会再来,以后只能一次次收拾。
一类问题之所以会反复出现,从来不是因为「某个人粗心」或者「模型今天抽风犯了个错」,而是因为系统里有个结构允许它出现。只要那个结构还在,换个文件、换个人、换个模型、换个时间,同一类故障就一定会换件衣服再敲门。相反,如果你顺着眼前这一个报错往下摸,找到那个允许它存在的系统结构并把它改掉,这一整类问题就同时消失了,眼前这一个不过是顺带扫掉的余灰。
一个好的抽象,等于提前把以后一批同类的事都定好了。只修这一个,是在替坏结构打工;消掉这一类,才是在给系统攒复利。
二、两大思维双引擎:第一性原理下潜,奥卡姆剃刀剪枝
怎么从「这一个」跳到「这一类」?光靠拍脑袋想「我要抽象一点」是没用的,空想出来的抽象往往是过度设计的空中楼阁。真正撑起上层抽象的,是两把相互咬合的手术刀:第一性原理负责向下挖到底,奥卡姆剃刀负责把多余的实体砍干净。
第一把刀:第一性原理(First Principles)—— 剥离表象与惯性,下潜到不可约简的物理本质。
绝大多数糟糕的设计,起点都是经验类比:「别人是怎么做的」「以前是怎么做的」「行业惯例是怎么配的」。类比让你留在地面上搬砖,而第一性原理逼你垂直往下挖井:把那些未经物理规律推导的「既定前提」一层层剥开,一直问到不能再简化的物理事实与业务诉求为止——这件事在物理上真正不可妥协的输入输出到底是什么?哪些我们习以为常的「必须条件」,其实只是过去图省事留下的历史妥协?
就像我们在上一篇《流程的本质》里推导过的那样:「上」是覆盖面的投影,「深」是地基的位置。好的抽象,高与深是重合的——你往下挖到了真正的基岩(不变量),站在基岩上说话,自然就拥有了俯瞰全局的海拔。
第二把刀:奥卡姆剃刀(Occam's Razor)—— 如无必要,勿增实体,能减不增。
人在遇到困难时天生有强烈的「加法本能」:服务调不通就想加层代理,状态不一致就想加个定时同步脚本,并发撞车就想加把锁,Agent 犯错就想往系统提示词里塞十条「严禁 XXX」的补丁,线上出事就想再加两道审批。更要命的是,大语言模型比人更爱做加法——它的预训练语料里装满了人类几十年来用中间层堆出来的防御性冗余。
结果系统越搞越重。每一个新加出来的实体(新组件、新配置、新网关、新账号、新流程),本身都有失效率,都会变成下一批故障的温床。奥卡姆剃刀的判据冷酷而直接:在所有能解决问题的结构里,挑假设最少、引入实体最少、依赖路径最短的那一个。能删减就不保留,能复用就不新建。真正顶级的工程架构,不是没有任何东西可以再加,而是没有任何东西可以再减。
三、四步除根法:把哲学变成肌肉记忆
为了不让团队和 Agent 在遇到问题时退回「打补丁」的本能,我们把这套思维固化成了每一轮设计、排障、评审都必须照走的四步:
第一步,从这一个,到这一类。
把眼前的具体事件翻译成一句脱掉专有名词的通用命题。把具体的文件名、接口名、人名、时间全部换掉,依然成立的那句话,才是你要对付的「这一类」。比如,「CI 是拿旧的 main 跑的,合进去以后把主干搞红了」是这一个;抽成这一类,叫「检查的结论绑在一个会动的指针上」。
第二步,找它从哪来。
用第一性原理往下问到系统的结构为止:这一类问题要哪几样东西同时在场才会出现?哪条本该成立的不变量被破了?系统为什么允许它被破——是没写出来、没人负责、靠人脑子记得,还是两样冲突的东西共用了一个位置?只要你的归因还停留在「某人漏看了文档」或「模型这次幻觉了」,就说明还没挖到底。
第三步,让这一类不存在。
对着找出来的结构成因,按从强到弱的顺序动用奥卡姆剃刀:
· 最强的一档叫「让它不可能发生(实体切除)」:直接拿掉导致问题的多余组件、把错位的执行环境放回原处、改成只追加(Append-only)、全链路只留唯一写作者、或者按内容哈希寻址;
· 次强的一档叫「让它一出现就被当场抓住」:离源头越近越好,用类型系统、CAS(比较后交换)、入口原子校验把脏状态挡在门外,且报错信息自己说清病因;
· 保底的一档才是「让它的代价变小」:自动回退、隔离爆炸半径。
第四步,最后才修眼前这一个,并留下一道单向回归棘轮。
在剪枝后的极简结构下,把眼前剩下的最后一点收尾动作做完,并把这条新立下的不变量写成回归守卫,永远锁进系统的底座里。
四、四个真实工程切片:结构改掉的那一刻,问题是怎么消失的
切片一:执行位置错位 —— 为什么最优雅的 VPN 是一行都不配
测试环境的云服务为了安全收紧了内网访问权限。紧接着,开发者在自己的 Mac 笔记本上跑本地冒烟评测脚本时,全线报 404。按照人类的「加法本能」,第一反应极其丝滑:内网不通,那就给每台开发者的 Mac 配上 Tailscale VPN、下发内网路由;为了管住 VPN 权限,再把公司的企业 SSO 接进来。方案越写越厚,不仅扯出了企业账号超管权限的黑盒,还埋下了「一旦某台机器 VPN 掉线就阻塞发版」的定时炸弹。
那晚我们停下来,用第一性原理问了一个最傻的问题:我们的真正物理诉求到底是什么?
诉求是「在测试环境跑完 18 张批量图片生成和冒烟验证」。这件事需要的是「内网连通性 + 脚本执行算力」。那么,这个算力有什么物理定律规定它必须在开发者的个人 Mac 笔记本上跑吗?根本没有!测试任务本来就属于云端,把它拉回个人电脑跑,再千辛万苦把内网牵到个人电脑上,是典型的执行位置错位。
看清这一层后,奥卡姆剃刀一刀落下:不要把内网往笔记本拉,直接把评测任务放回测试内网现成的云端测试机或 Cloud Run Job 里执行。一刀下去,Tailscale 没了,客户端没了,企业 SSO 没了,跨境网络抖动也没了——零新实体,零公网暴露,这一类网络打通问题连根消失。
切片二:单字段双写作者 —— 修了五次还在坏的状态回跳
在我们的多会话连接器里,曾经有一个让人抓狂的顽疾:前端消息流每隔几天就会出现一次滚动花屏或状态莫名回跳。前四次排障,无论是人还是 Agent,都盯着读取这个状态字段的前端渲染组件改——加防抖、加时间戳比对、加缓存兜底。每次改完看似好了,过几天换个交互路径又犯。
第五次我们拒绝再碰读取端,按四步法往上追了一层:把具体的变量名遮住,这一类到底是什么?答案瞬间浮出水面:「一个字段同时回答了两个问题,而且系统里有两个写作者在并发写它。」
只要允许两个不同时钟的写作者往同一个槽位里覆写,读取端写再精妙的防抖都是在猜谜。根治的动作轻得惊人:在源头把字段拆开,一个字段只回答一个问题,全链路只保留唯一写作者,在类型与接口层直接禁掉另一方的写权限。从那天起,前端那些复杂的防抖补丁全删了,状态回跳一次也没再出现过。
切片三:当底线吞掉了委托 —— 为什么发版 Agent 突然变成了「十万个为什么」
这是我们在自治 Agent 体系里踩过的一个极具代表性的治理问题。某天,我更新了一条全局最高安全准则:「所有生产操作必须逐项、指名授权,多条规则冲突时一律取更严的那条。」
规则合入后的第二天,原本已经非常熟练、每天只需要找我确认约 3 次的发版 Agent,突然变成了「十万个为什么」,一天找了我 9 次,连最常规的镜像标签核对都停下来等我点名批准。
如果按「这一个怎么修」的思路,你可能会去改发版提示词,催它「胆子大一点」。但如果你往上看一层结构,就会发现这是一个普遍的制度悖论:多层规则叠加时,如果合并函数只有「冲突取更严」这一个单向棘轮,而没有给「显式委托」留出结构槽位,那么任何一次底线加固,都会顺手把下层早已授出的合理自治权一刀切没。约束只能越加越紧,写不出「这个范围内由你自己判断」的系统,迟早会把所有活儿重新推回给人。
更妙的是,把它倒过来——「某次 Agent 没问人自己乱点了批准」——其实也是同一个结构硬币的背面(缺了不可逾越的底线)。于是我们在结构上同时补齐了两半:底线照旧做不可突破的围栏,但在合并规则里显式增设「带原话、日期与边界的委托范围(Authorization Scope)」;同时在行为层加了一条定时体检指标——「单会话找人次数突然翻倍」或「越权尝试次数 > 0」。用结构解耦替代提示词拔河。
切片四:会动的指针 —— 为什么「合并前必须基于最新 main 重跑 CI」是把全局锁
许多团队把「PR 合并前必须 rebase 到最新 main 并重跑全套检查」奉为圭臬。团队只有三个人时岁月静好;当十几个 Agent 并行提 PR 时,灾难来了:每合进一个 PR,其余十几个正在跑 CI 的 PR 结论瞬间全部作废,必须推倒重跑。吞吐量被活活锁死在串行队列里。
用第一步把它翻译成「这一类」:检查的结论绑在了一个会动的指针上。破损的不变量是「验证结论的有效性不该被无关的前进所污染」。怎么用结构消掉它?让检查结论只绑在不可变的内容哈希(代码树哈希 + 规则版本哈希)上;至于两个变更合在一起对不对,交给合并队列的快速增量验证与合并后的自动回退。把「全局锁」拆成「不可变内容证明 + 乐观并发回退」。
五、剃刀的两条护栏:切冗余实体,绝不切入口防呆与存量不变量
用熟了奥卡姆剃刀之后,我们发现无论是人类工程师还是 Coding Agent,都极容易滑向另一个伪极简的陷阱:打着「如无必要勿增实体、精简代码」的旗号,把系统入口处的参数校验、非空与有限数(NaN / Infinity)拦截给删了;或者在把一段慢代码重构为高效结构时,顺手把原代码里不起眼的开闭区间(>= 写成 >)、去重先后顺序或失败回滚给改丢了。
为了堵住这个陷阱,我们在上层抽象规范里立下了两条剃刀护栏:
第一,剃刀切的是冗余架构实体,绝不切入口原子防呆守卫(Fail-Fast Boundary Preconditions)。
奥卡姆剃刀要砍掉的是多余的中间层、重复的状态表和投机性的配置开关;而在系统或模块入口处,对非法类型、越界配额、缺失主键做「写状态前的先验原子拦截(脏请求一旦进来立即抛错,保证零脏状态残留)」,恰恰属于四步法里最硬的那一档——「让脏状态从源头不可能写进去」。省掉入口防呆不是极简,是拆掉城门。
第二,重构与提速必须严守「存量边界守恒」。
当你的诉求是「把 O(N²) 的慢代码改成 O(N)、或者把巨石文件拆成多模块,且保持既有行为一致」时,原本慢代码里已经成立的所有临界算符、滑窗边界、排序稳定性与异常回滚,全是不可破坏的物理不变量。正如我们在《忒修斯之船》里写过的:你可以把船上的木板(实现算法)一块不剩地全换掉,但船形(外部可观察的边界契约)连一毫米都不许漂移。
六、单向回归棘轮与「T1 变异证伪」:从没变红过的测试只是安慰剂
想清楚了结构,用剃刀切掉了冗余,修完了眼前的问题——这件事算结束了吗?
在真实工程里,最让人心寒的往往不是新 Bug,而是「三个月前明明已经根治过一次的一类问题,在某次新功能迭代时又被后来的代码悄悄弄坏了,而且直到线上告警响起都没人知道」。
靠人在 Code Review 时拿脑子记「这里以前踩过坑」,违背了有限理性。唯一的解法是把每一次上层抽象的胜利,都铸成一颗单向回归棘轮(Monotonic Regression Ratchet):棘轮只许往前咬合,绝不许往后倒退。
但这里藏着 Agent 时代最隐蔽的一个共因失效:如果你让写代码的同一个 Agent「顺手补一条回归测试」,它极有可能会写出一条一跑就绿、但实际上根本没测到核心边界的「恒真测试(Tautological Test)」。测试跑过了,绿灯亮着,所有人安心上线,其实守门员在呼呼大睡。
怎么证明守门员是醒着的?科学哲学家波普尔早就给过答案:可证伪性(Falsifiability)。在我们的交付门禁里,这件事被做成了硬性的 T1 变异证伪(Mutation Falsification):
任何一条新增的回归测试,在合入主干之前,必须亲手把生产代码里它声称看守的那道边界故意改坏一次(比如把 >= 改成 >、注释掉那行原子回滚),亲眼看到这条测试当场变红报错,再恢复生产代码变回全绿。从未在故意破坏下变红过的绿灯,只是昂贵的心理安慰剂。
七、尾声:代码归机器,抽象归人
现在可以回到开篇 Claude 社区的讨论,以及 Sammy 的那句话:「在这个 AI 时代,工程师贵在上层抽象思维。」
过去三十年,软件工程把大量的薪酬与体面发给了「手艺」:谁背熟了冷门的语言特性、谁手写样板代码更快、谁能熬夜在十万行日志里人肉捞出那行报错。今天,这些建立在「手工执行昂贵」之上的手艺,正在以肉眼可见的速度坍塌成十秒钟的上下文注入。
但当潮水退去,裸露出来的工程基岩反而比以往任何时候都更硬、更无法造假:
你能不能穿过业务方一句含糊的抱怨,一眼认出它属于哪一类结构冲突?你敢不敢在所有人忙着叠补丁、加中间件的时候,用第一性原理问一句「这个组件到底为什么要存在」?你能不能定出那条最简、最硬的物理不变量,并用一道经过变异证伪的棘轮把它焊死在系统里?
代码会越来越便宜,直到趋近于零;而那个决定代码往哪儿长、不许往哪儿长的「上层抽象」,就是 AI 时代工程师唯一拿不走的签名。
全文与交互式 Three.js 3D 实验室:
https://matthewohmygosh.com/2026/10/26/higher-abstraction/