
用 LLM 写代码,选"无聊"的语言更靠谱
LLM 代码生成中,选择简单一致的语言比花哨的语言更可靠——Go、Rails 胜出的背后原因
原文来源:Use boring languages with LLMs — LLM 代码生成中,选择简单一致的语言比花哨的语言更可靠
我最近一直在琢磨一个想法——一致性是会复利的。
作为顾问,过去两年我在多个不同项目上工作,这个感受特别强烈。大语言模型会放大技术栈中的不一致性,同时默默强化那些一致性好的技术。生态越碎片化的语言和框架,用 AI 写出来的代码质量就越差;而那些有强约定、少选择的工具,产出却出奇地好。我觉得这个效应会越来越显著,甚至会决定在大模型时代哪些技术能存活下来。
就算代码再便宜,每次跑推理本质上都是在赌博。你永远不知道模型下一秒会抽到什么——它可能在某个包管理器之间反复横跳,或者突然丢出一段 2019 年的过时写法。既然我们在拿 token 赌博,那不如把赌注压在那些嵌入向量最一致、模型权重被强化得最稳定的语言上。对于软件开发来说,这其实是理想状态,因为绝大多数程序干的事都很基础:处理信息、读写文件、响应网络请求,仅此而已。
在 AI 出现之前,程序员们就已经在抱怨那些隔三差五就自我革新的语言了。这些抱怨是真实的,但多半停留在审美层面,反映的是开发者对无意义生态变更的厌倦。
回头看一下 2024 年 State of JS 调查,JavaScript 的生态就是一盘散沙。对人来说,碎片化很烦人。但对于一个用整个公开语料训练出来的模型来说,碎片化已经不只是烦人的问题——它成了一个需要在强化学习或者 agent 框架层面去专门解决的问题。(Anthropic 泄露的 Claude Code 就暴露了他们在代码里硬编码了一些对 JavaScript 框架的偏好——可见问题的严重性。)
Python 的情况大同小异,只是换了一种调调。问一个简单的问题——"你到底在用哪个包管理器?"——就会炸出一堆语言版本、包管理器版本、操作系统兼容性交织的矩阵。作为技术负责人,我觉得这简直让人头疼到麻木。
到底该用 pip、poetry 还是 uv?你的工具链选择重要吗?需不需要交叉编译?怎么知道某个 Python 包悄无声息地依赖了一个 C 库?你到底用不用 async,还是扭头去搞个任务队列?Django 还是 FastAPI?
从模型的角度来看,写这些东西的方式太多了,而训练语料里每一种写法出现的频率都差不多——除非你在训练时引入了时间偏置。这意味着什么,我觉得很直白:语料中方差小的语言和生态,在编码 agent 中表现得更准确、执行得更可靠。 在高维向量空间里,训练数据的余弦相似度就是模型注意力和 MLP 层学习预测下一个 token 的基础。一致的语料产生一致的推理 token。
这个规律在其他语言中也成立。用 Rails 写后端,AI 产出的代码比用那些花里胡哨的 JavaScript 框架要稳定得多——不是因为 Ruby 本身在柏拉图意义上就是更好的语言,而是因为Rails 只有一个。而生产级别的 JavaScript 框架少说有十几个,它们用不同的 Venn 图去划分同一组默认功能。约定优于配置在人类程序员时代就已经被证明是成功的,因为灵活性被高估了,约束反而是解放。二十年后的今天,同样的道理在机器身上更加成立。模型只是在求解"哪个结果最有可能"而已……
Go:几乎是无意中做对了
Go 是最能体现这个原则的语言,而且几乎是歪打正着。多年来,Go 团队一直抵制程序员们嚷嚷着要的便利性和高阶表达(尤其是泛型),这在开发者中非常不受欢迎。我以前也是那群开发者之一——我写过几十万行 Go 代码,作为一个程序员,我经常被这门语言气得够呛。
我认识 Go 团队的一些人,我很佩服他们对程序未来的强势承诺。我现在越来越觉得,Google 几乎是阴差阳错地制造出了当前这个时代最适合的语言。开箱即用,Go 给 coding agent 提供了一系列其他主流语言无法匹敌的优势。
并发模型:goroutines
这是第一个优势。对于编码 agent 来说,goroutine 是远比线程、回调、async/await 或者那些着色函数范式更易处理的原语。它简单、类型安全,而且模型训练语料中到处都是它的影子。你根本不需要纠结"你的函数是什么颜色的",因为这个问题压根就不存在。
results := make(chan string, len(urls))
for _, u := range urls {
go func(u string) {
resp, err := http.Get(u)
if err != nil {
results <- err.Error()
return
}
defer resp.Body.Close()
results <- resp.Status
}(u)
}
for range urls {
fmt.Println(<-results)
}你看这段代码——它不会让你纠结"这里到底该用 async 还是 await"、"这个闭包是不是异步的"、"要不要 spawn 一个新线程"。goroutine 就是一个 go 关键字,没了。
标准库:net/http 撑起半个互联网
第二个优势是标准库。光是一个 net/http 就支撑了互联网上可观比例的微服务,而 Go 的密码学包(由 Google 资助和维护)是世界级的。我在 Zoom 和 Keybase 做生产系统时,全靠这些默认库,从来没有需要跳出标准库去找第三方替代品。
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "ok")
})
http.ListenAndServe(":8080", nil)
}三行注册一个健康检查端点,不需要框架,不需要路由库,不需要中间件。对于 AI 来说,这意味着它只需要生成标准库的调用,而不是去猜测"用户到底用的是 Gin 还是 Echo 还是 Chi"。
工具链:唯一正确的做法
第三个优势是工具链,这可能是最被低估的一点。Go 在设计上对大多数事情只提供一种正确做法:gofmt、go vet、go fix 强制单一规范风格,没有任何协商余地。对于语言模型来说,最好的组合就是一个一致的训练语料,加上"唯一正确做法"的工具来测试和反馈运行时结果。gopls 对 agent 来说是一个极好的护栏——它提供实时的语义反馈;golangci-lint 让你能静态地强制执行编码风格和原语,而不必靠提示词去说服 agent 遵守规范。
$ go vet ./...
./user.go:22:2: result of fmt.Errorf call not used
./user.go:38:9: declaration of "err" shadows declaration at line 34
$ golangci-lint run
user.go:51:6: exported func LoadUser should have comment (revive)
user.go:63:3: if block ends with return, drop this else (golint)想想 JavaScript 那边的情况——ESLint、Prettier、Biome、JSHint、JSLint……每个项目配的规则集都不一样。Agent 生成的代码可能在 A 项目上完美通过,在 B 项目上却炸出一百个 lint 错误。但在 Go 的世界里,gofmt 就是 gofmt,全世界只有一种格式。
性能与 GC:内存安全,不需要 agent 操心
第四个优势是带垃圾回收的性能。语言模型在管理内存上是不一致的——这是一个有充分记录的限制,短期内不会消失。Rust 在类型和借用检查层面强制内存安全,这对人类来说很好,但对 agent 来说是一场没完没了的战斗。用 coding agent 写 C 或 C++ 就更难了,因为训练数据里满是内存 bug、use-after-free 错误和几十年积累下来的血泪教训——模型既能避坑,也能挖坑。Go 给了你接近原生的性能,同时不需要 agent 直接管理内存。
func parseLines(r io.Reader) []string {
var out []string
s := bufio.NewScanner(r)
for s.Scan() {
out = append(out, s.Text())
}
return out
}这段代码简单到不可能出错——没有 unsafe,没有手动 free,没有生命周期标注。Agent 生成这种代码的成功率远高于同等级别的 Rust 或 C 代码。
有限的踩坑空间
第五个优势是一小套已知的"坑"。nil 指针在生产堆栈跟踪中对人类工程师来说可能很难排查,但有了合适的工具,agent 出奇地擅长处理这个问题。在惯用的 Go 里,可能出问题的范围是有限的;而在 Python 里,各种元类、动态导入、猴子补丁组合起来的坑——那是无限的。
data, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("read config %q: %w", path, err)
}处理错误的模式就这么一种。没有 try/except,没有 Result 类型,没有 Either monad。Agent 不需要猜测你喜欢的错误处理风格是什么。
所以,结论是什么?
读到这里,正确的反应不应该是"Go 是最好的语言",而应该是更具体的东西:这门语言和它的工具链可以写出一个从业工程师需要的大部分非视觉软件。 考虑用 Go 配合 agent 去构建下一个 CLI、后端服务、agent 编排器。
如果你想找一个能帮助团队稳定交付的技术负责人,欢迎联系我。
本文基于 Jacob Young(Sancho Studio 创始人)于 2026 年 4 月发表的原文 Use boring languages with LLMs 进行中文转述翻译。有删改和本地化处理。
© 2026 四月 · CC BY-NC-SA 4.0
原文链接:https://aprilzz.com/ramble/use-boring-languages-with-llms