<!-- title: 构建代码变更影响面分析Skill -->

# 从零构建「代码变更影响面分析」Skill：让每次改动的影响一目了然

## 概述

当你提交一个 Pull Request 时，最常被问到的一句话是：「这次改动会影响哪些功能？」人工回答这个问题既慢又容易遗漏。本教程带你从零构建一个 Claude Code Skill——`code-impact-analyzer`，它能自动解析 git diff，沿着调用链与依赖关系扩散，输出一份「受影响文件 / 函数 / 测试 / 风险等级」的结构化报告。你将完整掌握 Skill 的三要素：**SKILL.md 元数据触发、渐进式披露正文、脚本封装**，并学会用 subagent 并行加速分析。适合已跑通 Claude Code、想从「会写 Skill」进阶到「能写出工业级 Skill」的开发者。

## 前置条件

- Claude Code CLI 已安装并能正常对话（`claude --version` 有输出）
- 一个真实的 Git 仓库用来跑实战案例（任何语言均可，本教程以 Python/TS 为例）
- 本机装有 `ripgrep`（`rg`）、`git`、Node.js 18+ 或 Python 3.10+（脚本二选一）
- 了解 YAML frontmatter 的基本语法

## 第一步：把「影响面分析」拆成一个 Skill 任务

写 Skill 之前，先用一句话定义它的**输入、动作、输出**，这决定了后续所有设计：

- **输入**：当前工作区的 git diff（默认 `HEAD` 与目标分支的差，或两个 commit）
- **动作**：① 提取改动触及的符号（函数 / 类 / 模块）；② 沿调用关系与导入关系向外扩散一跳；③ 标记与这些符号相关的测试文件；④ 给出风险评级
- **输出**：一份 Markdown 影响面报告，含「直接改动 / 间接影响 / 关联测试 / 风险摘要」四段

> 为什么用 Skill 而不是写个普通脚本？因为「沿调用链扩散」这一步需要语言语义理解，纯正则会漏；而让模型来判断「谁调用了被改的函数」时，又需要先把仓库相关文件喂给它——这正是 Skill「按需加载上下文」机制的用武之地。脚本能精确做到的（grep 导入、解析符号）交给脚本，需要语义的（判断调用关系、评级）交给模型。

## 第二步：搭起 Skill 骨架

Skill 就是一个带 `SKILL.md` 的目录。个人级放在 `~/.claude/skills/`，项目级放在 `.claude/skills/`。我们用项目级，因为它跟着仓库走、可被团队共享：

```bash
mkdir -p .claude/skills/code-impact-analyzer/{scripts,references}
cd .claude/skills/code-impact-analyzer
```

创建 `SKILL.md`，先只写最关键的 frontmatter：

```yaml
---
name: code-impact-analyzer
description: Analyzes the blast radius of a git diff — extracts changed symbols,
  expands one hop along call/import edges, flags related tests, and writes a
  structured impact report. Use when the user asks "what does this change
  affect", before opening a PR, during code review, or when sizing a refactor.
---
```

**这一小段 frontmatter 就是 Skill 的全部「常驻开销」。** Claude 在每个会话里只会看到所有 Skill 的 `name + description`（即 Level 1 加载），看不到正文。所以 `description` 是 Skill 的「触发广告位」，必须同时包含：

1. **What**：它做什么（blast radius / impact report）
2. **When**：什么时候该被触发（"what does this change affect"、开 PR 前、code review、重构估时）

写好这两点，模型才能在用户说「这个改动稳吗」时自动联想到你的 Skill。

**预期结果**：在仓库里执行 `claude`，输入「分析一下我当前改动的影响」，模型应该能自动加载本 Skill。如果没触发，多半是 `description` 里缺了用户真实会说的话术。

## 第三步：把「能精确做的事」交给脚本

Skill 设计的黄金法则：**凡是确定性逻辑，封装成脚本让模型调用，而不是让模型现场推理。** 这能省 token、提准确率、可回归测试。

创建 `scripts/extract-symbols.sh`，它负责从 diff 里抽出被改动的符号名：

```bash
#!/usr/bin/env bash
# 从 git diff 中提取被改动的函数/方法/类符号名
# 输入: 通过 stdin 传入 diff，或读取第一个参数指向的 diff 文件
set -euo pipefail

if [[ $# -ge 1 ]]; then
  diff_text="$(cat "$1")"
else
  diff_text="$(cat)"
fi

# 提取 hunk 中新增/删除行，再用启发式匹配符号定义
echo "$diff_text" \
  | grep -E '^\+' \
  | grep -oE '(def|function|func|class|async def|public|private|protected)\s+[A-Za-z0-9_]+' \
  | awk '{print $NF}' \
  | sort -u
```

创建 `scripts/find-references.sh`，给定一个符号名，找出仓库里所有引用它的位置（即「影响扩散」的第一跳）：

```bash
#!/usr/bin/env bash
# 用法: find-references.sh <symbol> [search_root]
# 依赖: ripgrep (rg)
# depends: ripgrep
set -euo pipefail

symbol="${1:?usage: find-references.sh <symbol>}"
root="${2:-.}"

rg --no-heading -n "\b${symbol}\b" "$root" || true
```

两个关键细节：

1. **`# depends: ripgrep` 这种「依赖声明」写在脚本头部**，模型在调用前会检查依赖是否满足，不满足会先帮你装或换方案。这是 Claude Code 对 Skill 脚本的约定。
2. **脚本要「agent 友好」**：用 `set -euo pipefail` 严格模式、清晰的标准输入/输出、可机器解析的结果（`file:line:content`）、`--help` 友好的参数校验。模型读起来顺，人调试也顺。

赋予执行权限：

```bash
chmod +x scripts/*.sh
```

**预期结果**：`echo "def foo():" | ./scripts/extract-symbols.sh` 输出 `foo`；`./scripts/find-references.sh foo src/` 列出所有引用 `foo` 的行。

## 第四步：用「渐进式披露」组织 SKILL.md 正文

frontmatter 之下，开始写正文。**正文不要一次性堆满所有细节**——那是反模式，会让模型在被触发时被迫读一大段用不上的内容。正确做法是把 SKILL.md 写成「目录 + 操作步骤」，重资料放进 `references/` 按需加载。

这是 `SKILL.md` 的完整正文：

```markdown
# Code Impact Analyzer

You are performing a change impact (blast radius) analysis on the current git
working tree. Produce a report that lets a reviewer answer "what does this
change touch?" in under 30 seconds.

## Procedure

1. **Get the diff.** Default to `git diff $(git merge-base HEAD main)` to compare
   against the main branch. If the user names two commits/branches, use those.
   Save it to a temp file: `git diff <base> > /tmp/impact.diff`.

2. **Extract changed symbols.** Run:
   `${CLAUDE_SKILL_DIR}/scripts/extract-symbols.sh /tmp/impact.diff`
   Keep the unique symbol list as `CHANGED`.

3. **Expand one hop.** For each symbol in CHANGED, run:
   `${CLAUDE_SKILL_DIR}/scripts/find-references.sh <symbol> <repo-root>`
   Collect non-test files into `AFFECTED_PROD`, test files into `RELATED_TESTS`.
   De-duplicate. Stop at ONE hop — do not recurse, it inflates the report.

4. **Classify risk.** Score each AFFECTED_PROD file:
   - High: public API / entry point / shared utility touched
   - Medium: internal module with >5 callers
   - Low: leaf or test-only change
   See `references/risk-rubric.md` for the full rubric if a case is ambiguous.

5. **Write the report** to `IMPACT-REPORT.md` in the repo root and also summarize
   inline. Use the four-section template in `references/report-template.md`.

## Output sections (in order)
直接改动 | 间接影响（一跳） | 关联测试 | 风险摘要

## Guardrails
- Never claim a file is affected without a grep hit from step 3.
- If the diff is empty, say so and stop — do not invent impact.
- Cap the report at 40 affected files; if more, summarize and warn.
```

三个值得注意的设计：

- **`${CLAUDE_SKILL_DIR}`** 是 Skill 专用的环境变量，永远指向当前 Skill 目录。脚本用它来定位，比写死路径更可移植。
- 正文里**只放「做什么」的步骤**，把「怎么做评级」「报告长什么样」这种重资料拆到 `references/`。模型只在需要时（步骤 4 遇到模糊情况、步骤 5 写报告时）才读它们——这就是 Level 3 的按需加载，能省下大量 token。
- **Guardrails（护栏）** 段落是高阶技巧：明确告诉模型「不能做什么」（不许凭空声称影响、空 diff 要停、上限 40 文件）。这能把 Skill 从「能跑」提升到「可信任」。

## 第五步：把重资料放进 references，按需加载

创建 `references/risk-rubric.md`：

```markdown
# Risk Classification Rubric

| 维度 | High | Medium | Low |
|------|------|--------|-----|
| 暴露面 | 公开 API / 路由入口 / SDK 导出 | 内部服务模块 | 叶子函数 |
| 调用方数量 | >10 | 2-10 | 0-1 |
| 是否有测试覆盖 | 无 | 部分 | 完整 |
| 并发/状态 | 改了共享状态 | 改了纯逻辑 | 改了常量/注释 |

判定规则：任一维度命中 High 即整体 High；否则取最高维度。
```

创建 `references/report-template.md`：

```markdown
# 影响面报告

## 直接改动（{{count}} 个文件）
- `path/to/file.py` — 新增 `foo()`，删除 `bar()`

## 间接影响（一跳，{{count}} 个文件）
- `src/api/handler.ts:42` — 调用 `foo`
- `src/utils/helper.py:88` — 导入 `foo`

## 关联测试（{{count}} 个）
- `tests/test_foo.py` — 建议优先重跑

## 风险摘要
整体风险：**High/Medium/Low**。最需关注：`<file>`，原因：`<one line>`。
建议回归范围：`<test command>`。
```

这些文件**不会**默认进上下文。只有当模型执行到「评分」或「写报告」时才会主动读——这正是「98% Token 节省」架构的精髓：常驻的只是元数据，重资料延迟到真正需要的那一刻。

## 第六步：用 subagent 并行加速「一跳扩散」

当 `CHANGED` 符号很多时，串行 grep 会慢。Claude Code 允许 Skill 在正文里指示模型**派发 subagent 并行处理**。在 `SKILL.md` 末尾补一段：

```markdown
## Scaling up
If CHANGED contains more than 8 symbols, do NOT call find-references.sh serially.
Instead, spawn one subagent per batch of symbols, each running the script on its
batch and returning a deduped JSON array of {symbol, file, line}. Merge results.
This keeps wall-clock under 20s even for large diffs.
```

并行 subagent 的收益是双重的：**速度快**（多符号同时扩散），**上下文干净**（每个 subagent 在自己的上下文里读 grep 结果，主对话只收回精简的 JSON，不会被海量 `file:line` 行撑爆）。

## 第七步：本地测试与触发验证

Skill 写完，分两层验证。

**层一：脚本单测**（确定性逻辑必须可回归）：

```bash
# 准备一个迷你 diff
printf 'def process_order():\n    return None\n' > /tmp/sample.diff
./scripts/extract-symbols.sh /tmp/sample.diff
# 期望输出: process_order

./scripts/find-references.sh process_order src/ | head
# 期望输出: src/checkout/service.py:55:    result = process_order()
```

**层二：端到端触发**（验证 description 写得好不好）：

```bash
cd <你的仓库>
# 人为制造一个改动
git checkout -b feat/demo
echo "def new_endpoint(): pass" >> src/api.py
git add src/api.py

claude
# 在对话里输入这些「自然话术」，看 Skill 是否被自动加载：
# 1. "这次改动会影响哪些功能？"        ← 应触发
# 2. "帮我看看这个 PR 的 blast radius"  ← 应触发
# 3. "评估一下重构 auth 模块的工作量"    ← 应触发
# 4. "重命名这个变量"                   ← 不该触发（与影响面无关）
```

**预期结果**：前三句应看到 Skill 被加载（界面会提示 `loaded code-impact-analyzer`），并最终在仓库根目录生成 `IMPACT-REPORT.md`。第四句不应触发——如果触发了，说明 `description` 里 `refactor` 这个词太宽泛，需要收窄。

## 实战案例：对一个真实 PR 跑影响面分析

模拟一次「改了底层工具函数」的危险改动：

```bash
git checkout -b feat/touch-shared-util
# 改动一个被广泛调用的工具函数
cat >> src/utils/format.py <<'EOF'

def format_currency(amount: float) -> str:
    # 行为变更：原来返回 "$1.00"，现在返回 "USD 1.00"
    return f"USD {amount:.2f}"
EOF
git add src/utils/format.py
```

在 Claude Code 里说：「分析这次改动的影响面」。模型会：

1. 调 `git diff main` 拿到改动，发现符号 `format_currency`
2. 调 `find-references.sh format_currency src/`，发现 `invoice.py`、`receipt.py`、`report.py` 等 12 处调用
3. 把 `test_format.py`、`test_invoice.py` 归入关联测试
4. 因为是「被 12 处调用的工具函数 + 无对应测试命中行为变更」，评级 **High**
5. 生成 `IMPACT-REPORT.md`，建议重跑 `pytest tests/test_invoice.py tests/test_receipt.py`

这份报告直接能贴进 PR description——而这正是 reviewer 想要的第一屏信息。

## 整体工作流程

```mermaid
flowchart TD
    U[用户: 这次改动影响哪些功能?] -->|description 命中| L1[Level1: 仅 name+description 常驻]
    L1 --> L2[Level2: 加载 SKILL.md 正文]
    L2 --> S1[脚本: git diff]
    S1 --> S2[脚本: extract-symbols.sh]
    S2 --> SYM[CHANGED 符号列表]
    SYM -->|符号>8| PARA[并行 subagent 批量扩散]
    SYM -->|符号<=8| SERIAL[串行 find-references.sh]
    PARA --> HIT[命中文件:行 + 测试]
    SERIAL --> HIT
    HIT --> L3A[Level3 按需读 risk-rubric.md 评级]
    L3A --> L3B[Level3 按需读 report-template.md]
    L3B --> R[生成 IMPACT-REPORT.md]
    R --> PR[贴入 PR description]
```

三层加载的层次一眼可见：`L1/L2` 是 Skill 框架的常驻与按需，`L3A/L3B` 是 references 的延迟加载，脚本与 subagent 则承担确定性工作与并行加速。

## 常见问题与解决方案

**Q1：Skill 没被触发，模型直接自己 grep 了。**
`description` 没覆盖用户真实话术。把用户最近一周问过的原话逐条写进 `When` 子句。避免用「analyze」「handle」这类模型已有强先验的泛词。

**Q2：脚本里 `${CLAUDE_SKILL_DIR}` 没展开。**
它只在模型**调用脚本时**由 Skill 运行时注入，你在终端手动 `bash` 跑不会展开。手动调试时用 `CLAUDE_SKILL_DIR="$PWD" bash scripts/find-references.sh …` 临时模拟。

**Q3：报告说某个文件受影响，但其实只是注释里提到了符号。**
`find-references.sh` 用的是词边界正则，会误伤字符串/注释。可在脚本里加 `--type-not` 排除，或在 SKILL.md 的 Guardrails 里要求「调用点需在函数体内」，让模型做语义二次确认。

**Q4：大仓库 grep 太慢。**
让 subagent 并行（第五步），并给 `rg` 加 `--max-count 50` 限制每个文件的命中数。Skill 设计要始终考虑「仓库规模放大 10 倍后还能跑」。

**Q5：团队里别人怎么用上这个 Skill？**
项目级 Skill（`.claude/skills/`）跟着 git 走，提交后队友 pull 即得。若想跨项目复用，放 `~/.claude/skills/`，或打包成 Plugin 通过 marketplace 分发。

## 延伸阅读

- 官方 Steering 指南：`https://claude.com/blog/steering-claude-code-skills-hooks-rules-subagents-and-more` — Skills、Hooks、Subagents、Rules 七大定制手段的总览
- Skills 进阶文档：`https://claude-with-skills.vercel.app/03-advanced/` — `${CLAUDE_SKILL_DIR}`、bundled scripts、subagent 编排的官方写法
- Claude Code Skills 完整指南：`https://duet.so/guides/claude-code-skills-complete-guide` — SKILL.md、MCP、Subagents、团队协作全景
- 选择 Skills vs Subagents vs MCP：`https://smithhorngroup.substack.com/p/choosing-between-skills-subagents` — 决策矩阵，帮你判断「这个需求到底该用哪种机制」
- CLAUDE.md vs Skills vs Subagents 决策：`https://alexop.dev/posts/claude-code-customization-guide-claudemd-skills-subagents/` — 三者边界与组合用法

## 训练营适用性评估

- **适合训练营类型**：中高级开发者训练营、工程效能（DevEx）主题、Code Review 自动化专题。不适合纯零基础——需要先掌握「首个 Skill 跑通」。
- **预计教学时长**：2.5–3 小时。其中机制讲解 30 分钟、动手搭建（第一到四步）90 分钟、脚本调试与触发验证 45 分钟、实战案例与讨论 30 分钟。
- **教学节奏建议**：先让学员用「裸 prompt」手算一次影响面（感受痛点）→ 再引入 Skill → 最后对比 token 消耗与准确率，体会渐进式披露的价值。
- **可考核点**：① description 能否被自然话术触发；② 脚本是否 agent 友好（依赖声明、stdin/stdout、严格模式）；③ references 是否做到真正按需加载；④ 是否有 Guardrails 防止幻觉。
- **延伸作业**：把「一跳扩散」改成「可配置跳数」，或新增「关联文档/Runbook」一段，让影响面报告覆盖运维场景。

## 参考来源

1. [Steering Claude Code: skills, hooks, subagents and more](https://claude.com/blog/steering-claude-code-skills-hooks-rules-subagents-and-more) — Anthropic 官方对 Skills 等七大定制能力的总览说明
2. [Advanced: Progressive Disclosure, Bundled Scripts, and Subagent](https://claude-with-skills.vercel.app/03-advanced/) — `${CLAUDE_SKILL_DIR}`、脚本封装与 subagent 编排的官方进阶写法
3. [Claude Code Skills Complete Guide (2026)](https://duet.so/guides/claude-code-skills-complete-guide) — SKILL.md 结构、MCP、Subagents 与团队协作全景指南
4. [Choosing between skills, subagents, and MCP servers in Claude Code](https://smithhorngroup.substack.com/p/choosing-between-skills-subagents) — 三种扩展机制的决策矩阵
5. [Claude Code Customization Guide: CLAUDE.md vs Skills vs Subagents](https://alexop.dev/posts/claude-code-customization-guide-claudemd-skills-subagents/) — 定制手段的边界与组合实践
6. [claude-code-docs: claude-code-skills.md](https://github.com/gregpriday/claude-code-docs/blob/main/claude-code-skills.md) — 社区维护的三层渐进式披露架构详解

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
