当AI开始写代码:类型系统是最后的防线还是过时的枷锁?


当AI遇上代码:为什么“有类型”的语言反而更适合“凭感觉编程/ 氛围编程/vibecoding”?

谁能想到,有一天我会说:“TypeScript、Rust、Go,这些看起来冷冰冰、啰里八嗦、动不动就让你写一堆类型声明的语言,居然比Python更适合‘凭感觉编程’?”

这听起来就像是在说:“我爱上吃苦瓜,因为它比冰淇淋更解暑。”但别急,听我慢慢讲,讲一个关于AI、类型系统、还有程序员“第六感”的故事。



第一章:我的Python情结,像初恋一样热烈又盲目

我第一次写代码是在大学宿舍,那台二手联想笔记本风扇嗡嗡作响,屏幕上是一段歪歪扭扭的Python代码。print("Hello, World!")——那一刻,我觉得自己像个魔法师。Python,它不讲道理地简单,不讲道理地自由。你不需要告诉它变量是什么类型,它自己“猜”;你不需要编译,改一行代码,回车就跑;你甚至可以一边写代码一边吃泡面,吃完还能顺手给函数起个名叫do_the_thing()

那时候,我靠“感觉”写代码。什么叫“vibecoding”?就是你脑子里有个模糊的画面,你知道这个功能大概要干嘛,但你懒得画流程图,懒得写文档,懒得想边界条件。你打开编辑器,敲几行代码,运行,报错,修一下,再运行,再报错,再修……循环往复,像在即兴爵士乐中即兴发挥。Python就是那个最懂你的萨克斯手,随你怎么吹,它都能接得住。

我用Python写了爬虫、写了后端API、写了数据分析脚本,甚至用它给女朋友写了个自动发“早安”的微信机器人(后来她嫌烦,把我删了)。Python成了我的“默认语言”,就像人饿了会本能地伸手拿面包一样,我一有新项目,第一反应就是:“来,建个main.py。”

但问题也悄悄埋下了。有一次,我重构一个5000行的Python服务,改完之后部署上线,结果半夜三点被报警电话吵醒——某个关键函数接收了一个None,没人检查,一路传下去,最后数据库炸了。我一边修bug一边想:“这代码,怎么看起来像我十年前写的?怎么还是这么‘脆’?”

Python的自由,是用“不确定性”换来的。你写的每一行,都像在走钢丝,没有护栏,全靠直觉和经验。而直觉,会疲劳;经验,会出错。



第二章:AI来了,但它不是来救Python的

2024年底,Claude Code正式发布。我第一时间试用,结果震惊了。它不仅能写代码,还能理解上下文,能根据注释生成函数,甚至能帮我重构整个模块。我激动得差点把键盘摔了——这不就是我一直梦寐以求的“编程外挂”吗?

我第一时间拿它去改造我那个“脆弱”的Python项目。结果……翻车了。

AI生成的代码,逻辑上没问题,但类型上全是坑。它把一个预期为List[User]的地方,传了个User对象进去;它在异步函数里忘了加await;它甚至在一个应该返回dict的地方返回了None。Python不报错,直到运行时才崩溃。而AI,根本不知道这些错误会发生,因为它“看不见”类型。

我突然意识到:LLM是“漏的抽象”。它理解语义,但不理解类型系统。它知道“用户列表”应该是一个列表,但它不知道你的代码里user_list到底是不是list,还是None,还是str。在Python这种动态类型语言里,AI就像一个盲人画家——他能画出美丽的轮廓,但颜色全涂错了。

而更可怕的是,我发现自己越来越依赖AI。我不再一行行写代码,而是说:“AI,帮我写个用户认证模块。”然后它写完,我跑一下,出错了,我再让它改。结果改来改去,代码越来越乱,像一团打结的毛线。我开始怀疑:是我变懒了,还是AI不适合Python?



第三章:当AI遇上类型系统,魔法才真正开始

就在我几乎要放弃的时候,我决定试试TypeScript。我们公司TextCortex的前端是TS写的,我一直不太熟,但项目紧急,只能硬着头皮上。我让Claude Code帮我重构一个复杂的表单验证逻辑。

我输入需求:“这个表单有5个字段,其中3个是必填,2个有条件验证,提交时要调用API,失败要显示错误。”AI开始写代码。几秒钟后,它生成了一堆TS代码,包括接口定义、类型别名、异步函数……

我正准备运行,突然发现——它自动运行了tsc(TypeScript编译器),并且在提交前检查了所有类型错误。更离谱的是,它自己修复了一个我都没注意到的类型不匹配:我把一个string | undefined当成了string用了。

那一刻,我愣住了。AI不是在“猜”类型,而是在“遵守”类型。它生成的每一行代码,都必须通过编译器的审查。就像一个建筑师,不再是凭感觉搭积木,而是必须按照蓝图施工,每一块砖都得符合规格。

我继续让它重构整个前端模块。3000行代码,5个小时,我几乎没动手指。AI生成、编译、测试、提交。最后,git diff显示5000多行变更,但CI(持续集成)绿了,测试全过,线上零报错。

我坐在那里,手有点抖。这在过去是不可想象的。以前我改500行代码都要提心吊胆,生怕哪里漏了。而现在,AI+TypeScript的组合,像一辆自动驾驶的重型卡车,不仅跑得快,还自带防撞系统。



第四章:Rust?那个“折磨人”的语言,居然成了我的新宠?

你可能觉得TypeScript还算“温和”,毕竟它只是JavaScript加了个类型层。但接下来我要说的,可能会让你更震惊:我现在用Rust写后端,而且……我很享受。

Rust,那个以“借用检查器”和“生命周期”闻名的“程序员噩梦”,曾经让我敬而远之。每次看到&strString的区别,我都想关电脑。但自从Claude Code支持Rust后,一切都变了。

AI帮我写Rust代码时,它不会“忽略”所有权问题。它知道什么时候该clone(),什么时候该传引用,什么时候该用Arc>。它生成的代码,几乎总能通过cargo check。而一旦有错误,它会立刻修复,而不是等到运行时才崩溃。

有一次,我让AI实现一个并发任务调度器。它生成了200行Rust代码,用了tokiochannelFuture……我本以为会一堆SendSync错误,结果——编译通过,运行正常,性能还比Python版本快了8倍。

我突然明白:类型系统和编译器,不是在限制AI,而是在“教育”AI。它们提供了明确的规则,让AI的“创造力”有了边界。就像给一个天才画家一把尺子和量角器,他不仅能画得更快,还能画得更准。

而在Python里,AI没有规则可循。它自由,但混乱;它灵活,但危险。



第五章:Go的烟火气,像街边小馆的热汤面

如果说Rust是精密的瑞士手表,TypeScript是现代都市的摩天大楼,那Go就是街边那家开了二十年的小面馆。简单、直接、热气腾腾。

我用Go写微服务,也是靠AI。Go的类型系统没Rust那么复杂,但足够严格。error必须处理,interface{}不能乱用,goroutine虽然轻量但得小心死锁。AI在Go里写代码,就像一个老练的厨师,知道什么时候该放盐,什么时候该关火。

最让我感动的是,AI生成的Go代码,可读性极高。它不会写出那种“炫技”但难维护的代码,而是遵循Go的“少即是多”哲学。函数短,变量名清晰,注释恰到好处。每次我看AI生成的Go代码,都像在读一篇干净的技术文档。

有一次,我让AI把一个Python脚本重写成Go。原脚本用了大量try-exceptif not None,逻辑混乱。AI重写后,代码行数少了40%,性能提升了6倍,而且几乎没有运行时错误。我把它部署到生产环境,三个月没出过一次严重故障。

那一刻,我突然觉得:编程,不应该是“修修补补”的艺术,而应该是“一次建好”的工程。而AI+静态类型语言,正在让这个理想成为现实。



第六章:Python的黄昏?不,是转型

我知道,说“Python要衰落”会得罪很多人。毕竟,它依然是数据科学、机器学习、脚本自动化的王者。我依然用它写爬虫、画图表、做原型。但我想说的是:在AI时代,Python作为“生产级语言”的优势,正在被侵蚀

它的动态类型,在AI面前成了弱点。AI无法“感知”运行时错误,而Python又不提供编译时检查。结果就是:AI生成的代码,看起来没问题,跑起来全是坑。

而TypeScript、Rust、Go这些语言,天生适合AI协作。它们的类型系统,像一张地图,让AI知道哪里是悬崖,哪里是平地。AI不再是“盲人摸象”,而是“导航驾驶”。

我预测,未来五年,新创公司会越来越少地用Python写后端服务。他们会用Go写API,用Rust写高性能模块,用TypeScript写全栈。Python不会消失,但它会退居二线,成为“胶水语言”或“研究工具”,而不是“核心系统语言”。

这就像汽车发明后,马车没有消失,但它不再是主流交通工具。



尾声:编程的未来,是“人机共舞”

别再说“静态类型语言太麻烦”了。

在AI时代,麻烦的不是类型,而是没有类型的混乱

当你有了AI这个“超级助手”,类型系统不再是负担,而是安全带、是导航仪、是让你敢于全速前进的底气。

下次你再想“凭感觉编程”,不妨试试TypeScript或Rust。也许你会发现,真正的“vibe”,不是在混乱中摸索,而是在规则中自由起舞。

毕竟,最好的即兴演出,往往来自最扎实的排练。



极客辣评

根据我的经验,无论类型系统如何,强烈约束的框架更适合氛围编码。我们不能只说“类型语言更适合 vibe coding”,而应该说:“有强约定的、类型安全的框架,才是 AI 时代的 vibe coding 终极武器。”

比如,如果你使用 Rails Vibe 进行编程,那就太棒了,因为它有 MCP,有已发布的提示,而且在 Rails 中基本上只有一种方法可以完成所有操作。你知道文件应该如何命名、存放在哪里、应该采用什么格式等等。

在 Go 中尝试同样的事情,尽管 Go 具有更强的类型控制能力,但最终结果却截然不同。Claude 和 Gemini 都能轻松 one-shot Rails 应用,但在 Go 上翻车。

看看现在的趋势:

  • Next.js + TypeScript:文件即路由 + 类型检查 + AI 友好结构 → vibe 拉满
  • Laravel + PHP:虽然 PHP 被嘲,但 Laravel 的约定让 AI 能快速生成 CRUD
  • SvelteKit / Nuxt:类似 Next.js,结构清晰,AI 可预测
  • Even Rails 7 + Hotwire:老树新花,AI 生成 Turbo Stream 组件都行
而纯 Go、纯 Python Flask、纯 Express?AI 可以生成“能跑”的代码,但生成不了“可维护”的代码。因为“可维护”依赖结构,而结构依赖约定。

那是不是 Go 就没救了?当然不是。解决办法很简单:给 Go 加个强 opinion 的框架。
已经有苗头了:

  • Buffalo(虽然后来没火起来):试图复制 Rails 的体验
  • Go Kit / Go-Kit:微服务框架,有结构,但太重
  • Fiber:基于 Fastify 思路,API 设计清晰
  • Kratos(Bilibili 开源):有分层、有生成器、有约定
但还不够。Go 社区需要一个 “官方推荐的全栈框架”,像 Rails 之于 Ruby,Django 之于 Python,Next.js 之于 React。

这不是 AI 的问题,也不是 Go 的问题,而是生态成熟度的问题。
vibecoding 的本质,是“在已知路径上高速行驶”。类型系统提供了“防撞系统”(编译时检查),而强 opinion 框架提供了“导航系统”(结构约定)。两者结合,AI 才能既快又稳。

所以,别再只盯着“类型”了。下次你选技术栈,问自己:“这个框架,能让 AI 闭眼生成代码吗?”

如果答案是 yes,那你找到了真正的 vibe machine。如果 no,那你可能还在手动铺铁轨——累,而且 AI 还帮不上忙。

毕竟,最好的编程,不是写代码,而是让 AI 写代码,而你只负责说:“对,就这样。”



这种说法需要用评估来佐证。我也可以反驳,LLM 最擅长 Python 编程,因为他们的训练集中 Python 的数量比 C++ 或 Rust 多两个数量级。

无论如何,您可以通过添加一条规则轻松获得类型语言的大部分好处,该规则要求 LLM 始终输出带有类型注释的 Python 代码并通过运行 ruff 和 ty 验证其输出。



LLM 并不“懂”类型系统,它只是“见过”类型模式。

它之所以能在 TypeScript 中写出 interface User { name: string; age: number },不是因为它理解“结构化类型”或“鸭子类型”,而是因为它在训练数据里看过几百万次这种写法。

这就像一个外国人背熟了《菜谱大全》,能流利说出“先爆香葱姜蒜,再下五花肉煸炒”,但他不一定知道“煸炒”是为了逼出油脂、减少腥味。他只是在复现模式。

所以,当类型简单、常见、有模板可循时,LLM 表现极佳。但一旦进入复杂类型场景——比如:

  • TypeScript 中的 conditional types(T extends U ? X : Y)
  • 分布式约束下的泛型推导
  • Rust 中的生命周期 'a 与 trait bounds 的交互

LLM 就懵了。它不是“不会”,而是没有逻辑推理能力。它不能像编译器那样做类型推导树,只能靠“猜”和“试”。

于是,你看到了:

  • any 满天飞
  • as any 强转
  • // @ts-ignore 遍地开花
  • todo!() 占位符成堆
这不是“AI 菜”,而是AI 在“求生”。它知道类型检查会报错,但它解决不了,只好留个坑,等人类来填。

LLM 可能更适合Python等动态语言:
在 Python、JavaScript(无 TS)、Ruby 这些语言里,LLM 的“认知负担”更小:

  • 不需要维护类型上下文
  • 不需要跟踪变量是否可空
  • 不需要判断 Vec, E>> 怎么处理
  • 更少的语法噪声,更多语义自由
它只需要回答一个问题:“用户想让程序做什么?”
而在静态类型语言中,它必须同时回答五个问题:
  1. 这个函数要实现什么功能?(语义)
  2. 输入输出是什么类型?(类型)
  3. 是否要处理错误?返回 Result 还是 panic?(错误处理)
  4. 生命周期是否满足?(Rust 特有)
  5. 是否符合项目约定?(架构)

这就像让一个擅长写短篇小说的作家,突然去写一本结构严谨的学术专著——他能写,但写得慢,还容易出错。
所以,从“生成效率”角度看,动态类型语言确实更适合 LLM 快速产出可用代码。这也是为什么很多 AI 生成的脚本、工具、爬虫,第一语言往往是 Python。

TypeScript 的类型系统是“渐进式”的,意思是你可以从 any 开始,逐步加类型。但这也意味着:类型系统本身就在鼓励“先跳过,后补票”。
LLM 也学会了这一招。它知道:

  • 加 any 能让代码“通过”类型检查
  • 人类后续会手动修复
  • 比起卡在类型错误里循环 retry,不如先生成“能跑”的代码
这本质上是一种人机协作策略:“AI 负责功能逻辑,人类负责类型安全。”

类似地,todo!() 在 Rust 中泛滥,不是因为 AI 不行,而是因为:

  • Rust 的编译器太严格
  • 错误信息虽然好,但修复路径不明确
  • AI 很难“推理”出正确的 lifetime 或 trait 实现

所以它选择“投降”:“我知道这里要写代码,但我现在写不出来,你来。” 这就像导航软件说:“前方路况复杂,建议您停车问路。”todo!() 就是 AI 的“建议停车”。

现实是:

  • GitHub 上的 Python 代码数量远超 Rust
  • JavaScript(含 any)的代码量是 TypeScript 的数倍
  • 即使是 TypeScript 项目,也有大量 // @ts-ignore 和 any
LLM 的训练数据,本质上是“历史代码的统计平均”。它学到的,不是“最佳实践”,而是“最常见实践”。而“最常见实践”是什么?能跑就行,类型 later。

所以,当 LLM 面对一个类型严格的 Rust 项目时,它其实在“逆数据分布”工作。就像一个只会说方言的人,突然被要求用标准普通话做学术报告——他能说,但磕巴。

而当你让它写 Python 脚本,它如鱼得水,因为满世界都是 list.append()、dict.get()、try-except 的例子。



我注意到一个相当相似的模式。我特别喜欢用 Golang 进行 Vibecoding。Go 语言极其冗长,这几乎就像 Perl 的反面——编写 Go 语言体验糟糕,但阅读 Go 语言却令人愉悦。Go 语言的冗长性使得你总是能够快速理解上下文,通常只需一个文件即可。

在使用大模型LLM之前,编写 Go 语言代码需要预先投入大量成本,因此成本/收益的权衡往往不值得。
而使用大模型LLM之后,编写冗长代码的成本不仅会降低,会迫使 LLM 严格控制编写内容,确保代码按计划进行。

因此,成本/收益的权衡大大有利于 Go 语言。



我已经用 Rust 进行了数周的氛围编码,效果非常好。
我已经在 Haskell 中进行了几天的氛围编码,但我不喜欢结果。
也许我只是习惯了冗长的 Rust,而 Haskell 具有 LLM 未探索的巨大优雅潜力。
无论如何,类型以非常可靠的方式指导 LLM 的论点在两种情况下都成立。


类型化,但也许像Swift这样的语言除外,Claude揭示了这门语言的复杂性和模糊性。缺乏文档和过于复杂的开题报告似乎也让LLM课程负担过重,令学生感到困惑。

我一直在思考这个问题。我最初的假设是,LLM 最终会导致类型语言的消亡,因为类型系统的存在是为了帮助程序员避免犯明显的错误,而近乎完美的 LLM 几乎永远不会犯明显的错误。所以,在一个近乎完美的 LLM 世界里,类型系统只是在增加毫无意义的开销。

不过,在当今这个大模型(LLM)相当不完善的世界里,我同意楼主的观点。我还想知道,即使LLM有所改进,我们是否能够将类型系统不仅仅用于其最初的用途,而更多地作为一种方式来验证生成的代码是否真的按照我们的预期运行,类似于形式化验证。



我的经验是,LLM 通常在更常见的语言上表现更好(这并不奇怪:这些语言的数据更多!)。所以,我虽然有点业余,但编程经验在 Python 和相当普通的 Web 开发环境中表现最佳。当我用 LLM 来拟合 R 中的高级统计模型时,它们表现得相当糟糕。然而,它们却可以轻而易举地完成 PyTorch 或 SciKitLearn 的任务。


所有现有的编程语言都是为人类设计的。现在是时候设计一种专门用于氛围编码的语言了吗?