大家好我是Tony Bai。在这个由 Claude、GPT、Gemini等大模型定义的 2026 年我们似乎已经习惯了 AI 那种近乎“洁癖”的编码风格优雅的接口设计、滴水不漏的错误处理、以及永远对齐的工整格式。AI 正在用它那冰冷的、毫无感情的逻辑将软件工程推向一个前所未有的标准化时代。但就在前几天我在一个尘封的互联网角落里挖出了一本写于 1999 年的上古奇文——《How To Write Unmaintainable Code》如何编写不可维护的代码。这篇文章的作者 Roedy Green怀着一种极其黑色幽默的极客精神手把手教导当年的 Java 程序员们如何写出能让“接盘侠”当场崩溃、从而保证自己“终身就业”的屎山代码。当我用 AI 时代的眼光去重新审视这本 27 年前的“反向圣经”时我感到既荒谬又亲切。它就像一面镜子照出了在没有gofmt、没有 AI、没有 Claude Code 的“古法编程”时代我们曾如何野蛮生长以及 Go 语言在设计之初就已经用多么前瞻性的眼光封印了那些曾经肆虐一时的“魔鬼”。今天就让我们开启一场技术考古之旅用现代 Go 语言来“复刻”一下这些差点失传的“防御性”编程之术。底层哲学把你的同事想象成一个“管中窥豹”的傻子Roedy Green 在开篇就点明了核心维护者看代码就像通过一个卫生纸筒的中心在看世界视野极其狭窄。你的任务就是让他永远无法拼凑出完整的画面。古法复刻让同事“提刀来见”的 骚操作命名之罪用l冒充1用O冒充0利用字体的模糊性制造视觉混乱。// 古法复刻 var l int64 11 // 这是 11 还是 1l var speed int O1 // 这是 O1 还是 01创造极其相似的变量名仅通过大小写或一个不显眼的字母进行区分。// 古法复刻 var swimmer, swimner string // 99% 的 Code Review 都会看走眼 var hashTable, HashTable *map[string]int滥用缩写且不保持一致在不同的地方使用同一个单词的不同缩写让全局搜索彻底失效。// 古法复刻 func GetUserAuth(...) {} func GetUsrAuthorization(...) {} var athnClient *Client使用与业务逻辑无关的变量名比如在屏幕上显示“Postal Code”(邮政编码)但在代码里把它命名为zip。在函数名中使用极其抽象的词汇比如HandleIt,ProcessData,DoStuff。让调用者永远猜不透这个函数到底干了什么。注释之罪在注释里撒谎最简单的一招改了代码但不更新注释。写废话注释为每一行显而易见的代码配上同样显而易见的注释用大量的噪音淹没真正有价值的信息。// 古法复刻 i // i plus 1永远不要注释一个变量它的单位、取值范围、边界条件全让维护者自己去猜。结构之罪极限压行在一行里塞进尽可能多的逻辑挑战显示器的宽度极限。深度嵌套以能嵌套 10 层以上的if-else为荣坚决不使用early return或happy path。滥用全局变量永远不要使用局部变量把一切都提升为包级变量让并发的 Goroutine 们去为了争夺它而自相残杀。// 古法复刻 var tempResult string // 提升为包级变量 func HandleRequestA() { tempResult result_from_A // ... } func HandleRequestB() { tempResult result_from_B // ... } // 当这两个函数并发执行时一场血案即将发生。复制-粘贴-修改当有相似功能时坚决不抽象直接复制粘贴。在一个代码库里埋下 5 份只有细微差别的一模一样的代码等待日后引爆。一个函数只做一件事不一个函数必须干三件事让一个名为IsValid()的函数在校验的同时偷偷地把数据写入数据库。Go 语言的反击当然原文中Roedy Green的“骚操作”不止这些。但其中的一些“防御”手段对今天的Go语言来说并不生效。你会发现 Go 语言在设计之初就已经对这些“手段”进行了“免疫”比如关于缩进与格式Roedy Green 痛斥当年程序员通过“故意错位”的缩进来制造if-else匹配的视觉陷阱。Go 的反击对不起我们有gofmt。在 Go 的世界里关于代码格式的“圣战”在第一天就结束了。无论你的代码写得多乱CtrlS的瞬间一切都会变得整齐划一。关于花括号原文建议省略非必须的{}。Go 的反击Go 语言强制要求if,for后面必须跟{}从语法层面彻底消灭了这种的“防御”写法。现代化的“魔鬼”用 Go 复刻那些更高级的骚操作当然Go 也不是万能的。很多源自 Java/C 时代的“高级骚操作”在 Go 里依然可以“继续存在”。伪装成构造函数// 古法复刻这个函数名和类型名完全一样但它不是构造函数 type User struct{ name string } func User(name string) { /* ... do something evil */ }滥用interface{}把所有的函数参数都定义成interface{}然后在函数内部进行大量的类型断言Type Assertion。这能完美地把编译时错误转化为运行时panic。颠倒参数顺序定义一个DrawRectangle(height, width int)函数。在几个版本之后神不知鬼不觉地把它改成DrawRectangle(width, height int)但函数名保持不变。魔数Magic Numbers在代码里硬编码大量的数字100但就是不定义一个常量。更高级的玩法是偶尔用99代替100-1用50*2代替100。// 古法复刻 if len(users) 99 { // 这里是 99 // ... } for i : 0; i 100; i { // 这里是 100 // ... }迷惑性的函数重载Go 版本Go 没有函数重载但我们可以用“接口”来模拟。// 古法复刻 func Process(input interface{}) { switch v : input.(type) { case string: // 处理字符串 case int: // 处理整数但逻辑和 string 完全不同 // ... } }当你的同事传入一个他以为是数字的字符串123时他将收获一个意想不到的结果。小结在 AI 时代我们为什么要回顾“屎山”重温这本 27 年前的“反向圣经”在今天这个 AI 编程时代显得格外有意义。AI 的出现正在把“编写可维护代码”的门槛拉到前所未有的低点。一个初级程序员在 AI 的辅助下也能写出格式工整、变量命名规范的代码。但这是否意味着“屎山”将成为历史恰恰相反。AI 在解放我们生产力的同时也正在“批量化”和“隐蔽化”地制造着“新时代的屎山”。AI 可能会生成一段逻辑上看似完美但在高并发下会引发严重数据竞争的代码它也可能会为了实现一个简单功能引入一个庞大且带有安全漏洞的第三方库。这本古老的指南提醒我们技术的进步可以消灭“语法层面”的丑陋但永远无法替代人类工程师在“架构层面”的审美与抉择。在 AI 时代我们不再需要像 Roedy Green 那样靠着“加密代码”来保住饭碗。但我们比以往任何时候都更需要理解那些“不可维护代码”背后的设计缺陷从而在 Code Review 中扮演好 AI 的“最终质检员”角色。代码的整洁与混乱终究是一场关于“责任心”的博弈。无论时代如何变迁这或许是软件工程永恒的真理。当然如果你真的想了解古法编程时代的更多“骚操作”可以看看Roedy Green的原文https://www.doc.ic.ac.uk/%7Esusan/475/unmain.html 今日互动探讨在你见过的 Go 项目中遇到过哪些让你拍案叫绝、或者让你想“提刀来见”的“屎山代码”骚操作欢迎在评论区分享你的“开眼”经历如果本文对你有所帮助请帮忙点赞、推荐和转发点击下面标题干货- 当AI 榨干了编程所有的乐趣我不再是程序员而是“Claude Code”的项目经理- 别再用 AI 疯狂撸代码了我们正在把自己逼入“死胡同”- 停止“氛围编程”Vibe Coding拥抱新一代软件工程- 拒绝无效告警用 Govulncheck 构建高信噪比的 Go 安全扫描工作流- 告别古法编程黄金时代AI 时代不会再有新编程语言诞生的土壤- 告别单打独斗Claude Code 全新“Agent Team”模式当 AI 开始组队干活- REST 已老AI 时代的智能体需要怎样的 API 还在为“复制粘贴喂AI”而烦恼我的新极客时间专栏《AI原生开发工作流实战》将带你告别低效重塑开发范式驾驭AI Agent(Claude Code)实现工作流自动化从“AI使用者”进化为规范驱动开发的“工作流指挥家”扫描下方二维码开启你的AI原生开发之旅。