请添加图片描述

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 写得有多好,而是它让你每次提交都被提醒:“这段代码的意图,我记下来了吗?”

参考资料

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐