跳到正文
Hacker News· zakirullin·· 9 天前AI 评分22

从 COBOL 到 vibe coding:用英语取代代码为何一再失败

English Instead of Code

AI 导读

文章回顾了 COBOL、SQL、4GL 等"用自然语言取代编程"的尝试,指出它们非但没消灭程序员,反而催生了 DBA、数据工程师、企业系统架构师等新岗位。作者认为当前以 vibe coding 为代表的新一轮尝试将重蹈覆辙,因为编程语言并非"英语的劣化版",而是用于消除歧义、固定每一个决策细节的专用工具。

正文 · AI 翻译

一种为了让程序员不再被需要而发明的语言,催生了一个至今依然存在且薪酬相当不错的职业;另一种语言被称为“结构化英语”,人们以为任何会计都能用它来写程序,它同样催生了一个新的专业领域,并有着自己的薪资等级。此外还有人尝试“到两千年前实现无需程序员的编程”,结果也同样适得其反。

我想你认出了 COBOL,它至今仍在日常生产中使用,据各种估计,它支撑着 80% 的普通(店内)银行交易,其代码库高达数千亿行,并且仍然养活着数万乃至数十万名开发者。不过,如果你深究这些漂亮的数字,几乎全都追溯到九十年代末的一项调查,然后被无限地外推到全世界,所以更诚实的做法是把它们当作数量级,而不是精确数据。

SQL 催生了 DBA(数据库管理员)和数据工程师这一奇特阶层,如今一个写“简单查询”的人就能轻松拿到中级水平的薪资,而语言本身已变得如此复杂,以至于现代方言(PostgreSQL、Oracle)都是带有过程逻辑的完整编程语言,你可以在其中写任何东西,从分形生成器到游戏引擎。

4GL 家族结果成了一个“金笼子”,在你需要构建典型的“输入-输出”表单时表现得非常出色,但一旦需要非标准业务逻辑或与外部服务集成,这个工具就达到了极限,程序员不得不用低级语言拼凑“拐杖”,这使开发变成了可视化设计与肮脏 hack 的地狱鸡尾酒。而程序员非但没有消失,4GL 反而创造了“企业系统架构师”(例如 SAP ABAP),他们成了极其昂贵的专家,它也没有消除编程,只是把它从“通用语言”的领域移到了“昂贵且脾气暴躁的工具”的领域,让公司被锁定在特定供应商上。

每隔十到十五年,业界就会郑重宣布不再需要手写代码,并推销一种“用人类语言向机器解释任务”的新方式。而每一次,几年后都会发现程序员不是变少了而是变多了,只是现在他们被叫做别的名字。我敢说,我们现在正经历这一循环的又一次转折,这类人的新名字将是 vibe coder,而我认为这个工具注定会像之前的尝试一样。


“用人类语言与机器对话”的想法比计算机这个概念本身还要古老;二十世纪上半叶的科幻小说中充满了它,从弗里茨·朗的《大都会》——它马上就要满一百岁了——中人类的机械替身,到那些机器通过语音接受命令并乖乖执行的故事。但四十年代一出现真正的计算机器,就发现问题不在机器。

康拉德·楚泽(Konrad Zuse)在四十一年建造了 Z3,并提出了最早的高级语言之一(Plankalkül),他把这个问题表述成这样:“危险的不是计算机会变得像人,而是人会变得像计算机”。我认识的许多开发者相当认真地认为编程语言只是英语的劣化版本;我不打算和他们争论……它不是“英语的劣化版本”,它是一种具有根本不同特性的工具,不比自然语言差也不比它好,它只是关于别的东西。

编程语言实际上做了什么

“计算员工的季度奖金”这句话对会计来说意味着什么?在我职业生涯的早期,我待过一家小公司,它通过为工资单编写类似 1C 引擎的东西在这个世界上滋生熵,所以我稍微接触过这片沼泽。对于一个活生生的人来说,这句话就足以重建几十个默认值:到底谁拿到奖金,按哪个公式,对于季度中途加入的人怎么办,对于离职的人怎么办,是否四舍五入以及朝哪个方向,用什么货币,如果某个人完全没有数据怎么办。自然语言之所以有效,正是因为听者用上下文、常识和一般知识填补了这些漏洞,而他们拥有大量这些,因为他们就在那个领域工作。

一个程序,确切地说是一段程序、代码……做不到这一点。每一个这样的决定都必须在某处被钉死、支撑起来、作为过程详细说明,否则结果就是非确定性的,而非确定性的会计就不再是会计,而是与税务局谈话的理由。而且往往即使经验丰富的会计也无法一次性考虑到所有事情,所以一句简短的自然语言会展开成这样:

"calculate the quarterly bonuses"
    ▼
- which quarter exactly (calendar? fiscal? counted from the hire date?)
- who is included (staff? part-timers? those who quit mid-quarter?)
- the accrual base (base salary? salary + overtime? average for the period?)
- the formula (a percentage? a fixed sum? a progressive scale?)
- proration for a partial quarter (by days? by months? none at all?)
- rounding (half-up? down? to the ruble? to the kopeck?)
- currency and exchange rate (as of which date do we take the rate?)
- missing data (skip it? treat it as zero? fail with an error?)

一个问题变成另外八个问题,而且我当时还偷懒了,而每个问题又会衍生出更多问题,一旦一个问题不再衍生其他问题,它就可以用编程语言的术语来描述。

编程语言的存在不是尽管有这种乏味的精确性,而是为了这种精确性,因为它给你一种语法,在其中歧义在物理上无法表达,以及一种语义,在其中同一个程序总是意味着同一件事。一个把经理和会计的愿望“翻译”成代码的程序员,实际上并不是忙于把规格说明翻译成代码,而是承担起那八个或更多问题,并为答案承担责任。

就用大白话说……

行不通。为什么“用自然语言代替代码”会撞上天花板是有原因的,而这些原因关乎我们……人类,而不是关乎工具、编译器、标准库或现有编程语言的局限,其中最主要的是自然语言不可约简的歧义性。

如果有人让你“删除这个文件夹里所有超过 30 天的文件”,你会怎么做?最可能的是你会直接按修改日期选择文件并删除它们……这看起来像一个简单的动作,但你刚刚用你的经验回答了 90% 的子问题。而一旦你开始形式化那个基本问题,这些子问题就相当不少:

—— 超过是按创建日期还是按修改日期?
—— 我们要不要动子文件夹及其中的文件?
—— 我们要删除隐藏文件吗?
—— 还有符号链接:我们删除链接还是文件本身?
…… 等等

要消除歧义,你要么得问一大堆澄清问题,从而实际上用文字写出规格说明(也就是程序的代码),要么替提问的人做决定,结果可能做出并非他们想要的东西。编程语言用蛮力逻辑解决了随之而来的子问题,因为每个构造都恰好只有一个含义,mtime 永远不会突然变成 ctime。

但即便精确回答了每一个问题以及每一个相关问题,我们还是会撞上莱斯定理——一篇 1953 年相当枯燥的数学论文,对我们人类来说有一个毫无乐趣的实用推论,大致是这样的:在一般情况下,程序的任何非平凡语义属性都无法用算法检查。也就是说,你无法自动保证生成的代码恰好做了被要求的事,这意味着必须有人来检查产出。而要检查它,你就得读代码;要读代码,你就需要一种形式语言。循环闭合了。

间接地,还有一个性质浮现出来,不再关乎莱斯,而是关乎自然语言本身。我们想构建的复合体越复杂,它映射到词语上就越糟糕。用大白话说就是:一个小函数可以用文字描述,而且描述出来甚至还算像样、清晰,但一个百万行的系统无法用文字描述,这并不是因为词不够用,而是自然语言根本不能组合,意义可能重叠、从一句流到另一句,或依赖于相邻的句子,涵盖段落,有时还涵盖更大范围的文本。

接下来是一段难啃的内容,我会把它藏在剧透里,如果语言学不是你的菜,你可以直接滚到下一节,那里会通过开发者能理解的事物和类比把同样的想法再讲一遍。

但如果你好奇这种“不可化约为文字”究竟从何而来,语言学家早在任何 AI 出现之前很久就已经详细描述过了。他们试图形式化地写下并计算一句普通人类话语的含义,结果发现句子的含义无法仅靠看这个句子本身来计算。自然语言中的任何文本都无法原样搬进其他表示形式而不丢失上下文或各个部分的含义。搬运的过程需要做一些工作,把共享的上下文拆分成部分,并对任务的本质复杂度做出评估。

程序员在写任何代码之前就处于这种状态;它叫作……鼓点……分析瘫痪(有时也叫布鲁克斯的恍惚,即克服那种本质复杂度的过程)。‘直接用文字告诉机器’这个想法就卡在这个状态上,自上而下的‘这是一份文字规格说明,去写个程序……’也一样,所以值得看看四十多年来都挖出了些什么。

关于莱斯、坎普和 DRT

但自然语言还有另一种困扰,而这与 Rice 无关。句子本身可能被不合逻辑地构建,三重叠加,模糊不清,前后颠倒地编造,扭曲纠缠,从内向外切割,以各种方式编织,以至于那片词语的花园中不止一条路径,而路径就在短语中间分叉,你只有读到结尾,甚至从头再读一遍才会意识到。并不是说我曾是个伟大的语言学家,但在十年代末,我不得不扩展我们某个应用中的脏话过滤器,而在伟大而强大的俄语中,一个人可以用许多方式斥责别人,所以我也得稍微触及这个话题。但让我们回到我们的羊……也就是,我们的词语。

只要我们使用像“删除文件”这样的小短语,一切都没问题,短语完整地装进你的脑袋,不会拖着一串上下文尾巴。但试着用词语描述更复杂的东西,就会发现词语开始“互相挡道”或“互相依赖”,跨越句子边界,而正常编程语言中的变量应当只依赖于使用它的精确上下文。

经典短语“每个拥有驴的农夫都打它”对任何英语使用者来说都能理解,通常读起来毫无障碍。现在试着把它拆成部分,并像编译器处理表达式那样,分别保留每个部分的含义。

“谁拥有驴”现在仅仅断言存在一头属于某个农夫的驴,而“打它”若与其他一切分开,就完全无法提示被打的是谁,因为“它”出现时,“驴”已经一闪而过,形式上已超出作用域。

人类不会注意到这一点,因为他们把整个语块记在脑中,并即时重建那种联系,而一个像任何正常解析器那样从左到右逐片计算句子含义的函数,要么会崩溃,要么会复杂到谈论实际应用毫无意义。

对程序员来说,这就像在一个函数内部声明的变量,却在下一个函数中也可见,既没有显式传递,也不是全局变量,因为某处使用上下文被钩住了。没有语言允许这样,作用域的发明正是为了让这种事在设计上不可能发生,而自然语言恰恰靠这个存活:它的“驴”泄漏跨越句子边界,而这被视为正常行为。

这种上下文泄漏早在任何 AI 之前就被语言学家描述过:Hans Kamp 在八十年代初关于动态语义学的工作中,以及几乎同时,Irene Heim 在其关于文件变更语义学的学位论文中;两人独立地遇到了同一个事实:不拖上先前说过的东西,就无法计算句子的含义。

话语表征理论,DRT,就是这样诞生的,它对我们程序员来说唯一实用的结论听起来无聊且不适用,因为在其“自然栖息地”中,它在 DRT 之外运作。你无法形式化地保证一段文本可以孤立于相邻片段而被理解,这意味着你也不能要求你用来向机器解释任务的语言,在保持自然语言的同时像代码一样行事。

程序员的天性栖息地是代码,而代码没有这种毛病,调用 calculateAge(1985, 2026) 无论你把它塞进游戏循环还是塞进报税报表,它都意味着同一件事,它不会拖着一根看不见的尾巴,让前面的行改变它的含义。

而程序员的工作是用独立的部件组装出一个大系统,这只有在部件不会因为放置位置不同而改变含义时才可能实现,自然语言从根本上做不到这一点,一个小句子的状态会随着你把它塞进去的上下文规模而漂移。如果 sum(a + b) 的结果突然开始取决于调用函数的长度,几乎没有人会高兴。

希望我没有用这番论述让你太累?

如果你对 DRT 仍然好奇

首先,你可以看看《斯坦福哲学百科全书》的条目“话语表征理论”和“动态语义学”,它们是免费的,由专家校对,语言通俗易懂,不像 Kamp 的原著那样。

下一层是原始文献,论文《真理与语义表征理论》和 Irene Heim 的学位论文《有定与无定名词短语的语义学》。至少值得翻一翻,看看这不是什么秘传之学,而是在前 AI 时代,为构建那个“作用于上下文的函数”所做的尝试。

所有这些文献确实很难,对非专业人士来说就像一桶水里的一滩水,但请注意,仅仅为了形式化描述自然语言语义的心理表征(DRS)的一个层级,就花了多少功夫,而即便如此,也没有穷尽整个语言理论。

现代大型语言模型不是建立在 DRT 之上的,它们是统计变换器,而不是 Kamp 的理论,所以这不是 AI 的基础,而是一个故事,讲述为什么作用于上下文的函数会陷入如此复杂和数 TB 内存的境地,仅仅是为了让你向模型询问列宁的生日。也是为了理解为什么把自然语言变成精确规范语言的想法看起来很天真。这正是 vibe 编程者现在兜售的“用英语代替代码”之梦

这以前被尝试过。不止一次……

历史上发生过好几次围绕“让我们通过一种接近英语的语言来摆脱程序员”这一主题的重大尝试。有趣的是,每一次都产生了同样的副作用:老程序员留了下来,但新的专业分工诞生了,给新一代程序员带来了工作。

COBOL(1959)的创建目标是让管理者和业务分析师能够读写程序。因此语法被刻意做得像英语句子,ADD SALARY TO TOTAL GIVING NEW-TOTAL 读起来几乎是一个普通句子,嗯,在美洲那边是普通的。结果是诞生了 COBOL 程序员这一职业,它至今仍然存在,仍然运行着相当一部分银行和政府系统,正如我在文章开头所说。而管理者们,一如既往,从未开始用它写作,而是继续手写他们的报告,在 Word 里,以表格和图表的形式,然后分配任务,现在是分配给 COBOL 开发者。

接着迎来了 SQL 时代(1974 年),它最初叫 SEQUEL,即结构化英语查询语言,之所以起这个名字,正是因为它本应让非技术人员用近乎英语的方式向“程序”提问。后来出于广告和金钱的原因,它被改名为 SQL,但“用于查询的英语”这一初衷仍被缝进了名字里。如今,SQL 开发者已是一个独立的职业,而那位遇到一个带有三个子查询和一个窗口函数的 LEFT JOIN 的“非技术管理者”,通常会跑去找一位懂这门三层楼高、伟大而强大的构造的技术人员。

然后是 4GL 和 CASE(计算机辅助软件工程,八十年代),它们被视为第四代“自然编程语言”,并再次承诺“没有程序员的编程”。广告从每一块铁皮上大声宣扬,声势如此浩大,以至于在八十年代末,许多人真的担心出现“空办公室效应”——大多数应用无需编写代码即可创建,开发者将集体奔向“免费登记处”。但同样,到两千年时,这个行业不仅没有摆脱程序员,反而收获了一整片由 SAP、Oracle 和专有框架领域高薪专家组成的新星系。

事实证明,4GL 系统的“自然性”与之前的 COBOL 和 SQL 一样,是一种幻觉,只不过是在可视化模块的外表下,隐藏了可怕的配置复杂性和不可预测的系统行为。CASE 工具越是试图自动化编写代码,程序员就越是花时间与这些工具本身的局限性作斗争,从逻辑的创造者变成“系统集成的架构师”,最终,开发者非但没有消失,反而出现了短缺,而“没有程序员的编程”只是转移到了另一个层面:如今,你不必再用 C++ 或 Pascal 编写代码,而是必须学会理解某个特定厂商的“秘密知识”,把可视化构建器变成又一个 albeit 非常昂贵的编程工具。

确实昂贵,因为一名普通 SAP 开发者的薪水轻松超过 10 万美元,外加培训和年度考试,此外还有 X 千美元的培训费和强制性的年度重新认证,没有这些,证书就会直接过期,而这一切都绑定在厂商自己的付费订阅上。

接着轮到 UML 和模型驱动架构(九十年代和两千年代),其美好理念是绘制能生成代码的图表。在实践中,UML 作为文档工具和开发者的图表存活了下来,开发者最终还是手写代码,有时还根据已经写好的代码绘制图表,这多少暗示了些什么。后来 UML 里的钱少了很多,那些一直在推销模型驱动架构的人,在十年代初大批涌入各种类型和规模的 No-Code 和 Low-Code 初创公司和机构,这多少也暗示了些什么。

Bubble、Webflow、OutSystems 和 Mendix 开始向所有想要的人兜售这样一句话:“现在任何企业都能自己组装出一个应用”,而实际上它归结为标准任务、表单、CRUD 界面和自动化,还配有各自的培训师、学校、工具和支持。但同样,一旦你需要非标准逻辑、集成或性能,你就得在平台内部用积木堆出一堆拐杖,或者雇一个懂那个特定平台、能用 C++/Java/Swift/随你挑写“普通”代码的开发者。也就是说,出现了,惊喜……又多了一个专业方向。结果就是,那个兜售“你再也不需要开发者了”这一梦想的行业,为了赚点小钱,顺带卖起了“我是这个东西的开发者”的证书。然后轮到 LLM 了……

你在这里,而这一次与之前几次有本质区别,因为工具第一次开始处理任意自然语言,而不是由积木、图表和特殊词汇构成的有限 DSL。这是一次非常大的飞跃,堪比 COBOL/SQL/UML 以及其他那些,我说这话毫无讽刺之意。但歧义性、可验证性和可复现性这些根本限制并没有消失,因为它们关乎的不是工具,而是任务本身的性质。仍然得有人来陈述任务,精确地、小份地陈述,阅读生成的代码,并为那段代码凌晨三点在生产环境里会做什么负责。

如果你回头看这份清单,你会发现每一波浪潮确实都消灭了某一层手工劳动,但每一波浪潮也在自身之上创造出一层新的工作。

杰文斯悖论

1865 年,威廉·斯坦利·杰文斯注意到,蒸汽机效率越高、每台机器产生单位功所需的煤越少,总体上消耗的煤反而越多,因为如今更便宜的单位功开启了如此多的新应用,以至于所需的总工作量增长了。

每当一种工具让写代码变得更便宜,对软件的需求增长得就比生产成本下降得更快。正因如此,程序员的数量从五十年代初的几千人增长到九十年代的几十万人、两千年代的几百万人,再到今天的几千万人,而这还是在现代开发者拥有框架、IDE、包管理器和搜索……的情况下。

有时一天里误打误撞交付的量,就抵得上上个世纪的一位同事写上几天甚至几个月,因为普通程序员的生产力已经增长了好几个数量级,而他们的工作量也增长了,因为廉价的代码让那些以前没人会启动的项目变得值得一做。所以,如果你的直觉告诉你“工具替我干活了,因此活儿会变少”,杰文斯有些坏消息要告诉你,而且这消息已经有一百六十年了。

实际改变的是什么

然而,以上这些都不意味着什么都不变——它确实在变,而且变化相当大,只是不是朝着我们看到的方向。市场底部正在消失:样板代码、简单的 CRUD 系统、标记和样板生成,所有那些解决方案已经已知、只需打出来的工作。在这方面,神经网络确实很擅长。

但顶层正在增长:设计系统、理解领域、调试、性能优化(这是连万能的 Claude 都像斧头一样漂浮、糟糕地胡搞的领域之一,所以你几乎跟不上拒绝提交的节奏)、安全,以及将组件嵌入到必须可预测运行的系统里。总得有人去检查生成器的生成器用生成器生成了什么,对吧?而那个人必须比生成它的人更深入地理解这个系统,不,甚至不是代码……因为生成它的人会撞上莱斯定理和坎普的工作。

你可以说,“机器能做什么”的边界正在移动,而且很快,但“业务需要什么”的边界移动得更快。在这种环境下,程序员不会消失,但他们工作的构成发生了变化,现在用手打字更少了,思考更多了,而你仍然要为一切负责。

编程是不朽的吗

让编程语言得以存续的,不是语法、开发者的习惯或行业对变革的抵制,尽管抵制也很多。编程语言解决的是自然语言(无论你把它包装成什么工具)从根本上不适合的任务,无论是图表、md 文件或计划,还是与 Claude 的对话。编程语言明确地描述必须可预测运行的系统的行为。

只要还存在这样的系统——“大致正确”意味着损失利润、损失时间或遭受损害,而这几乎就是所有银行、所有医疗、所有航空,可能还有大多数普通企业——就需要一种形式化的方式来描述它们究竟必须做什么。这种形式化的方式就是编程语言,无论到 2050 年它看起来如何,是文本、语音,还是直接从你脑海中冒出的想法。

一台接受“我想要一百万美元”并神奇地交付的机器,仍然是上世纪开端的科幻情节,而非工程现实,因为总得有人告诉机器这笔钱从哪里来、以什么货币、转到哪个账户,以及如果来的不是钱而是一群穿短夹克的小伙子该怎么办。那个人就是程序员,除了小伙子们的情况,无论他是手动输入 transfer(amount, account)、与机器人聊天,还是用语音向助手口述。

我正引导你得出关于程序员职业一直以来的基础的结论。氛围编程并没有消除精确陈述任务的需要,它只是把你为不精确措辞付出代价的时刻推迟了。于是结果就是,问题再次没有被解决,也无法被解决,但一如既往,它可以被推给技术人员这一层级,只是现在他们会被叫做别的什么,比如高级 Opus 工程师,大约十年后,Habr 上会有人写一篇文章,讲述这个拥有自己薪资区间的新职业从何而来。同意吗?

← 所有文章

来源:Hacker News · dalerank.github.io