
我记得在某个技术论坛的讨论串里看到过这样一条高赞回复“我不是反对 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( modelyour-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(parentsTrue, exist_okTrue) file_path output_dir / f{sanitize_filename(topic)}.md file_path.write_text(content, encodingutf-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 时代守住的专业底线。