2025-12-23 07:48:00
原文: https://kyleshevlin.com/nothing-is-something/
作者: Kyle Shevlin
译者: Gemini 3 Pro High
我真的需要更多人去观看 Sandi Metz 的 “Nothing is Something”(无也是一种有),这样当我在代码中应用其中的教训时,就不会收到奇怪的目光了。
今天我正在处理类似下面这样一个问题。想象你有一个键值对对象,其中键代表某种状态,而值则是对该状态的某种转换。
// Just some arbitrary state for our example
type State = {
count: number
createdAt: Date
isActive: boolean
name: string
}
type Transform = <T>(x: T) => T
const STATE_TRANSFORMS: Partial<Record<keyof State, Transform>> = {
count: x => x * 2,
name: x => x.toUpperCase(),
}
这些转换本身并不特别重要,但请注意,我并没有为 State 类型中的每一个键都提供转换。
现在,如果你要编写一个函数来运行这些转换,你可能会想这样做:
function transformStates(state: State) {
const nextState: State = {}
for (const key of state) {
const transform = STATE_TRANSFORMS[key]
// This handles the condition at the last possible moment
// and it results in two separate ways of managing the state
if (transform) {
nextState[key] = transform(state[key])
} else {
nextState[key] = state[key]
}
}
return nextState
}
但我更喜欢这样做:
const identity = x => x
function transformStates(state) {
const nextState = {}
for (const key of state) {
// Condition is handled a step earlier and we've narrowed
// the type of `transform` from `Transform | undefined`
// to just `Transform`, simplifying the next step
const transform = STATE_TRANSFORMS[key] ?? identity
nextState[key] = transform(state[key])
}
return nextState
}
因为,正如演讲标题所暗示的,什么都不做(doing nothing)本身就是在做某事(doing something)。
你可能会争辩说我在这里进行了不必要的函数调用,但我认为为了代码的简洁,这是一个值得的权衡。
我们可以利用这种模式——即识别“无”(也就是默认行为)并为其提供功能——作为简化代码的一种方式,主要是通过减少过多的条件判断,而条件判断往往是复杂性的来源。
请观看那个演讲。你会因此成为一名更好的程序员。
2025-12-23 05:30:00
原文: https://simonwillison.net/2025/Dec/15/porting-justhtml/
作者: Simon Willison
译者: Gemini 3 Pro High
2025年12月15日
我昨天写了关于 JustHTML 的文章——这是 Emil Stenström 的项目,旨在利用运行于全面的 html5lib-tests 测试库之上的编码代理,构建一个新的、符合标准的纯 Python 代码 HTML5 解析器。昨晚,纯粹出于好奇,我决定尝试使用 Codex CLI 和 GPT-5.2,以尽可能少的精力将 JustHTML 从 Python 移植到 JavaScript。结果超出了我的预期。
我构建了 simonw/justjshtml,这是一个 JavaScript 中的无依赖 HTML5 解析库,它通过了 html5lib-tests 套件中的 9,200 项测试,并模仿了 Emil 的 JustHTML 库的 API 设计。
这只用了两个初始提示和一些微小的后续跟进。在 Codex CLI 中运行的 GPT-5.2 不间断地运行了几个小时,消耗了 1,464,295 个输入 token,97,122,176 个缓存输入 token 和 625,563 个输出 token,最终生成了 9,000 行经过充分测试的 JavaScript 代码,共计 43 次提交。
从项目构思到完成库的时间:大约 4 小时,在此期间我还和家人一起购买并装饰了圣诞树,并观看了最新的《利刃出鞘》(Knives Out) 电影。
十年前 HTML5 规范最重要的贡献之一是它精确地规定了应如何解析无效 HTML。世界上充满了无效文档,拥有涵盖这些情况的规范意味着浏览器可以用相同的方式处理它们——在构建解析软件时不再需要担心“未定义行为”。
不出所料,这些无效解析规则相当复杂!Simon Pieters 的免费在线书籍 HTML 解析器的特质 (Idiosyncrasies of the HTML parser) 深入探讨了这一主题,特别是 第 3 章:HTML 解析器。
Python 的 html5lib 项目启动了 html5lib-tests 仓库,其中包含一组独立于实现的测试。此后,这些测试已成为 HTML5 解析器互操作性测试的黄金标准,并被诸如 Servo 之类的项目所使用,Servo 使用它们来帮助构建 html5ever,这是一个用 Rust 编写的“高性能浏览器级 HTML5 解析器”。
Emil Stenström 的 JustHTML 项目是一个纯 Python 实现的 HTML5 解析器,通过了完整的 html5lib-tests 套件。Emil 花了几个月时间将其作为一个副业项目,特意选择了一个拥有全面现有测试套件的问题,以看看利用编码代理能走多远。
有一次,他让代理根据对 Rust html5ever 库的仔细检查来重写它。我不知道这其中有多少是直接翻译,有多少是灵感借鉴(这里是 Emil 对此的评论)——他的项目总共有 1,215 次提交,所以看起来包含了大量的迭代,而不仅仅是直接移植。
我的项目就是一个直接移植。我指示 Codex CLI 构建 Emil 的 Python 代码的 JavaScript 版本。
我从一些准备工作开始。我检出了两个仓库,并为新项目创建了一个空的第三个目录:
cd ~/dev
git clone https://github.com/EmilStenstrom/justhtml
git clone https://github.com/html5lib/html5lib-tests
mkdir justjshtml
cd justjshtml
然后我像这样为 GPT-5.2 启动了 Codex CLI:
codex --yolo -m gpt-5.2
那个 --yolo 标志是 --dangerously-bypass-approvals-and-sandbox(危险地绕过批准和沙箱)的快捷方式,正如听起来那样危险。
我的第一个提示告诉 Codex 检查现有代码,并使用它为新的 JavaScript 库构建规范:
We are going to create a JavaScript port of ~/dev/justhtml - an HTML parsing library that passes the full ~/dev/html5lib-tests test suite. It is going to have a similar API to the Python library but in JavaScript. It will have no dependencies other than raw JavaScript, hence it will work great in the browser and node.js and other environments. Start by reading ~/dev/justhtml and designing the user-facing API for the new library - create a spec.md containing your plan.
我们将创建 ~/dev/justhtml 的 JavaScript 移植版 - 这是一个通过完整 ~/dev/html5lib-tests 测试套件的 HTML 解析库。它将拥有与 Python 库类似的 API,但是是 JavaScript 版本的。除了原生 JavaScript 外它没有任何依赖,因此它将在浏览器、node.js 和其他环境中运行良好。首先阅读 ~/dev/justhtml 并为新库设计面向用户的 API - 创建一个包含你计划的 spec.md。
我审查了规范,其中包括一组建议的里程碑,并告诉它再添加一个:
Add an early step to the roadmap that involves an initial version that parses a simple example document that is valid and returns the right results. Then add and commit the spec.md file.
在路线图中添加一个早期步骤,涉及解析一个简单有效示例文档并返回正确结果的初始版本。然后添加并提交 spec.md 文件。
这是生成的 spec.md 文件。我对该初始版本的请求变成了“里程碑 0.5”,如下所示:
里程碑 0.5 — 端到端冒烟解析(单个有效文档)
- 实现最小的端到端切片,以便公共 API 尽早真实可用:
new JustHTML(\"<html><head></head><body><p>Hello</p></body></html>\")返回具有预期标签结构和文本节点的树。- 对于此有效输入,
doc.toText()返回\"Hello\"且doc.errors为空。- 添加
scripts/smoke.js(无依赖),运行该示例并断言预期的结构/输出。- 门控:
node scripts/smoke.js通过。
然后我告诉它:
实现里程碑 0.5
然后它就开始工作了。生成的代码似乎可以工作,所以我说:
Configure GitHub Actions test.yml to run that on every commit, then commit and push
配置 GitHub Actions test.yml 以在每次提交时运行它,然后提交并推送
此时我运行了 git init 并为该项目创建了一个私有 GitHub 仓库,并设置本地目录以推送到该远程仓库。
这是那次初始推送。然后我告诉它:
OK do the rest, commit and push often
好的,做剩下的部分,经常提交和推送
这就差不多了!
我将笔记本电脑设置为不休眠,让它自行运行,而我们去买圣诞树了。“经常提交和推送”意味着我可以通过刷新 GitHub 上的提交日志在手机上监控其进度。
我是在我的每月 20 美元的 ChatGPT Plus 账户上运行此操作的,该账户对 Codex CLI 有五小时的 token 限额窗口。这在下午 6:35 用完了,Codex 暂停了,所以我等到下午 7:14 的重置点,然后输入:
继续
晚上 9:30,它宣布完成,并显示以下摘要消息:
• What’s Done
- html5lib-tests now run in CI and pass: tokenizer 6810/6810, tree 1770/1782
(12 skipped: #script-on), serializer 230/230, encoding 82/83 (1 skipped:
scripted) (.github/workflows/test.yml:1).
- Public API expanded: stream() (src/stream.js:1), CSS selectors query()/\n matches() (src/selector.js:1), and toMarkdown() (src/markdown.js:1), all
exported via src/index.js:1.
- Added no-deps unit scripts for selectors/stream/markdown (scripts/run-\n selector-tests.js:1, scripts/run-stream-tests.js:1, scripts/run-markdown-\n tests.js:1) and wired them into CI.
- Fixed TreeBuilder so <br> no longer closes <p> (src/treebuilder.js:647).
作为最后的润色,我让它添加了一个 playground 界面,这样我就可以在浏览器中试用新库。我提示道:
Add a playground.html in the top level folder which loads the necessary ES modules from ./src/… and implements the exact same functionality as seen on https://tools.simonwillison.net/justhtml but using the JavaScript library instead of Pyodide
在顶层文件夹中添加一个 playground.html,从 ./src/… 加载必要的 ES 模块,并实现与 https://tools.simonwillison.net/justhtml 上完全相同的功能,但使用 JavaScript 库而不是 Pyodide
它使用 curl 获取了我现有的 JustHTML playground 页面(在此处描述),并构建了一个新的 playground.html 文件,改为加载新的 JavaScript 代码。这工作得完美。
我为我仍然私有的仓库启用了 GitHub Pages,这意味着我可以通过此 URL 访问新的 playground:
https://simonw.github.io/justjshtml/playground.html

现在它只需要一些文档:
Add a comprehensive README with full usage instructions including attribution plus how this was built plus how to use in in HTML plus how to use it in Node.js
添加一个全面的 README,包含完整的使用说明,包括归属、它是如何构建的、如何在 HTML 中使用它以及如何在 Node.js 中使用它
你可以在这里阅读结果。
现在我们总共用了八个提示,运行了四个多小时,而且我已经装饰了圣诞树并在 Netflix 上观看了《死人复活》(Wake Up Dead Man)。
根据 Codex CLI:
Token 使用情况:总计=2,089,858 输入=1,464,295 (+ 97,122,176 缓存) 输出=625,563 (推理 437,010)
我的 llm-prices.com 计算器 估计,如果我按 API 价格支付这些 token,费用为 29.41 美元,但它们包含在我的每月 20 美元的 ChatGPT Plus 订阅中,因此对我来说实际额外费用为零。
我分享这个项目是因为我认为它展示了 2025 年 12 月 LLM 状态的许多有趣之处。
最后,我提出一些开放性问题:
2025-12-23 04:30:00
原文: https://azukiazusa.dev/blog/claude-code-lsp-support
作者: azukiazusa1
译者: Gemini 3 Pro High
Claude Code 从版本 2.0.74 开始增加了 LSP(语言服务器协议)支持。通过 LSP 支持,Claude Code 可以对代码库执行符号定义查找、引用查找、获取悬停信息等操作。本文将介绍 Claude Code 的 LSP 支持概要及使用方法。
编码代理通过掌握 LSP(语言服务器协议),可以更高效地执行任务。与通过字符串搜索整个代码库不同,通过 LSP 可以访问代码结构和符号信息,从而能够迅速获取和编辑准确信息,大幅节省 Token 和时间。对于尝试过为编码代理提供语义代码搜索和编辑工具 Serena 的人来说,这尤其容易理解。
Claude Code 从 v2.0.74 开始增加了 LSP 支持。LSP 支持作为 Claude Code 插件功能的一部分提供。启用 LSP 功能后,Claude Code 可以对代码库执行以下操作:
本文将介绍 Claude Code 的 LSP 支持概要及使用方法。
要启用 LSP 功能,请启动 Claude Code 并运行 /plugin 命令,以交互方式启用插件。要安装插件,需要添加提供目标插件的市场。LSP 插件在 Claude Code 官方市场(claude-plugins-official)中提供。官方市场通常无需任何设置即可默认使用。请在“Marketplaces”选项卡中确认官方市场已启用。

接下来移动到“Discover”选项卡,将显示可用插件列表。在搜索框中输入“LSP”进行搜索,LSP 插件列表将按语言显示。可用语言如下:
使用 Space 键选择要使用的语言的 LSP 插件,然后按 i 键开始安装。

已安装的插件可以在“Installed”选项卡中确认。

此外,还需要安装对应 LSP 插件语言的 LSP 服务器。例如,如果想使用 TypeScript 的 LSP 功能,请安装 typescript-language-server。
npm install -g typescript-language-server typescript
实际上,我尝试启用了 TypeScript LSP 插件并在 TypeScript 代码库中进行了测试,但目前似乎无法正常工作。LSP 服务器插件似乎需要一个名为 .lsp.json 的配置文件,但目前似乎只提供了 README.md。让我们期待未来的更新。
如果想使用 Claude Code 官方市场不支持的语言的 LSP 服务器,可以创建自定义插件来提供 LSP 插件。要创建插件,请创建一个新目录,并创建一个 /.claude-plugin 子目录。
mkdir -p my-lsp-plugin/.claude-plugin
创建 .claude-plugin/plugin.json 文件,并按如下方式编写:
{
"name": "my-typescript-lsp-plugin",
"description": "TypeScript/JavaScript language server for Claude Code, providing code intelligence features like go-to-definition, find references, and error checking.",
"version": "1.0.0",
"author": {
"name": "Your Name"
}
}
为了向插件添加 LSP 支持,请在插件目录的根目录下创建 .lsp.json 文件,并按如下方式编写。这是一个使用 TypeScript LSP 服务器的示例。
{
"typescript": {
"command": "typescript-language-server",
"args": ["--stdio"],
"extensionToLanguage": {
".ts": "typescript",
".tsx": "typescript"
}
}
}
要测试创建的插件,请使用 --plugin-dir 选项启动 Claude Code。
claude --plugin-dir ./my-lsp-plugin
运行 /plugin 命令以确认插件已被正确识别。通过 --plugin-dir 指定的插件将被识别为 inline 市场。

在 v2.0.74 中,由于存在插件和 LSP 服务器冲突的问题,似乎无法正常工作。 https://github.com/anthropics/claude-code/issues/13952
为了避开上述问题并测试 LSP 功能,可以将 Claude Code 版本降级到 v2.0.67,并将环境变量 ENABLE_LSP_TOOL 设置为 true 启动。
ENABLE_LSP_TOOL=true npx @anthropic-ai/[email protected] --plugin-dir ./my-lsp-plugin
当 Claude Code 使用 LSP 功能时,会使用 LSP(…) 工具。这里为了重命名 <Button> 组件,使用 findReferences 命令来搜索定义 Button 符号的位置。

.lsp.json 文件ENABLE_LSP_TOOL 设置为 true 启动来规避2025-12-22 06:45:30
原文: https://addyo.substack.com/p/my-llm-coding-workflow-going-into
作者: Addy Osmani
译者: Gemini 3 Pro High
AI 编程助手在今年成为了游戏规则的改变者,但有效利用它们需要技巧和结构。 这些工具极大地扩展了 LLM 在现实世界编程中的能力,许多开发者(包括我自己)都欣然接受了它们。
例如在 Anthropic,工程师们大量使用 Claude Code,以至于 今天 Claude Code 约 90% 的代码是由 Claude Code 自己编写的。然而,使用 LLM 进行编程并不是一种按键即得的魔法体验——它“困难且不直观”,要获得好的结果需要学习新的模式。批判性思维仍然是关键。经过一年多的项目实践,我总结出了一套工作流,类似于许多经验丰富的开发者正在发现的那样:将 LLM 视为一个强大的结对程序员,它需要明确的指导、上下文和监督,而不是自主判断。
在这篇文章中,我将分享我迈向 2026 年时如何规划、编码以及与 AI 协作,提炼出我从自身经验和社区集体学习中获得的技巧和最佳实践。这是一种更自律的 “AI 辅助工程” 方法——积极利用 AI,同时对生成的软件自豪地承担责任。
如果你对我的工作流感兴趣,可以查看“AI 原生软件工程师”,否则让我们直接深入探讨我学到的一些经验教训。
不要只是向 LLM 抛出愿望——从定义问题和规划解决方案开始。
一个常见的错误是带着模糊的提示直接进入代码生成。在我的工作流中,以及许多其他人的工作流中,第一步是与 AI 一起头脑风暴一份详细的规格说明,然后列出一步步的计划,在编写任何实际代码之前。对于一个新项目,我会描述这个想法,并要求 LLM 反复向我提问,直到我们充实了需求和边缘情况。最后,我们将这些汇编成一份全面的 spec.md——包含需求、架构决策、数据模型,甚至测试策略。这份规格说明构成了开发的基础。
接下来,我将规格说明输入到一个具有推理能力的模型中,并提示它生成项目计划:将实现分解为逻辑清晰、小块的任务或里程碑。AI 本质上是在帮我做一个迷你的“设计文档”或项目计划。我经常迭代这个计划——编辑并要求 AI 批评或完善它——直到它连贯且完整。只有在那时我才开始编码。这种前期投资可能会感觉很慢,但回报巨大。正如 Les Orchard 所说,这就像是在做 “15 分钟的瀑布流”——一个快速的结构化规划阶段,使随后的编码更加顺畅。
拥有清晰的规格和计划意味着当我们释放代码生成能力时,人类和 LLM 都确切知道我们在构建什么以及为什么。简而言之,先规划迫使你和 AI 处于同一页面,防止浪费循环。这是许多人试图跳过的一步,但经验丰富的 LLM 开发者现在将稳健的规格/计划视为工作流的基石。
范围管理就是一切——给 LLM 提供可管理的任务,而不是一次性提供整个代码库。
==我学到的一个重要教训是避免要求 AI 输出大型、单体的内容。相反,我们将项目分解为迭代步骤或工单,并逐一解决。这反映了良好的软件工程实践,但在有 AI 参与时更为重要。当给出专注的提示时,LLM 表现最好:实现一个函数,修复一个 bug,一次添加一个功能。==例如,在规划之后,我会提示代码生成模型:“好了,让我们实现计划中的第一步”。我们编写代码,测试它,然后通过第二步,依此类推。每个块都足够小,AI 可以在上下文中处理它,你也可以理解它生成的代码。
这种方法可以防止模型偏离轨道。如果你一次要求太多,它很可能会感到困惑或产生一个难以理清的 “混乱的烂摊子”。开发者报告说,当他们试图让 LLM 生成应用程序的大部分内容时,最终得到的是不一致和重复的代码——“就像 10 个开发者在没有相互交流的情况下工作一样,”有人这样说。我感受过这种痛苦;解决办法是停下来,退后一步,将问题分解成更小的部分。每次迭代,我们都延续已构建内容的上下文,并增量地添加它。这也非常适合测试驱动开发 (TDD) 方法——我们可以边做边为每个部分编写或生成测试(稍后会详细介绍测试)。
一些编码代理工具现在明确支持这种分块工作流。例如,我经常生成一个结构化的 “提示计划” 文件,其中包含每个任务的一系列提示,以便像 Cursor 这样的工具可以逐一执行它们。关键点是避免巨大的飞跃。通过在小循环中迭代,我们大大降低了灾难性错误的几率,并且可以快速纠正方向。LLM 擅长快速、包含的任务——利用这一点。
LLM 的好坏取决于你提供的上下文—— 向它们展示 相关的代码、文档和约束。
在代码库上工作时,我确保向 AI 提供它所需的所有信息以表现良好。这包括它应该修改或引用的代码、项目的技术约束,以及任何已知的陷阱或首选方法。现代工具在这方面有所帮助:例如,Anthropic 的 Claude 可以在“Projects”模式下将整个 GitHub 仓库导入其上下文,像 Cursor 或 Copilot 这样的 IDE 助手会自动在提示中包含打开的文件。但我经常更进一步——如果我怀疑模型没有这些信息,我会使用像 Context7 这样的 MCP,或者手动将代码库或 API 文档的重要部分复制到对话中。
资深 LLM 用户强调这种“上下文打包”步骤。例如,在编码之前进行 “脑力倾倒”,将模型应该知道的所有内容都告诉它,包括:高层目标和不变量、好的解决方案示例,以及关于要避免的方法的警告。如果我要求 AI 实现一个棘手的解决方案,我可能会告诉它哪些简单的解决方案太慢,或者提供其他地方的参考实现。如果我使用的是一个小众库或全新的 API,我会粘贴官方文档或 README,这样 AI 就不会盲目操作。所有这些前期上下文极大地提高了其输出质量,因为模型不是在猜测——它面前有事实和约束。
现在有一些实用工具可以自动进行上下文打包。我尝试过像 gitingest 或 repo2txt 这样的工具,它们本质上是将你代码库的相关部分“转储”到一个文本文件中供 LLM 阅读。当处理大型项目时,这可能是救命稻草——你生成一个包含关键源文件的 output.txt 包,让模型摄取它。原则是:不要让 AI 在信息不全的情况下操作。如果一个 bug 修复需要理解四个不同的模块,就向它展示这四个模块。是的,我们必须注意 token 限制,但当前的前沿模型拥有相当大的上下文窗口(数万个 token)。明智地使用它们。我经常选择性地仅包含与手头任务相关的代码部分,并明确告诉 AI 如果某些内容超出范围就不要关注(以节省 token)。
我认为 Claude Skills 很有潜力,因为它们将过去脆弱的重复提示转化为持久且可重用的东西,通过将指令、脚本和特定领域的专业知识打包成模块化能力,工具可以在请求匹配 Skill 时自动应用。这意味着你可以获得比通用提示更可靠和上下文感知的结果,并且你将从一次性交互转向以一致方式编码可重复过程和团队知识的工作流。存在许多社区策划的 Skills 集合,但我最喜欢的例子之一是 frontend-design skill,它可以“终结”LLM 生成的 UI 中普遍存在的紫色设计美学。在更多工具正式支持 Skills 之前,变通方法是存在的。
最后,在提示中使用注释和规则指导 AI。我可能会在代码片段前加上:“这是 X 的当前实现。我们需要扩展它以做 Y,但要小心不要破坏 Z。”这些小提示很有用。LLM 是字面主义者——它们会遵循指令,所以给它们详细的、上下文相关的指令。通过主动提供上下文和指导,我们将幻觉和离谱建议降至最低,并获得符合我们项目需求的代码。
并非所有编码 LLM 都是平等的——有目的地挑选你的工具,并且不要害怕中途更换模型。
在 2025 年,我们被各种各样专注于代码的强大 LLM 宠坏了。我工作流的一部分是选择最适合每个任务的模型或服务。有时甚至并行尝试两个或更多 LLM 来交叉检查它们如何以不同方式处理同一问题也是有价值的。
每个模型都有自己的“个性”。关键是:如果一个模型卡住了或给出了平庸的输出,试试另一个。 我实际上曾将相同的提示从一个聊天复制到另一个服务中,看看它是否能更好地处理。这种“模型抢椅子”游戏可以在你遇到模型的盲点时拯救你。
此外,确保你使用的是可用的最佳版本。如果可以,请使用最新的“pro”层级模型——因为质量很重要。是的,这通常意味着付费访问,但生产力的提高可以证明这一点。最终,选择那个 “氛围”与你相投的 AI 结对程序员。我知道有些人仅仅因为喜欢某个模型的回复感觉而偏爱它。这是合理的——当你本质上是在与 AI 进行持续对话时,用户体验和语气会有所不同。
就我个人而言,最近我倾向于使用 Gemini 进行大量的编码工作,因为交互感觉更自然,而且它经常在第一次尝试时就理解我的请求。但如果需要,我会毫不犹豫地切换到另一个模型;有时第二种意见有助于解决方案的浮现。总之:使用最适合工作的工具,并记住你有一个 AI 军火库可供支配。
在整个 SDLC 中利用特定于编码的 AI 帮助来增强你的工作流。
在命令行上,新的 AI 代理出现了。Claude Code、OpenAI 的 Codex CLI 和 Google 的 Gemini CLI 是你可以直接在项目目录中与之聊天的 CLI 工具——它们可以读取文件、运行测试,甚至多步修复问题。我也使用过 Google 的 Jules 和 GitHub 的 Copilot Agent——这些是异步编码代理,它们实际上将你的仓库克隆到云 VM 中并在后台处理任务(编写测试、修复 bug,然后为你打开 PR)。目睹这一切有点令人毛骨悚然:你发出像“为 X 重构支付模块”这样的命令,过一会儿你会收到一个包含代码更改和通过测试的拉取请求。我们真的生活在未来。你可以在从指挥家到编排者中阅读更多关于这方面的内容。
即便如此,这些工具并非绝对可靠,你必须了解它们的局限性。它们加速了编码的机械部分——生成样板代码、应用重复性更改、自动运行测试——但它们仍然极大地受益于你的指导。例如,当我使用像 Claude 或 Copilot 这样的代理来实现某些东西时,我经常向它提供早期步骤中的计划或待办事项列表,以便它知道确切的任务顺序。如果代理支持,我会在告诉它执行之前将我的 spec.md 或 plan.md 加载到上下文中。这让它保持在正轨上。
我们还没有达到让 AI 代理无人值守地编写整个功能并期望完美结果的阶段。相反,我以受监督的方式使用这些工具:我会让它们生成甚至运行代码,但我会关注每一步,准备在看起来不对劲时介入。还有像 Conductor 这样的编排工具,允许你在不同的任务上并行运行多个代理(本质上是一种扩大 AI 帮助的方式)——一些工程师正在尝试同时运行 3-4 个代理来处理不同的功能。我涉足过这种“大规模并行”方法;它在快速完成大量工作方面出奇地有效,但监控多个 AI 线程在精神上也很有压力!对于大多数情况,我坚持一次使用一个主要代理,也许还有一个次要代理用于审查(下文讨论)。
只需记住这些是电动工具——你仍然控制着扳机并引导结果。
AI 如何改善开发者体验的全面概述。这涵盖了设计、内部循环、提交和外部循环——突出了 AI 可以有意义地减少苦差事的每一个点。
AI 会很乐意生成看起来似是而非的代码,但 你 对质量负责——始终彻底审查和测试。
我的首要规则之一是永远不要盲目信任 LLM 的输出。==正如 Simon Willison 恰当地说,将 LLM 结对程序员视为 “过度自信且容易犯错”。它带着完全的确信编写代码——包括 bug 或废话——除非你抓住它,否则它不会告诉你有什么不对。所以我把每一段 AI 生成的代码都当作是初级开发人员写的:我通读代码,运行它,并根据需要进行测试。你绝对必须测试它写的东西——运行那些单元测试,或者手动演练该功能==,以确保它做到了它声称的事情。在氛围编码不是低质量工作的借口中阅读更多相关内容。
事实上,我将测试编织到了工作流本身中。我早期的规划阶段通常包括为每一步生成测试列表或测试计划。如果我使用像 Claude Code 这样的工具,我会指示它在实现任务后运行测试套件,并在出现故障时让它调试。这种紧密的反馈循环(编写代码 → 运行测试 → 修复)是 AI 擅长的,只要测试存在。这就不足为奇了,那些从编码代理中获益最多的人往往是那些拥有强大测试实践的人。像 Claude 这样的代理如果有良好的测试套件作为安全网,就可以在一个项目中“飞”起来。没有测试,代理可能会轻率地假设一切都很好(“当然,一切都好!”),而实际上它已经破坏了几样东西。所以,投资于测试——它会放大 AI 的实用性和对结果的信心。
甚至除了自动化测试之外,进行代码审查——包括手动和 AI 辅助的。我经常暂停并逐行审查迄今为止生成的代码。有时我会启动第二个 AI 会话(或不同的模型),并要求它批评或审查第一个生成的代码。例如,我可能会让 Claude 编写代码,然后问 Gemini:“你能审查这个函数是否有错误或改进吗?”这可以捕捉到微妙的问题。关键是不要仅仅因为是 AI 写的代码就跳过审查。如果有的话,AI 编写的代码需要额外的审查,因为它有时可能表面上令人信服,却隐藏着人类可能不会立即注意到的缺陷。
我还使用 Chrome DevTools MCP,这是我与上一个团队一起构建的,用于我的调试和质量循环,以弥合静态代码分析和实时浏览器执行之间的差距。它“给你的代理装上了眼睛”。它让我能够授权我的 AI 工具直接访问浏览器能看到的内容,检查 DOM,获取丰富的性能跟踪、控制台日志或网络跟踪。这种集成消除了手动上下文切换的摩擦,允许直接通过 LLM 进行自动化 UI 测试。这意味着可以根据实际运行时数据高精度地诊断和修复 bug。
跳过人类监督的可怕后果已被记录在案。一位在紧急项目中严重依赖 AI 生成的开发人员描述结果是一团不一致的混乱——重复的逻辑、不匹配的方法名、没有连贯的架构。他意识到他一直在“构建、构建、构建”,却没有退一步真正看看 AI 编织在一起的是什么。解决办法是一次痛苦的重构,并发誓再也不让事情失控到这种地步。我对此铭记在心。无论我使用多少 AI,我仍然是负责任的工程师。
实际上,这意味着我只有在理解代码后才会合并或发布它。如果 AI 生成了一些令人费解的东西,我会要求它添加注释来解释,或者我会用更简单的术语重写它。如果感觉不对劲,我会深入研究——就像如果人类同事贡献了引起危险信号的代码我会做的那样。
这一切都关乎心态:LLM 是助手,而不是自主可靠的编码员。我是高级开发人员;LLM 是为了加速我,而不是取代我的判断。保持这种立场不仅能产生更好的代码,还能保护你自己作为开发人员的成长。(我听到一些人表示担心,过于依赖 AI 可能会削弱他们的技能——我认为只要你保持在循环中,积极审查和理解一切,你仍然在磨练你的直觉,只是速度更快。)简而言之:保持警惕,经常测试,始终审查。 归根结底,这仍然是你的代码库。
频繁提交是你的存档点——它们让你能够撤销 AI 的失误并理解变更。
当与可以快速生成大量代码的 AI 合作时,事情很容易偏离轨道。我通过采用超细粒度的版本控制习惯来缓解这种情况。我尽早并经常提交,甚至比我正常手动编码时还要多。在每个小任务或每次成功的自动编辑之后,我会用清晰的信息进行 git 提交。这样,如果 AI 的下一个建议引入了 bug 或混乱的更改,我有一个最近的检查点可以回滚(或从中挑选),而不会损失数小时的工作。一位从业者将其比作把提交当作 “游戏中的存档点”——如果 LLM 会话出现偏差,你总是可以回滚到上一个稳定的提交。我发现这个建议非常有用。当你知道如果需要可以用 git reset 撤销时,尝试大胆的 AI 重构压力会小得多。
适当的版本控制在与 AI 协作时也有帮助。由于我不能指望 AI 记住它所做的一切(上下文窗口限制等),git 历史记录成为了宝贵的日志。我经常扫描我最近的提交来向 AI(或我自己)简要介绍发生了什么变化。事实上,如果你提供的话,LLM 本身也可以利用你的提交历史——我已经将 git diff 或提交日志粘贴到提示中,以便 AI 知道哪些代码是新的或之前的状态是什么。有趣的是,LLM 非常擅长解析 diff 并使用像 git bisect 这样的工具来查找引入 bug 的位置。它们有无限的耐心来遍历提交历史,这可以增强你的调试能力。但这只有在你一开始就有整洁的提交历史时才有效。
另一个好处:带有良好信息的小提交本质上记录了开发过程,这在进行代码审查(AI 或人工)时很有帮助。如果 AI 代理一次性进行了五处更改并且某些东西坏了,将这些更改放在单独的提交中可以更容易地查明哪个提交导致了问题。如果所有东西都在一个名为“AI 更改”的巨大提交中,祝你好运!所以我约束自己:完成任务,运行测试,提交。 这也与之前关于将工作分解为小块的提示非常吻合——每个块最终都成为它自己的提交或 PR。
最后,不要害怕使用分支或工作树来隔离 AI 实验。我采用的一个高级工作流(受到 Jesse Vincent 等人的启发)是为新功能或子项目启动一个新的 git worktree。这让我可以在同一个仓库上并行运行多个 AI 编码会话,而不会相互干扰,稍后我可以合并更改。这有点像每个 AI 任务都在自己的沙盒分支中。如果一个实验失败了,我扔掉那个工作树,main 中什么也不会丢失。如果成功了,我将其合并进来。这种方法在我,比如说,让 AI 实现功能 A,而我(或另一个 AI)同时在处理功能 B 时至关重要。版本控制使这种协调成为可能。简而言之:经常提交,用分支组织你的工作,并拥抱 git 作为控制机制,使 AI 生成的更改可管理且可逆。
通过提供风格指南、示例甚至“规则文件”来引导你的 AI 助手——一点点前期的调整会产生更好的输出。
我学到的一件事是,你不必接受 AI 的默认风格或方法——你可以通过给它指导方针来极大地影响它。例如,我有一个 CLAUDE.md 文件,我会定期更新它,其中包含 Claude(Anthropic 的模型)要遵循的流程规则和偏好(使用 Gemini CLI 时也有类似的 GEMINI.md)。这包括诸如“按照我们项目的风格编写代码,遵循我们的 lint 规则,不要使用某些函数,偏好函数式风格而非 OOP”等内容。当我开始会话时,我会将此文件提供给 Claude,使其与我们的约定保持一致。正如 Jesse Vincent 指出的那样,这在保持模型“正轨”方面效果惊人——它减少了 AI 偏离脚本或引入我们不想要的模式的倾向。
即使没有花哨的规则文件,你也可以通过自定义指令或系统提示来设定基调。GitHub Copilot 和 Cursor 都引入了允许你为你的项目全局配置 AI 行为的功能。我利用这一点写了一段关于我们编码风格的简短段落,例如“使用 4 个空格缩进,在 React 中避免箭头函数,偏好描述性变量名,代码应通过 ESLint。”有了这些指令,AI 的建议更接近人类队友可能写的内容。Ben Congdon 提到他很震惊很少有人使用 Copilot 的自定义指令,考虑到它们是多么有效——他可以通过预先提供一些示例和偏好来引导 AI 输出符合他团队习惯用法的代码。我对此表示赞同:花时间教 AI 你的期望。
另一个强大的技术是提供你想要的输出格式或方法的行内示例。如果我希望 AI 以非常特定的方式编写函数,我可能会先向它展示代码库中已有的类似函数:“这是我们实现 X 的方式,对 Y 使用类似的方法。”如果我想要某种注释风格,我可能会自己写一条注释,然后让 AI 继续这种风格。本质上,用要遵循的模式启动模型。LLM 擅长模仿——向它们展示一两个例子,它们就会沿着这条路走下去。
社区还想出了创造性的“规则集”来驯服 LLM 行为。你可能听说过 “Big Daddy” 规则或者在提示中添加“无幻觉/无欺骗”条款。这些基本上是提醒 AI 诚实并且不要过度捏造不存在的代码的技巧。例如,我有时会在提示前加上:“如果你对某事不确定或缺少代码库上下文,请要求澄清而不是编造答案。”这减少了幻觉。我使用的另一条规则是:“在修复 bug 时,总是在注释中简要解释你的推理。”这样,当 AI 生成修复时,它也会留下像“// 修复:将 X 更改为 Y 以防止 Z(根据规范)”这样的注释。这对以后的审查非常有用。
总之,不要把 AI 当作黑盒子——调整它。通过配置系统指令、共享项目文档或写下明确的规则,你将 AI 变成了团队中更专业的开发人员。这类似于入职新员工:你会给他们风格指南和一些入门提示,对吧?对你的 AI 结对程序员做同样的事情。投资回报是巨大的:你得到的输出需要更少的调整,并且更顺畅地集成到你的代码库中。
利用你的 CI/CD、linter 和代码审查机器人——AI 在自动捕捉错误的环境中工作效果最好。
这是保持在循环中并提供上下文的推论:运转良好的开发管道可以提高 AI 生产力。我确保我在其中使用大量 AI 编码的任何存储库都具有强大的持续集成设置。这意味着每次提交或 PR 都会运行自动化测试,强制执行代码风格检查(如 ESLint、Prettier 等),并且理想情况下,任何新分支都有一个暂存部署可用。为什么?因为我可以让 AI 触发这些并评估结果。例如,如果 AI 通过 Jules 或 GitHub Copilot Agent 打开拉取请求,我们的 CI 将运行测试并报告失败。我可以将这些失败日志反馈给 AI:“集成测试因 XYZ 失败,让我们调试一下。”它将 bug 修复变成了一个具有快速反馈的协作循环,AI 处理得很好(它们会建议修复,我们再次运行 CI,然后迭代)。
自动化代码质量检查(linter、类型检查器)也指导 AI。我实际上有时会在提示中包含 linter 输出。如果 AI 编写的代码没有通过我们的 linter,我会将 linter 错误复制到聊天中并说“请解决这些问题”。模型随后就知道该怎么做了。这就像有一位严厉的老师在看着 AI 的肩膀。根据我的经验,一旦 AI 意识到工具的输出(如失败的测试或 lint 警告),它就会非常努力地纠正它——毕竟,它“想要”产生正确的答案。这回到了提供上下文:给 AI 提供其在环境中的行动结果(测试失败等),它就会从中学习。
AI 编码代理本身也越来越多地包含自动化挂钩。一些代理会拒绝说代码任务已“完成”,直到所有测试通过,这正是你想要的尽职调查。代码审查机器人(AI 或其他)充当另一个过滤器——我将它们的反馈视为改进的额外提示。例如,如果 CodeRabbit 或其他审查者评论“这个函数正在做 X,这并不理想”,我会问 AI,“你能根据这个反馈进行重构吗?”
通过将 AI 与自动化相结合,你开始获得一个良性循环。AI 编写代码,自动化工具捕捉问题,AI 修复它们,依此类推,而你监督高层方向。感觉就像有一个速度极快的初级开发人员,其工作由不知疲倦的 QA 工程师即时检查。但请记住,你设置了那个环境。如果你的项目缺乏测试或任何自动化检查,AI 的工作可能会带着微妙的 bug 或低质量溜过去,直到很久以后才被发现。
因此,当我们迈向 2026 年时,我的目标之一是加强 AI 代码贡献周围的质量门槛:更多测试,更多监控,甚至可能是 AI 对 AI 的代码审查。这听起来可能有些矛盾(AI 审查 AI),但我见过它捕捉到一个模型错过的东西。底线:AI 友好的工作流是一个具有强大自动化的工作流——使用这些工具来保持 AI 诚实。
将每一次 AI 编码会话视为学习机会——你懂的越多,AI 能帮你的就越多,从而创造良性循环。
在开发中使用 LLM 最令人兴奋的方面之一是我在这个过程中学到了多少。AI 非但没有取代我了解事物的需要,反而实际上让我接触到了我可能自己不会尝试的新语言、框架和技术。
这种模式普遍成立:如果你带着扎实的软件工程基础来到桌前,AI 将成倍放大你的生产力。如果你缺乏这种基础,AI 可能只会放大困惑。经验丰富的开发者已经观察到,LLM “奖励现有的最佳实践”——诸如编写清晰的规范、拥有良好的测试、进行代码审查等,当 AI 参与其中时,所有这些都会变得更加强大。根据我的经验,AI 让我可以在更高的抽象层面上操作(专注于设计、接口、架构),而它则负责生成样板代码,但我需要首先拥有那些高层技能。正如 Simon Willison 所指出的,几乎所有使某人成为高级工程师的因素(设计系统、管理复杂性、知道什么该自动化与手动编码)现在都能通过 AI 产生最佳结果。因此,使用 AI 实际上促使我提升了我的工程水平——我对规划更加严谨,对架构更加自觉,因为我实际上是在“管理”一个非常快但有点天真的编码员(AI)。
对于那些担心使用 AI 可能会降低他们能力的人:如果是正确使用,我认为恰恰相反。通过审查 AI 代码,我接触到了新的习语和解决方案。通过调试 AI 错误,我加深了对语言和问题领域的理解。我经常要求 AI 解释它的代码或修复背后的理由——就像不断面试候选人关于他们的代码一样——并从它的回答中获得见解。我还将 AI 用作研究助手:如果我不确定某个库或方法,我会让它列举选项或比较权衡。这就像随叫随到有一位百科全书式的导师。所有这些都让我成为了一名知识更渊博的程序员。
大局是 AI 工具放大了你的专业知识。迈向 2026 年,我不害怕它们“抢走我的工作”——我很兴奋它们将我从苦差事中解放出来,让我可以将更多时间花在软件工程的创造性和复杂方面。但我也意识到,对于那些没有扎实基础的人来说,AI 可能会导致类固醇上的达克效应(它可能看起来像你构建了一些很棒的东西,直到它分崩离析)。所以我的建议是:继续磨练你的手艺,并利用 AI 加速这一过程。要有意识地定期在没有 AI 的情况下编码,以保持你的原始技能敏锐。最终,开发者 + AI 二人组比任何一方单独都要强大得多,而这二人组中的开发者那一半必须承担起他们的责任。
随着我们进入 2026 年,我已经完全在我的开发工作流中拥抱了 AI——但以一种深思熟虑、专家驱动的方式。我的方法本质上是**“AI 增强的软件工程”**,而不是 AI 自动化的软件工程。
我学到了:当你将经典的软件工程纪律应用于 AI 协作时,效果最好。事实证明,我们所有来之不易的实践——编码前设计、编写测试、使用版本控制、维护标准——不仅仍然适用,而且当 AI 编写你一半的代码时,它们甚至更重要。
我对接下来会发生什么感到兴奋。工具在不断改进,我的工作流肯定会随之演变。也许完全自主的“AI 开发实习生”将处理更多的苦力活,而我们则专注于更高层次的任务。也许会出现调试和代码探索的新范式。无论如何,我计划保持在环——指导 AI,向它们学习,并负责任地放大我的生产力。
对我来说,底线是:AI 编程助手是令人难以置信的力量倍增器,但人类工程师仍然是这场演出的导演。
以此……祝 2026 年构建愉快! 🚀
我很兴奋地分享我已经与 O’Reilly 发布了一本新的 AI 辅助工程书籍。如果有兴趣,书的网站上有许多免费提示。
2025-12-16 00:32:00
2025-12-15 08:01:00
距离写 605 - 《我怎么用 AI 编程 2025.11》 也一个多月了,不少开发方式已有变化,更新一版。