Karpathy 的 Claude Code 编码指南

基于 Andrej Karpathy 对 LLM 编码陷阱的观察,提炼出的四大编码原则:编码前思考、简单优先、精确修改、目标驱动执行。

LLM 编码的常见问题

Karpathy-Inspired Claude Code Guidelines 是一个基于 Andrej Karpathy 观察的编码指南项目。Karpathy 在使用 LLM 编码时发现了几个关键问题:

"模型会在你不知情的情况下做出错误的假设,然后继续沿着错误的方向走下去。它们不管理自己的困惑,不寻求澄清,不暴露不一致性,不展示权衡,不在此时推回。"

"它们真的喜欢过度复杂化代码和 API,膨胀抽象层,不清理死代码...用 1000 行代码实现一个臃肿的结构,而 100 行就够了。"

"它们有时仍然会更改/删除不充分理解的注释和代码作为副作用,即使这些更改与任务正交。"

这个项目通过一个单一的 CLAUDE.md 文件来解决这些问题,提炼出四个核心编码原则。

四大核心原则

这四个原则直接解决了上述问题:

原则 解决的问题
Think Before Coding 错误假设、隐藏的困惑、缺失的权衡
Simplicity First 过度复杂化、膨胀的抽象层
Surgical Changes 正交编辑、触及不该碰的代码
Goal-Driven Execution 通过测试优先、可验证的成功标准获得杠杆

原则一:编码前思考

不要假设。不要隐藏困惑。展示权衡。

LLM 经常会默默地选择一种解释然后继续执行。这个原则强制进行显式推理:

  • 显式陈述假设 — 如果不确定,询问而不是猜测
  • 展示多种解释 — 当存在歧义时,不要默默地选择
  • 在有必要时推回 — 如果有更简单的方法,说出来
  • 困惑时停止 — 说出不清楚的地方并请求澄清

实践建议:在开始编码之前,先花时间理解需求。如果有任何不确定的地方,立即提问。这比写完代码后再返工要高效得多。

原则二:简单优先

解决问题的最小代码量。没有投机性的代码。

对抗过度工程化的倾向:

  • 不要添加超出要求的功能
  • 不要为单次使用的代码创建抽象层
  • 不要添加未要求的"灵活性"或"可配置性"
  • 不要为不可能的场景添加错误处理
  • 如果 200 行可以缩减为 50 行,重写它

测试标准:一位高级工程师会说这过于复杂吗?如果是,简化它。

原则三:精确修改

只碰你必须碰的。只清理你自己的烂摊子。

编辑现有代码时:

  • 不要"改进"相邻的代码、注释或格式
  • 不要重构没有坏掉的东西
  • 匹配现有风格,即使你会用不同的方式写
  • 如果注意到不相关的死代码,提及它 — 但不要删除它

当你的更改创建孤儿代码时:

  • 删除你的更改导致的未使用的导入/变量/函数
  • 不要删除预先存在的死代码,除非被要求

测试标准:每一行更改都应该直接追溯到用户的请求。

原则四:目标驱动执行

定义成功标准。循环直到验证。

将指令性任务转换为可验证的目标:

不要这样做... 转换为...
"添加验证" "为无效输入编写测试,然后让它们通过"
"修复这个 bug" "编写一个重现它的测试,然后让它通过"
"重构 X" "确保测试在重构前后都通过"

对于多步骤任务,陈述一个简短的计划:

1. [步骤] → 验证: [检查]
2. [步骤] → 验证: [检查]
3. [步骤] → 验证: [检查]

强大的成功标准让 LLM 能够独立循环。弱标准("让它工作")需要持续澄清。

注意:这些指南偏向于谨慎而非速度。对于琐碎的任务(简单的拼写修复、明显的单行代码),使用判断 — 不是每个更改都需要完整严格性。

CLAUDE.md 完整内容

以下是该项目的核心文件 CLAUDE.md 的完整内容,你可以直接复制使用:

# CLAUDE.md

Behavioral guidelines to reduce common LLM coding mistakes.
Merge with project-specific instructions as needed.

**Tradeoff:** These guidelines bias toward caution over speed.
For trivial tasks, use judgment.

## 1. Think Before Coding

**Don't assume. Don't hide confusion. Surface tradeoffs.**

Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.

## 2. Simplicity First

**Minimum code that solves the problem. Nothing speculative.**

- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.

Ask yourself: "Would a senior engineer say this is overcomplicated?"
If yes, simplify.

## 3. Surgical Changes

**Touch only what you must. Clean up only your own mess.**

When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.

When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.

The test: Every changed line should trace directly to the user's request.

## 4. Goal-Driven Execution

**Define success criteria. Loop until verified.**

Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"

For multi-step tasks, state a brief plan:
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]

Strong success criteria let you loop independently.
Weak criteria ("make it work") require constant clarification.

---

**These guidelines are working if:** fewer unnecessary changes in diffs,
fewer rewrites due to overcomplication, and clarifying questions come
before implementation rather than after mistakes.

实战案例:LLM 常见错误 vs 正确做法

以下是项目中 EXAMPLES.md 提供的真实代码对比案例,清晰展示了 LLM 在编码中容易犯的错误。

案例 1:隐藏假设(Think Before Coding)

用户请求:"添加导出用户数据的功能"

LLM 的错误做法:默默假设导出所有用户、假设文件位置、假设字段列表,直接写出完整的 JSON/CSV 导出函数。

正确做法:先列出假设并请求澄清:

Before implementing, I need to clarify:

1. **Scope**: Export all users or filtered subset? (privacy implications)
2. **Format**: You mentioned "export" - did you mean:
   - Download file in browser?
   - Background job with email notification?
   - API endpoint returning data?
3. **Fields**: Which user fields? (some might be sensitive)
4. **Volume**: How many users typically? (affects approach)

Simplest approach: Add an API endpoint that returns paginated JSON.
What's your preference?

案例 2:过度抽象(Simplicity First)

用户请求:"添加一个计算折扣的函数"

LLM 的错误做法:创建策略模式(Strategy Pattern),抽象基类 DiscountStrategy,枚举类型,数据类 DiscountConfig,完整的计算器类——30+ 行代码实现一个简单的乘法。

正确做法:

def calculate_discount(amount: float, percent: float) -> float:
    """Calculate discount amount. percent should be 0-100."""
    return amount * (percent / 100)

# Usage
discount = calculate_discount(100.0, 10.0)  # $10 off

何时添加复杂性:只有当你确实需要多种折扣类型时。如果需求后来才出现,到时候再重构。

案例 3:顺带重构(Surgical Changes)

用户请求:"修复空邮箱导致验证器崩溃的 bug"

LLM 的错误做法:修复 bug 的同时,还"改进"了邮箱验证逻辑、添加了用户名验证、改了注释、加了 docstring——大量与 bug 无关的改动。

正确做法:只修改处理空邮箱的那几行代码,其余保持不变。

关键原则:每一行更改都应该能追溯到用户的请求。不要"顺便"改任何东西。

案例 4:模糊目标 vs 可验证目标(Goal-Driven Execution)

用户请求:"修复认证系统"

LLM 的错误做法:"我会审查代码、识别问题、进行改进、测试更改"——没有明确的成功标准。

正确做法:定义可验证的目标:

如果问题是"密码修改后用户仍保持登录状态":

Plan:
1. 写测试:修改密码 → 验证旧会话失效
   Verify: 测试失败(重现 bug)

2. 实现:密码修改时使会话失效
   Verify: 测试通过

3. 检查边缘情况:多个活跃会话、并发修改
   Verify: 附加测试通过

4. 验证无回归:现有认证测试仍然通过
   Verify: 完整测试套件绿色

反模式总结

原则 反模式 修复
Think Before Coding 默默假设文件格式、字段、范围 列出假设,请求澄清
Simplicity First 为单个折扣计算创建策略模式 一个函数,直到复杂性确实需要
Surgical Changes 修复 bug 时改了引号风格、加了类型提示 只改修复报告问题所需的行
Goal-Driven "我会审查并改进代码" "写测试重现 bug X → 让它通过 → 验证无回归"

核心洞察:"过度复杂化"的例子并非明显错误——它们遵循了设计模式和最佳实践。问题在于时机:在需要之前就添加了复杂性。好的代码是简单地解决今天问题的代码,而不是过早地解决明天的问题。

安装方法

方法 A:Claude Code 插件(推荐)

在 Claude Code 中,首先添加市场:

/plugin marketplace add forrestchang/andrej-karpathy-skills

然后安装插件:

/plugin install andrej-karpathy-skills@karpathy-skills

这会将指南安装为 Claude Code 插件,使其在所有项目中可用。

方法 B:CLAUDE.md(每个项目)

新项目:

curl -o CLAUDE.md https://raw.githubusercontent.com/forrestchang/andrej-karpathy-skills/main/CLAUDE.md

现有项目(追加):

echo "" >> CLAUDE.md
curl https://raw.githubusercontent.com/forrestchang/andrej-karpathy-skills/main/CLAUDE.md >> CLAUDE.md

使用 Cursor

该仓库包含一个 Cursor 项目规则文件(.cursor/rules/karpathy-guidelines.mdc),因此相同的指南在 Cursor 中也适用。详见 CURSOR.md

关键洞察

Karpathy 的核心洞察:"LLM 非常擅长循环直到达到特定目标...不要告诉它做什么,给它成功标准然后看它执行。"

"目标驱动执行"原则捕捉了这一点:将指令性说明转换为带有验证循环的声明性目标。

如何知道它在工作

如果你看到以下迹象,说明这些指南正在发挥作用:

  • diff 中更少不必要的更改 — 只有请求的更改出现
  • 更少因过度复杂化而重写 — 代码第一次就是简单的
  • 澄清问题在实现之前出现 — 而不是在错误之后
  • 干净、最小的 PR — 没有顺带重构或"改进"

自定义指南

这些指南设计为与项目特定指令合并。将它们添加到现有的 CLAUDE.md 或创建一个新的。

对于项目特定规则,添加如下部分:

## 项目特定指南

- 使用 TypeScript 严格模式
- 所有 API 端点必须有测试
- 遵循 `src/utils/errors.ts` 中现有的错误处理模式

相关资源

总结

Karpathy 的编码指南代表了一种与 LLM 协作的新范式。它不是让 AI 自由发挥,而是通过明确的原则和成功标准来引导 AI 的行为。

这个项目的启示是:与其告诉 AI 做什么,不如告诉它成功的标准是什么。这种"声明式"的指令方式让 AI 能够独立循环验证,直到达到目标。

如果你在使用 Claude Code 或其他 LLM 编码工具,这些指南值得一试。它们可能会显著减少不必要的代码更改、过度工程化和返工。

提示:这些原则不仅适用于 LLM 编码,对于人类开发者同样有价值。它们代表了良好的软件工程实践。