深度理解编码方法论

晚上睡觉的时候,刷到了Matt Pocock前段时间在AI Engineer大会上的切片,感觉有点东西,于是就爬起来把完整的视频看了一遍,感受颇深。

他分享的内容叫做 Software Fundamentals Matter More Than Ever

意思就是软件工程基础比以往任何时候都更加重要

那么什么是软件工程基础呢?Matt在分享中反复提到了两本书,分别是:

哦,原来这就是软件工程师基础惊喜啊!

什么是AI Engineer?

Matt参加的大会是AI Engineer大会,那么什么是AI Engineer呢?

AI Engineer 是指通过 AI 构建并通过 AI 驱动应用程序的软件开发者。

  • 你不需要理解线性代数或高级数学
  • 你不需要从零开始构建基础模型(Foundation Model)
  • 你不需要读过《Attention is All You Need》这篇论文

你真正需要的是:

  • 扎实的软件工程基础 Software Fundamentals Matter More Than Ever
  • 构建可靠、可扩展应用的能力
  • 了解现代 AI 工具和框架
  • 关注用户体验和实际应用场景

AI 工程师的职责,是连接传统软件工程与人工智能之间的鸿沟。主要工作内容是:

  • 编排和调用 AI API
  • 实现 RAG(检索增强生成)系统
  • 构建健壮的评估系统,确保 AI 应用稳定可靠

不管要写什么样子的代码,都是用自然语言来写。

这样就很简单了,会说话=会开发。

但真的是这样吗?

Vibe Coding

很多人对 Vibe Coding 的理解,是随性地跟 AI 聊,把脑子里的想法直接告诉它。哪里有 bug,继续让 AI 改。哪里跑不通,再补一句 Prompt。

反正 AI 已经很强了,多试几次总能跑起来。

如果只是写一个小工具,或者做一个不会长期维护的原型,这套方法当然可以。它甚至是目前最舒服的用法。你不用太纠结结构,也不用花太多时间设计,先把东西跑起来再说。

但如果目标是一个大中型系统,事情就不是这样了。

我自己的感受是,AI 生成第一版代码时,往往看起来还不错。第二次补需求,质量开始下降一点。第三次、第四次继续改,代码里就会开始出现一些不太舒服的东西:命名不一致,逻辑散落在多个文件里,同一个概念被写成几种说法,局部看都能解释,放到一起就很别扭。

几轮下来,代码库会慢慢变得难维护。人再去读,需要花很多时间重新理清关系;AI 再回来读,也很容易沿着错误的结构继续写。

《程序员修炼之道》里有个词很适合形容这个现象:软件熵

熵增是宇宙基本规律,万事万物都倾向于变得混乱。

软件系统和现实世界一样,会自然地走向混乱。每次改动如果只盯着眼前的需求,不考虑系统整体结构,代码就会一点点变差。区别只是以前这个过程比较慢,现在 AI 生成代码的速度很快,熵增的速度也跟着快了。

Vibe Coding 有一个隐含前提:代码很便宜。

这个前提只对了一半。写出第一版代码确实变便宜了,但代码本身并没有变便宜。真正贵的从来不是那几行语法,而是后面的理解成本、维护成本、返工成本,以及系统失控之后重新收拾的成本。

在一个结构清晰的代码库里,AI 是很好的杠杆。目录清楚,命名一致,模块边界明确,测试能跑,AI 接到任务后很容易判断该改哪里,也不太容易改坏不相关的地方。

在一个混乱的代码库里,AI 就会变成放大器。同一个概念有好几种名字,业务逻辑散在十几个文件里,类型和测试都不完整。AI 每次改动都只能猜,猜错了还会把错误继续扩散。

所以我现在越来越觉得,AI 不是复杂度清理工具。它更像复杂度放大器。

给它一个有秩序的系统,它能放大秩序;给它一个混乱的系统,它也会把混乱放大。

这就是为什么Matt在那里反复强调, AI 时代Software Fundamentals Matter More Than Ever

以前它们只是“写好代码”的方法,现在它们直接决定 AI 能不能在一个项目里持续发挥作用。

Grill-With-Docs

《软件设计的哲学》固然教会了我如何去设计系统,但是实际使用调用AI中,总是感觉“它做出来的东西不是我想要的”。

明明需求也说了,指令也写了,AI 也确实完成了一个东西,但就是差点意思。于是继续补 Prompt,让它调整。调整完又差一点,再补一句,再改一轮。最后不是代码变好了,而是人开始怀疑自己是不是没表达清楚。

大多数时候,问题确实出在“没表达清楚”。但更准确地说,是我们自己也没有真正想清楚。

软件开发里有句老话:没人一开始就知道自己真正想要什么。

这句话在 AI Coding 里尤其明显。因为 AI 太快了,人脑里一个还没完全成形的想法,很快就会被它变成一堆文件。等文件生成出来以后,我们才发现很多细节其实没想过。

解决这个问题,开工前要多花一点时间。

第一个方法叫 Grill-With-Docs

做法很简单:不要一上来就让 AI 写代码,而是先让 AI 反过来问你问题。让它围绕这个计划,把不清楚的地方都问出来,把设计分支走一遍,把依赖关系理清楚,直到双方对要做的事情有一个比较一致的理解。

实际用起来会有点烦。AI 可能会问几十个问题,从用户角色、边界条件、失败场景,一直问到数据怎么存、权限怎么划、状态能不能回退。很多问题一开始看起来没必要,但问着问着就会发现,原来自己脑子里只是有一个大概的方向,并没有真正形成设计。

这种烦是值得的。

因为需求里的歧义,如果不在写代码之前暴露,就会在代码写完之后变成返工。前面多花十分钟把问题问清楚,后面可能少花几个小时拆掉重来。

我和小马在玩游戏的时候发现一个很有意思的现象:就拿Split Fiction来说,我们已经形成了一种“只可意会”的默契。

比如某个关卡,需要两个人协作控制,我控制左半大脑,他控制右半大脑,我说一句“这里感觉不太对”,小马立刻就知道是哪里不对。

这种理解既不在文档里,也不在游戏里,而是在长期合作中慢慢长出来的共同认知。也有可能是游戏玩太多了

人和 AI 协作也是一样,需要慢慢培养这种默契,而Grill-With-Docs这个Skill就能快速生成这种默契。

你以为自己说清楚了,AI 也表现得像是听懂了,但双方脑子里的东西可能完全不一样。Grill-With-Docs 的作用,就是把这些隐藏的假设摆到桌面上,让共同理解在动手之前先建立起来。

多说一句,Grill-With-Docs 也是Matt大神开发的。

第二个方法,是建立 Ubiquitous Language,也就是通用语言。

这是 DDD (domain-driven-design)里的概念。DDD 我这里不展开,简单说,就是让业务、代码和沟通里的核心词汇保持一致。

以前我学 DDD 的时候,对这个概念没有太强的体感。最近用 AI 写代码多了之后,反而越来越能理解它的重要性。

举个例子,一个电商项目里,运营把某个东西叫“单子”,客服叫“订单”,技术代码里写 Order,数据库表又叫 trade_record。如果这次 Prompt 里用了“订单”,AI 可能写一个 order_service;下次你说“交易”,它又写一个 trade_handler

过几轮之后,同一件事在代码里出现好几套名字。人看着费劲,AI 继续写的时候也会更容易误解。

通用语言的做法并不复杂。维护一个 Markdown 文件,把项目里的关键概念写清楚:哪些词是标准叫法,哪些词不要用,代码命名怎么定,数据库表和接口字段怎么对应。然后让 AI 每次开始工作前先读这个文件。

这个做法很朴素,但效果明显。

术语对齐之后,AI 输出的代码会稳定很多。命名更一致,模块职责也不容易飘。很多看似“AI 不够聪明”的问题,本质上其实是人和 AI 没有站在同一套语言里。

所以和 AI 协作里,最贵的成本不是写代码的时间,而是消除歧义的时间。

这件事越早做,后面越省力。

Agent介入

意图对齐之后,才轮到真正让 AI 干活。

这里有两个很重要的节奏控制方法:垂直切片和 TDD(Test-Driven-Development)。

先说垂直切片。

很多人用 AI 做项目时,会习惯性地给一个很大的需求。比如:帮我做一个任务管理系统,要有登录、任务列表、提醒、协作、后台管理。

AI 通常也不会拒绝。它会直接生成前端、后端、数据库、接口、组件,一口气给出很多文件。看上去项目一下子就有了规模。

但这种规模很多时候是虚的。

每一层都生成了一点,但每一层都没完全打通。前端有页面,后端有接口,数据库也有表,但真要验证一个用户场景时,可能发现到处都有缺口。你想确认“用户能不能创建任务并看到它”,结果要同时检查前端状态、接口参数、数据库字段、登录态和错误处理。

这时候问题就会变得很难定位。

更靠谱的方式,是按垂直切片来做。

不要先横着铺架构,而是先选一个具体的用户场景。比如:

用户输入一个问题 → 系统去知识库检索 → AI 生成答案 → 返回引用来源 → 用户看到结果

围绕这个场景,从前端按钮,到后端接口,到数据存储,再到测试和错误处理,竖着打通一条完整链路。这条链路能跑通了,再做下一片。

这种方式看起来慢一点,但它更适合 AI。

因为每一片的范围不大,AI 更容易保持上下文;每一片又是完整的,可以真实验证。出了问题,改动范围也比较清楚,不会牵一发动全身。

切片解决的是范围问题。TDD 解决的是反馈问题。

AI 写代码默认会比较冒进。给它一个任务,它很容易一口气写很多实现,写完以后再提醒你去跑测试、做类型检查。问题是等它写完时,错误可能已经扩散到很多地方了。

这时再回头修,就会很痛苦。

TDD 的价值,是把 AI 的步子切小。

先写一个测试,描述期望行为。再让 AI 写能通过这个测试的最小实现。跑通之后,再加下一个测试。

这样每一步都有反馈。跑偏了,马上就能发现,而不是等整个功能写完以后再从一堆 diff 里找问题。

《程序员修炼之道》里有句话说得很好:反馈速度决定开发速度。

在 AI 时代,这句话更明显。AI 生成代码的速度很快,如果反馈机制跟不上,代码库很快就会失控。

以前测试更像是写完代码之后的安全网,而现在它更像是写代码之前的约束。

人负责定义行为,AI 负责填充实现。测试写得越清楚,AI 越不容易乱跑。

AI 不缺速度,缺的是边界和反馈。

Deep Modules

Matt 在分享里问过现场的程序员,有多少人觉得自己用了 AI 之后反而更累。台下很多人举手。

这件事一开始听起来很矛盾。AI 不是提高效率吗,为什么人反而更累?

原因其实很简单:AI 产出代码的速度,已经超过了人理解代码的速度。

能写出来,不等于能看懂;能跑通,不等于能维护。

代码库的组织方式,直接决定这种压力有多大。如果模块都很浅,每个模块都暴露很多细节,AI 写得越多,人要装进脑子的东西就越多。每次想看懂一次改动,都要把一整张调用关系重新理一遍。

这时就要回到一个老概念:深模块,Deep Modules

这个概念出自 John Ousterhout 的《软件设计的哲学》。简单说,一个模块对外暴露的接口越少越清楚,内部封装的复杂度越多,这个模块就越深。

举个例子,要发邮件。一个比较深的模块,对外可能只暴露一个 sendEmail(to, subject, body)。调用方只需要传收件人、标题和正文。

至于里面如何连接服务器、如何认证、如何组装邮件头、如何重试、如何关闭连接,这些都是模块内部的事情。外部不需要关心。

浅模块则相反。它会把这些步骤一个个暴露出来,让调用方自己按顺序调用。表面上看,每个函数都很小,似乎很清楚;实际上是把内部复杂度泄露给了外部。

需要注意的是,深模块不是把所有代码都塞进一个函数里。那只是另一种混乱。

深模块讲的是公共接口要少而稳定,内部实现可以拆成很多小函数,但这些小函数属于模块内部,不应该随便暴露给外部使用。

AI 很容易写出浅模块。

它倾向于把一个过程拆成很多看起来合理的函数,然后把这些函数都放到外面。这样代码表面上很“模块化”,但调用方要理解太多内部细节。时间一长,整个系统就会变得很难改。

深模块的好处,是让人可以把一部分复杂度暂时放下。

只要接口设计清楚,行为有测试覆盖,模块内部的实现日常就不需要每次都重新读一遍。需要改它时再进去看,不需要改它时,只要相信它的契约。

人的脑力是有限的。

AI 把代码生产速度提高之后,这个限制会变得更明显。如果每一处复杂度都暴露在外面,人很快就会被细节淹没。

深模块本质上是在给人减负。

它让开发者不用把整个系统的所有细节都装进脑子里,而是可以把注意力放在更高层的问题上:边界画得对不对,接口够不够稳定,模块之间有没有互相污染。

到这里,AI 时代开发者的角色其实也就清楚了。

AI 可以很快地完成实现,但它仍然需要有人控制方向。这个方向不是简单地写一个 Prompt,而是持续判断系统应该怎么长。

开发者的工作,会越来越多地从“亲手写每一行代码”,转向“设计约束、控制复杂度、维护系统秩序”。

总结

前面说的这些方法,通用语言、垂直切片、TDD、深模块,没有一条是新东西。

它们都能在很多年前的软件工程书里找到出处。

这也是 Matt 那场分享真正有意思的地方。

他不是在说 AI 时代出现了一套全新的工程方法,而是在说:AI 越强,过去那些基本功越不能丢。

很多做了十年、十五年、二十年的开发者,看到 AI 一夜之间能写代码,会自然地产生一种焦虑:自己过去积累的能力是不是不值钱了。

但从实际使用看,情况可能正好相反。

AI 把“手快”这件事变得不再稀缺。以前能很快写一个页面、接一个接口、搭一个原型,是很明显的优势。现在这部分能力被工具大幅放大之后,真正稀缺的东西开始转移到判断力上。

  • 你能不能判断什么是好代码。

  • 你能不能看出系统正在变乱。

  • 你能不能把模糊的需求拆成清楚的行为。

  • 你能不能设计出稳定的接口和合适的模块边界。

这些能力不会因为 AI 会写代码就消失。相反,AI 写得越快,这些能力越重要。

因为没有判断力的人,用 AI 只会更快地制造复杂度。

有判断力的人,才能让 AI 变成真正的杠杆。

所以我不太认同“会写 Prompt 就够了”这种说法。

Prompt 当然重要,但 Prompt 只是入口。真正决定结果的,是你能不能理解系统,能不能管理复杂度,能不能在代码变多之前先把秩序建立起来。

AI 能帮人写代码,但帮不了人理解什么是好代码。

编码这件事变容易了,理解编码,反而更重要了。

面向DeepSeek编程
Microsoft 365 Message Center档案馆