Claude Code + Python 自动生成设计文档:让 AI 替你还“意图债“(附完整代码)

TL;DR
Addy Osmani 提出了第三种技术债:意图债(Intent Debt)——代码能跑,但没人知道当初为什么这么写。AI 写代码的速度越快,意图债累积的速度也越快。本文用 Claude Code + 一个 80 行的 Python 脚本,搭建一套自动生成架构决策记录(ADR)和设计文档的流水线——每次提交代码自动附带一份"为什么这么做"的说明,从源头消除意图债。
1. 什么是意图债?
Addy Osmani 在 2026 年 6 月 5 日的博文《The Intent Debt》中定义了三种技术债:
- 代码债(Technical Debt):代码写得烂,难以维护。
- 认知债(Cognitive Debt):代码逻辑绕,新人看不懂。
- 意图债(Intent Debt):代码能跑,但当初的设计意图、约束条件、被否决的替代方案,全部丢失了。
三种债里,意图债是最隐蔽的。代码债能靠 lint、测试、重构来还;认知债能靠重命名、加注释来缓解。但意图债——那些"为什么选了 Redis 而不是 PostgreSQL 做缓存"、“为什么这个 API 设计成异步的”、“当初为什么不直接用 ORM”——这些信息一旦丢失,后来的人只能靠猜。
AI 写代码让这个问题雪上加霜。当 Claude Code 或 Copilot 替你生成了 200 行代码,你 review 通过就合入了,但生成这段代码时做的架构取舍、隐含假设,全部没有记录下来。三个月后,连你自己都不知道这段代码"为什么长这样"。
解决方案不是让 AI 写得更慢,而是让 AI 同时写一份设计文档。
2. 核心思路:每次 commit 自动产出 ADR
ADR(Architecture Decision Record,架构决策记录)是一种轻量级的设计文档格式,只有四个字段:
# 标题:做了什么事
## 背景:当时的情况是什么
## 决策:选了什么方案,为什么
## 后果:这个决策会带来什么影响
我们的方案:用 Git pre-commit hook 触发 Claude Code,自动分析即将提交的 diff,生成对应的 ADR 草稿,你 review 后一起合入仓库。
3. 环境准备
# 确保安装了 Claude Code
npm install -g @anthropic-ai/claude-code
# 创建项目
mkdir intent-debt-free && cd intent-debt-free
git init
mkdir -p docs/adr scripts
4. 核心脚本
创建 scripts/generate-adr.py:
#!/usr/bin/env python3
"""
自动生成架构决策记录(ADR)
用法: python scripts/generate-adr.py
在 git pre-commit hook 中调用,自动分析 diff 并生成 ADR 草稿
"""
import subprocess
import json
import os
import sys
from datetime import datetime
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
ADR_DIR = REPO_ROOT / "docs" / "adr"
def get_git_diff():
"""获取暂存区中代码文件的 diff"""
try:
result = subprocess.run(
["git", "diff", "--cached", "--diff-filter=AM",
"--", "*.py", "*.ts", "*.tsx", "*.js", "*.go", "*.rs",
"*.java", "*.rb", "*.yaml", "*.yml", "*.json", "*.sql"],
capture_output=True, text=True, cwd=REPO_ROOT
)
return result.stdout.strip()
except Exception as e:
print(f"获取 diff 失败: {e}", file=sys.stderr)
return ""
def get_commit_context():
"""获取当前分支名和最近的 commit 信息作为上下文"""
try:
branch = subprocess.run(
["git", "rev-parse", "--abbrev-ref", "HEAD"],
capture_output=True, text=True, cwd=REPO_ROOT
).stdout.strip()
recent_commits = subprocess.run(
["git", "log", "--oneline", "-5"],
capture_output=True, text=True, cwd=REPO_ROOT
).stdout.strip()
return f"分支: {branch}\n最近提交:\n{recent_commits}"
except Exception:
return "无上下文信息"
def generate_adr_with_claude(diff_text, context):
"""调用 Claude Code 分析 diff 并生成 ADR"""
if not diff_text:
print("没有代码变更,跳过 ADR 生成")
return None
prompt = f"""你是一个技术文档工程师。分析下面的代码变更,生成一份架构决策记录(ADR)。
格式要求(中文,简洁,每个字段不超过 5 行):
标题:[一句话概括这次变更做了什么]
背景
[变更前的状态是什么?触发了这次变更的需求或问题是什么?]
决策
[选了什么方案?为什么选这个而不是别的?简要说明被否决的替代方案]
后果
[积极影响:解决了什么问题?]
[需要注意:引入了什么新的约束或风险?]
上下文信息:
{context}
代码变更(git diff):
{diff_text}
只输出 ADR 内容,不要加额外的解释。"""
try:
result = subprocess.run(
["claude", "--print", "--output-format", "text", prompt],
capture_output=True, text=True, cwd=REPO_ROOT,
timeout=120 # 2 分钟超时
)
return result.stdout.strip()
except subprocess.TimeoutExpired:
print("Claude Code 超时,diff 可能太大,尝试缩小提交范围", file=sys.stderr)
return None
except Exception as e:
print(f"Claude Code 调用失败: {e}", file=sys.stderr)
return None
def save_adr(adr_content):
"""保存 ADR 到文件"""
ADR_DIR.mkdir(parents=True, exist_ok=True)
date_str = datetime.now().strftime("%Y-%m-%d")
# 从标题提取文件名
first_line = adr_content.strip().split("\n")[0]
slug = first_line.replace("# 标题:", "").replace("# ", "").strip()
slug = "".join(c if c.isalnum() or c in "_- " else "" for c in slug)
slug = slug.lower().replace(" ", "-")[:60]
filename = f"{date_str}-{slug}.md"
filepath = ADR_DIR / filename
with open(filepath, "w", encoding="utf-8") as f:
f.write(f"# {first_line.replace('# ', '').replace('#', '').strip()}\n\n")
f.write(f"日期: {date_str}\n")
f.write(f"状态: 提议中\n\n")
f.write(adr_content)
print(f"ADR 已生成: {filepath}")
return str(filepath)
if __name__ == "__main__":
diff = get_git_diff()
context = get_commit_context()
adr = generate_adr_with_claude(diff, context)
if adr:
path = save_adr(adr)
print(f"\n请 review 后决定是否合入:{path}")
print("提示:如果 ADR 内容不准确,可以直接编辑该文件。")
else:
print("未生成 ADR(无变更或调用失败)")
5. 接入 Git Hook
创建 .git/hooks/pre-commit(或使用 husky 等工具管理):
#!/bin/bash
# 每次 commit 前自动生成 ADR
echo "分析变更并生成架构决策记录..."
python3 scripts/generate-adr.py
# 如果 ADR 文件被创建,自动加入暂存区
ADR_FILE=$(ls -t docs/adr/*.md 2>/dev/null | head -1)
if [ -n "$ADR_FILE" ]; then
git add "$ADR_FILE"
echo "ADR 已暂存: $ADR_FILE"
fi
# 不阻塞提交——ADR 是辅助性的,不通过就阻止提交会破坏工作流
exit 0
chmod +x .git/hooks/pre-commit
6. 更进一步:用 CLAUDE.md 做项目级意图基线
除了每次提交自动生成 ADR,还有一个更"治本"的做法:维护一份项目级的 CLAUDE.md(或 AGENTS.md),作为所有 AI 工具共享的意图基线。
创建 CLAUDE.md:
# 项目意图基线
## 架构决策
- 选择 FastAPI 而非 Django:团队对异步有强需求,Django 的 async 支持当时还不成熟
- Redis 做缓存层:访问频率远高于数据变更频率,用 Redis 比数据库物化视图更灵活
- 放弃微服务:团队 3 人,微服务的运维成本远超收益
## 技术约束
- Python 版本:≥ 3.11,因为用了 match-case 语法
- 部署环境:AWS ECS Fargate,不能用需要持久化本地存储的方案
- 第三方 API 限速:OpenAI API 每分钟 500 次,所有批量操作必须有 rate limiter
## 已否决的方案
- 不用 Celery:太重,用 Redis Queue (RQ) 足够
- 不用 Kubernetes:团队没有 K8s 运维经验,ECS 够用
这份 CLAUDE.md 有两个作用:一是每次 Claude Code 启动时自动加载,确保它生成代码时尊重已有的架构决策;二是它本身就是一份"意图文档",新成员入职第一件事就是读它。
7. 踩坑与最佳实践
坑 1:diff 太大导致 ADR 质量下降
一次提交改了 20 个文件、500 行,Claude Code 很难生成精准的 ADR。解决方案:养成小步提交的习惯。如果你在用 Claude Code 写代码,可以要求它"每完成一个独立功能就提示我 commit"。
坑 2:ADR 文件越来越多,管理混乱
建议建一个 docs/adr/INDEX.md,按时间倒序列出所有 ADR,并给每份 ADR 加状态标签(提议中 / 已采纳 / 已废弃)。可以用一个简单脚本自动更新索引。
坑 3:不要用 ADR 替代真正的设计讨论
ADR 是记录工具,不是决策工具。如果一个架构决策涉及多个团队或需要权衡,先在文档或会议上讨论清楚,再用 ADR 记录结论,不要反着来。
坑 4:CI 成本
每次 commit 都调用 Claude Code 会增加 API 开销。如果你的提交频率很高(一天几十次),建议改成只在 push 时生成(用 pre-push hook),或者在 CI 中只在合并到 main 分支时触发。
8. 效果检验
搭好这套系统一个月后,你应该能看到这些变化:
- 每个 PR 都附带一份 ADR,reviewer 能快速理解"为什么这么改"
- 新人入职时,读
docs/adr/目录就能了解项目的关键架构决策 - 当你半年后回来看一段代码,不需要在 Slack 历史里翻当时的讨论——ADR 文件就在代码旁边
意图债不会因为你"注意了"就消失。它需要系统化的机制来对抗。这套脚本的价值不是 AI 写得有多好,而是它让你每次提交都被提醒:“这段代码的意图,我记下来了吗?”
参考资料
更多推荐

所有评论(0)