首页 › tutorials › 构建代码变更影响面分析Skill

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

概述

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

前置条件

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

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

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

第二步:搭起 Skill 骨架

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

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

创建 SKILL.md,先只写最关键的 frontmatter:

---
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 里抽出被改动的符号名:

#!/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,给定一个符号名,找出仓库里所有引用它的位置(即「影响扩散」的第一跳):

#!/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 友好的参数校验。模型读起来顺,人调试也顺。

赋予执行权限:

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 的完整正文:

# 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.

三个值得注意的设计:

第五步:把重资料放进 references,按需加载

创建 references/risk-rubric.md:

# Risk Classification Rubric

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

判定规则:任一维度命中 High 即整体 High;否则取最高维度。

创建 references/report-template.md:

# 影响面报告

## 直接改动({{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 末尾补一段:

## 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 写完,分两层验证。

层一:脚本单测(确定性逻辑必须可回归):

# 准备一个迷你 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 写得好不好):

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 跑影响面分析

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

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 想要的第一屏信息。

整体工作流程

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 分发。

延伸阅读

训练营适用性评估

参考来源

  1. Steering Claude Code: skills, hooks, subagents and more — Anthropic 官方对 Skills 等七大定制能力的总览说明
  2. Advanced: Progressive Disclosure, Bundled Scripts, and Subagent — ${CLAUDE_SKILL_DIR}、脚本封装与 subagent 编排的官方进阶写法
  3. Claude Code Skills Complete Guide (2026) — SKILL.md 结构、MCP、Subagents 与团队协作全景指南
  4. Choosing between skills, subagents, and MCP servers in Claude Code — 三种扩展机制的决策矩阵
  5. Claude Code Customization Guide: CLAUDE.md vs Skills vs Subagents — 定制手段的边界与组合实践
  6. claude-code-docs: 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学通信知识星球海报-扫码加入