我记得在某个技术论坛的讨论串里看到过这样一条高赞回复:“我不是反对 AI 编程,我反对的是那种‘让 AI 写、然后我什么都不懂’的编程方式。”
这句话几乎可以概括当前技术圈一个非常有意思的分裂现象:一方面,以 GitHub Copilot、ChatGPT、Claude 为代表的 AI 编程工具正在成为主流开发者的日常选择,各种“AI 编程效率提升 50%”的分享铺天盖地;另一方面,在海外一些以兴趣驱动、强调亲手构建的编程社区里,“我不用 Copilot”“我关闭了 AI 补全”反而成了一种态度表达。更耐人寻味的是,这种反对并不是出于对新技术的不了解,而是来自一群技术能力相当扎实的爱好者。
这篇文章不想简单站队“支持 LLM”或“反对 LLM”。我想做的是拆解这个现象背后的深层逻辑:为什么业余编程社区对 LLM 使用如此警惕?这种反对到底是在反对什么?对于正在使用或准备使用 LLM 的开发者来说,理解这种反对声音,反而能帮你找到更合理的使用边界。
读完这篇文章,你会得到一个比较清晰的判断框架:LLM 在什么样的项目里是放大器,在什么样的项目里会变成“理解力的替代品”;以及,作为开发者,如何在不失去代码掌控权的前提下,把 LLM 用到实处。
1. 一个让很多人意外的现象:兴趣型社区正在“用脚投票”
先说一个容易被忽略的事实:海外围绕“hobby programming”(兴趣编程)形成了大量社区,比如 Hacker News、Reddit 的 r/programming、r/selfhosted,以及各种独立开发者论坛。这些社区的参与者并不排斥技术,恰恰相反,他们对新技术非常敏感。但正是在这些社区里,“抵制 LLM 写代码”的帖子每隔一段时间就会引发大量讨论。
你可能会想:这些人是不是在用“不用 AI”来标榜自己?从我观察到的讨论看,大多数反对者给出的理由其实非常具体,也相当工程化。高频出现的理由包括:
- LLM 生成的代码看起来合理,但一旦出现问题,排查成本非常高。
- LLM 倾向于给出“通用解”,而不是针对某个项目上下文的定制方案。
- 长期依赖 LLM 之后,自己的代码阅读能力和设计能力会退化。
- 兴趣项目的核心价值是“把它弄明白”,而不是“把它跑起来”。
这些理由里没有一条是“AI 太强会毁灭人类”之类的宏大叙事。它们关注的都是一件事:当 LLM 参与进来之后,我对这个项目的理解是否还在?
这和我们国内技术社区的主流叙事有挺大差异。国内目前更强调 LLM 对开发效率的提升,很多教程在教你“如何用 LLM 十分钟写一个应用”。这种叙事没有错,但它掩盖了一个问题:效率只是开发的一个维度,理解、掌控、维护、审美,同样是开发的一部分。对于业余项目来说,后者的权重往往更高。
所以这个现象本身值得研究。它不是说“LLM 不好用”,而是说“LLM 的默认使用方式,和某些开发场景的价值目标是冲突的”。理解了这一点,你才能解释为什么同一项技术,在企业级开发中被奉为神器,在兴趣型社区里却被频繁诟病。
2. 先给结论:反对的不是工具,而是工具被使用的姿势
为了避免文章读起来像“各打五十大板”,我先把核心判断放在前面:
业余编程社区反对 LLM,本质上反对的不是“大语言模型”这个技术,而是反对那种用 LLM 取代个人理解、判断和责任感的使用方式。
换句话说,一个在自己服务器上部署了完整 LLM 推理环境、每天用模型辅助查文档的开发者,和一个“让 AI 把代码写完、自己只管复制粘贴”的开发者,对 LLM 的态度可能完全不同。前者反对的是后者,但后者往往以为前者是在“反对 AI”。
进一步拆解,这种反对可以归结为三个层面的冲突:
第一层:学习价值的冲突。业余项目的产出不只是代码,更是开发者本人的认知升级。如果一个项目是靠 LLM 生成的,开发者没有经历从模糊到清晰的思考过程,那么这个项目对他的成长贡献就接近于零。社区看重的是“理解”,而 LLM 的默认交互模式是“直接给答案”,两者存在天然张力。
第二层:维护成本的冲突。个人项目往往没有团队,没有专门的技术文档,甚至没有完整的测试。代码的唯一维护者就是作者本人。如果作者自己都不理解某段代码是怎么来的,那这个项目半年之后基本就废了。LLM 生成的“外来的代码”,如果不能被作者重新消化,本质上是在给项目的未来制造技术债。
第三层:控制权的冲突。兴趣项目的核心乐趣之一,是“我拥有这个系统”。哪怕它很简陋,但每一行代码都在自己的理解半径里。LLM 介入后,系统开始出现一些作者自己无法解释的部分。对一个追求掌控感的开发者来说,这种“失控感”是难以接受的。
理解了这三个冲突,你就明白了:社区抵制的不是“使用 LLM”这个行为本身,而是它背后的默认假设——“产出代码比理解代码更重要”。这个假设在企业环境里可能成立,但在兴趣编程里,恰恰被颠倒了。
3. 业余编程社区的文化内核:理解,比产出更重要
要深入理解这种反对情绪,还得回到业余编程(hobby programming)本身的文化内核。它和我们通常讨论的“职业开发”有着非常不同的激励结构。
职业开发的激励目标是交付。代码只是实现业务目标的中间产物,效率、可维护性、协作性都服务于最终的业务价值。在这个目标下,LLM 的帮助几乎是纯粹的加分项:它写得更快,你 review 一下,然后上线,没有额外的成本。即使某个模块你不太懂,只要同事懂、文档懂、测试覆盖了,系统依然可以稳定运行。
但业余项目的激励结构完全不同。你做业余项目不是为了交付给谁,而是为了满足自己的好奇心、探索欲和创造欲。在这个过程中,“我亲手把它做出来了”这个事实本身就构成了最大的回报。这也解释了为什么很多业余开发者会刻意选择“更麻烦”的技术路线——不是因为那条路线效率更高,而是因为那条路线更能满足他们对理解的渴求。
举个可能不太恰当但很形象的类比:做业余项目和做手工很像。手工的乐趣从来不只是“得到一个成品”,而是享受从一块木头、一块布料开始,一点点把它变成想要的东西的过程。如果有人给你一个现成的成品,告诉你“你要的那个东西就是这个”,你会觉得索然无味。LLM 在业余编程社区里遭遇的抵触,某种程度上就是这种“被递给你一个成品”的感觉。
更关键的是,业余项目往往是开发者学习新技术的实验场。很多人第一次接触 Python、第一次部署服务器、第一次写自动化脚本,都是在业余项目里完成的。在这个过程中,“踩坑”本身就是学习的一部分。你因为写错一个语法去查文档,远比直接复制 LLM 给出的正确答案记忆深刻。后者虽然高效,但它绕过了“试错—理解—纠正”这条最自然的学习路径。
当然,我不是说“每个错误都必须亲自犯一遍”。我的意思是,业余编程社区的价值排序里,“理解”的优先级远超“产出”。当一个工具让产出变得非常轻松的同时,也在悄悄削弱理解的可能,那么社区对它产生警惕,是再自然不过的事。
4. 技术矛盾:LLM 的产出方式与小项目原则的冲突
除了文化层面的原因,业余编程社区对 LLM 的抵触还有一些非常实在的技术原因。我把它归纳为三个小项目开发中极其看重的原则,以及 LLM 在这三个原则上的表现。
4.1 可读性:LLM 的“通用可读”不等于“你的可读”
LLM 生成的代码,单独看往往结构清晰、命名规范、注释完整,看起来很“可读”。但这种可读性是“面向一般开发者”的可读性,而不是“面向你这个项目上下文”的可读性。
举个典型场景:你的项目里有一个模块叫DataProcessor,这个类在整个系统中承担了特定的数据清洗职责。LLM 在帮你写新功能时,可能完全不知道这个类的历史包袱,于是生成一段代码直接调用了它的内部方法,破坏了原有的封装。从 LLM 的角度看,它生成的代码非常标准;但从你的项目角度看,这是一次不可接受的强拆。
这种问题在职业开发中可以通过 code review 拦截,但在业余项目里,你既是作者又是审查者,如果 LLM 生成的代码超过你的理解能力,你就很难发现问题。结果就是:代码表面上很规范,实际上在与你项目的真实需求慢性背离。
4.2 最小依赖:LLM 倾向于“多引入依赖,而不是少引入”
个人项目非常注重依赖的最小化。每多一个依赖,就多一份维护成本和安全隐患。但 LLM 在做技术选型时,天然倾向于推荐“主流”“热门”“功能全面”的库,而不是“对这个项目来说最合适”的库。
你问 LLM“如何解析这个配置文件”,它大概率会推荐一个功能完整的 YAML 库;但你在一个只有 200 行的小工具里,可能只需要标准库里的几行字符串处理就能解决。LLM 不是不知道标准库方案,而是在训练数据里,“使用成熟库”是更常见、更正确的通用答案。它缺少对你项目规模的感知,因此很难主动做出“这里我要少用依赖”这种针对性决策。
4.3 可调试性:黑盒产出的代码,黑了你的排查路径
这一点可能是最让社区反感的地方。LLM 生成的代码一旦出了 bug,排查起来比你自己写的代码困难得多。原因很简单:你自己写的代码,即使有 bug,你也知道当初是怎么想的、为什么这么写;而 LLM 生成的代码,你只能从行为上去推断它的意图,一旦推断不出来,就只能整段重写或者去问 LLM“为什么你给的代码有 bug”。
更麻烦的是,LLM 在解释自己的代码时并不会承认问题。它会非常自信地告诉你“这段代码应该能工作”,你进一步追问,它又给出一个看似合理的修改建议。结果就是你在这段代码上花的时间,可能比你自己从零写一遍还多。对一个追求效率的业余开发者来说,这是最不值得的买卖。
把这三个点放在一起看,你会发现一个共性:LLM 的设计目标是“生成一个大概率能工作的东西”,而个人项目的需求是“生成一个我能理解和掌握的东西”。这两个目标大部分时候不冲突,但在项目复杂度上升、个人理解深度不足的时候,就会剧烈碰撞。
5. LLM 在个人项目里的合适位置:从“写代码的人”到“协作审查的人”
前面说了这么多反对的声音,现在该给出建设性的方向了。我的观点很明确:
LLM 在个人项目里的正确位置,不是替你写代码的“外包程序员”,而是帮你把代码理解得更透彻的“结对审查者”。
这个定位的区别非常关键。当你把 LLM 当作“写代码的人”时,你交出的是设计和判断权;当你把 LLM 当作“审查者”时,你保留设计和判断权,只让它用它的知识面来帮你补盲区。后者不会削弱你的理解,反而会加深你的理解。
具体来说,有三种适合个人项目的用法。
5.1 让 LLM 审查你的代码,而不是生成代码
把你自己写完的代码贴给 LLM,要求它从代码质量、边界条件、潜在 bug、性能隐患四个角度给意见。这种方式让 LLM 站在你的代码之上做分析,而不是凭空生成一段与你项目无关的代码。
下面是一份可以复用的 Prompt 模板:
你是一位经验丰富的代码审查者。请审查下面的代码,不要重写它。 只需要指出以下问题: 1. 是否存在边界条件没有处理? 2. 是否存在潜在的空指针或异常风险? 3. 是否可以简化复杂度而不改变行为? 4. 是否存在并发或性能隐患? 请用中文回答,按问题严重程度排序,并给出具体行号。 如果代码没有明显问题,请直接说“未发现明显问题”。将你自己的代码放在这个 Prompt 下面提交给 LLM。你会发现,LLM 给出的建议通常比你预想的更细致,而你自己仍然掌握着是否采纳的最终决定权。这对保持代码的“作者感”非常重要。
5.2 让 LLM 生成测试用例,而不是生成实现
个人项目最常见的偷懒方式就是不写测试。LLM 虽然不能帮你理解代码,但它非常擅长帮你找出边界条件和异常输入。你写完一个函数之后,可以先让 LLM 基于你的实现生成一组测试用例,然后你运行它们,验证你的代码是否正确。
这里有一个真实的工程判断:让 LLM 写测试,比让 LLM 写实现安全得多。因为测试必须基于代码的实际行为,你运行之后立刻就能看到结果,对错非常清楚。而让 LLM 写实现,你往往要等代码上线出问题之后,才知道它错在哪。
5.3 让 LLM 做“解释器”,而不是“答案生成器”
遇到看不懂的报错、不熟悉的库函数、不明所以的语法糖时,把代码片段贴给 LLM,问它“这段代码在做什么?为什么会有这个写法?”而不是问“帮我改一下”。这两种问法的区别是:前者让你获得理解,后者让你获得一个你不理解的新答案。
我对这个策略的体会很深:很多时候,报错信息的解决方式并不复杂,真正复杂的是理解“为什么会报错”。LLM 在解释机制、对比方案、翻译文档方面,比生成代码靠谱得多。因为你把“生成”的权力留给了自己,而把“查阅”的工作交给了模型。
6. 用 LLM 构建个人的“知识基础设施”
上面讲的是代码层面的用法。在这个部分,我想把视角拉高一点,谈一个对业余开发者更有价值的 LLM 使用方向:用 LLM 构建你自己的知识系统,而不是用 LLM 替代你自己的知识系统。
这里要提到一个在技术圈讨论度颇高的思路——LLM Wiki。这个思路概括起来很简单:把 LLM 当作一个“知识整理助手”,用对话的方式把零散的文档、笔记、学习资料整理成结构化的个人知识库,再用类似 Wiki 的方式持续维护和检索这个知识库。它和直接问 LLM“给我解释一下这个概念”最大的区别在于:它把 LLM 的输出沉淀成了你自己可控的、长期积累的知识资产,而不是一次性对话。
对于业余开发者来说,这个思路的价值被严重低估了。做业余项目的时候,我们经常要接触新领域:可能这周要做一个硬件相关的上位机,下周要研究一下某个算法。每一次跨进新领域,都存在一个“从零搭建认知框架”的过程。如果没有知识库,你三个月前查过的资料、踩过的坑、总结过的结论,下次遇到时大概率要重新查一遍。
LLM Wiki 的思路是:每次接触新概念时,都让 LLM 帮你整理出一份结构化的笔记,你负责校对、补充和保存。时间一长,这份笔记就成了你的“第二大脑”。
实现这个流程不需要很复杂的工具。下面是一个最小化的整理脚本,它读取你收集的学习材料,调用 LLM API 生成结构化笔记,并保存为 Markdown 文件。为了方便演示,我用 Python 写了一个框架,具体 API 参数请以你使用的模型服务官方文档为准。
# 文件路径:notes_builder.py """ 最小化的 LLM Wiki 笔记生成脚本。 原理:把学习材料发送给 LLM,要求其按固定模板输出结构化笔记。 """ import os import re from pathlib import Path # 在实际项目中,推荐使用 requests 或 openai 官方 SDK 调用你的模型服务。 # 这里用函数占位,避免绑定特定厂商。 def call_llm(prompt: str) -> str: """ 调用 LLM 接口的占位函数。 你需要替换为实际的 API 调用逻辑,例如: response = client.chat.completions.create( model="your-model", messages=[{"role": "user", "content": prompt}] ) return response.choices[0].message.content """ # 示例直接返回提示词本身,避免未配置 API Key 时报错。 # 实际使用时替换为上面注释掉的部分。 return prompt def build_note(topic: str, source_text: str) -> str: prompt = f""" 请根据下面的学习材料,整理一份结构化笔记。 要求: 1. 使用 Markdown 格式。 2. 结构必须包含:核心概念、关键原理解释、适用场景、常见误区、实践中要注意的问题。 3. 使用中文。 4. 不要添加材料中没有的事实。 5. 结尾增加“待验证问题”一栏,列出你认为需要进一步查证的内容。 学习主题:{topic} 学习材料: {source_text} """ return call_llm(prompt) def sanitize_filename(name: str) -> str: return re.sub(r'[\\/:*?"<>|]', "_", name) def save_note(topic: str, content: str, output_dir: Path) -> None: output_dir.mkdir(parents=True, exist_ok=True) file_path = output_dir / f"{sanitize_filename(topic)}.md" file_path.write_text(content, encoding="utf-8") print(f"笔记已保存到:{file_path}") if __name__ == "__main__": topic = "LLM 微调" source_text = """ 这里是你在资料中复制/粘贴过来的原始文本,或者是你自己的零散笔记。 比如关于微调数据集构建、loss 变化、过拟合现象的观察。 """ note_content = build_note(topic, source_text) save_note(topic, note_content, Path("./my_wiki"))运行方式如下:
python notes_builder.py这段代码本身没什么高深的,但它体现了一个很关键的思路转变:你让 LLM 帮你整理知识,但知识的筛选权、保存权和校验权都在你手里。长期做下去,你积累的不只是笔记,而是对某个领域越来越清晰的认知地图。这个方向的价值,远高于让 LLM 帮你写几段代码。
7. 常见问题与排查思路
在个人项目里使用 LLM,一定会遇到一些问题。这里整理了我认为最常见的几个,并给出可操作的排查思路。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| LLM 生成的代码运行报错,但报错信息看不懂 | 代码本身可以运行,但与你项目的上下文不匹配 | 先阅读报错堆栈,定位到具体文件和函数;再对照 LLM 输出时的假设条件 | 把报错信息和项目上下文一起回传 LLM,让它解释报错原因,而不是直接让它重写 |
| LLM 推荐了一个新的库,但安装后发现项目原本的版本被破坏了 | LLM 推荐的库与现有依赖存在版本冲突 | 查看依赖冲突日志;检查requirements.txt或包管理器输出的依赖树 | 先回滚依赖变更;除非必要,不轻易引入新库;优先使用项目已有的库 |
| 用 LLM 解释了某个概念,但后来发现解释是错的 | 模型对训练数据中的知识进行了错误关联 | 回到官方文档或源码核对关键细节 | 把 LLM 当作“检索入口”,不要当作“事实来源”;对重要结论做二次验证 |
| 让 LLM 帮忙重构代码,结果行为变了 | LLM 不理解原代码的行为约束,重构时改了语义 | 对比重构前后函数的输入输出 | 要求 LLM 给出“保持行为不变”的重构,最好配合已有测试;没有测试时不要轻易重构 |
| 依赖 LLM 写了几周代码后,发现自己看不懂没有 LLM 时的实现了 | 学习路径被压缩,理解没有跟上 | 尝试不借助 LLM,用画图或注释方式梳理现有代码结构 | 减少生成式提问,增加解释式提问;给自己设定“无 AI 编程日” |
这些问题的共性很有意思:大部分坑不是 LLM 本身造成的,而是因为使用者把 LLM 的“参考答案”当成了“标准答案”。在业余项目里,没有外部团队替你兜底,唯一的兜底就是你对代码和概念的理解。一旦理解掉线,LLM 的帮助就会瞬间变成负担。
8. 工程建议:如何在个人项目中合理使用 LLM
如果你认同前面的分析,那么你可能会想知道:具体到日常开发流程里,应该怎么做?我给出几条比较务实的建议,也算是个人实践中沉淀下来的准则。
第一,为自己设置“理解门禁”。任何 LLM 生成的代码,合并进项目之前,你都必须能用自己的话解释每一行在做什么。如果解释不了,就回到官方文档或亲手调试,直到能解释为止。这条门禁建立起来,LLM 就会从“替代你思考”变成“帮你加速理解”。
第二,先写测试,再让 LLM 实现。哪怕测试写得很粗糙,也比没有强。你先把预期的输入输出定好,然后让 LLM 去实现。实现完之后直接跑测试,通过才算数。这样 LLM 的输出就有了一个客观的验收标准,而不是凭感觉觉得“好像可以跑”。
第三,把 LLM 的上下文窗口当成“临时的”,把项目里的笔记当成“永恒的”。一段对话再长,关掉窗口就没了。所以在重要的技术决策、架构分析、踩坑记录上,记得让 LLM 帮你整理成笔记,然后保存到项目目录里。这比任何提示词技巧都重要,因为长期的项目价值来自沉淀,而不是即时反馈。
第四,分辨场景:学习型项目尽量少用 LLM,工具型项目可以多用。如果你做这个项目就是纯粹为了学新东西,那么建议你克制使用 LLM 的冲动,至少要让自己经历完整的思考过程;如果这是一个你已经很熟悉的领域的工具型项目,那么 LLM 的加快效率作用完全可以放开用。判断标准只有一个:这段代码被 LLM 写走之后,你是感觉自己“省了一步”,还是感觉自己“失去了一段”?
第五,记录 LLM 的使用痕迹。在代码注释里标记哪些部分是 LLM 生成的、哪些是你自己写的。这不是为了甩锅,而是为了后续维护时快速定位“这段代码可能包含我不理解的逻辑”。同时,在 review 自己的项目时,优先 review 这些标记部分。
第六,不要追求“零 AI”的心态,但也不要追求“全 AI”的状态。这两种极端都没有必要。零 AI 会让你错过一个非常好的工具;全 AI 会让你逐渐脱离技术根基。找到你自己的平衡点,然后随着项目类型动态调整,才是更合理的状态。
9. 一个更底层的视角:LLM 是把手,但不是手的主人
写到这里,我想再回到题目本身。“Born Against”这个标题,如果直译,是一种“天生反对”的姿态。但我在前面已经反复强调过,业余编程社区的反对,本质上不是“天生反对新工具”,而是“天生反对丧失理解”。
这两者之间有非常微妙的区别。前者是一种立场,后者是一种判断。立场不需要理由,判断需要。而恰恰是这些“需要理由”的判断,才是技术讨论中最有价值的部分。它逼着我们去思考:工具为什么被设计成这样?它默认了什么样的使用方式?这种使用方式和我自己的项目目标是否兼容?
对普通开发者来说,最值得警惕的不是“用了 LLM”,而是“在没想清楚的情况下用了 LLM”。当你拿到一个自动补全出来的函数,你真的知道它为什么这么写吗?当你按照 LLM 的建议引入一个依赖,你真的知道它会给项目带来什么吗?如果你不知道,那你不是在“使用工具”,而是在“被工具使用”。
反过来说,当你带着清晰的判断去使用 LLM——知道自己从它那里要什么、不要什么,知道它的输出必须经过自己的理解门禁——那么它就是一个极好的手足。它替你查资料、帮你写测试、帮你整理笔记、帮你发现盲区,但最终写进项目里的每一行代码,依然是你自己的决定。
这或许就是兴趣编程社区真正想传递的态度:你可以使用一切工具,但工具的产出必须经过你的理解和选择,才能真正成为你能力的一部分。这也应该是每一个对代码还有热爱的开发者,在 AI 时代守住的专业底线。