首页 › tutorials › 造变更影响分析Skill:精准定位改动风险面

造变更影响分析Skill:精准定位改动风险面

概述:本教程带你从零构建一个「变更影响分析」Skill,让 Claude Code 在你提交代码改动时,自动解析 git diff、反向追踪符号引用、定位需要回归的测试,并产出带 Mermaid 依赖图的风险报告。适合掌握基础 Skill 写法、想进阶学习「脚本 Skill + 渐进式披露」分层架构的开发者。

前置条件

为什么需要这个 Skill

改一行函数签名,到底会波及哪些文件?靠人脑记忆依赖关系在大型代码库里几乎不可能。每次 PR review 都要回答三个问题:改了什么、影响了什么、要补哪些测试。「变更影响分析」(Change Impact Analysis,社区俗称 Blast Radius)就是用静态追踪的方式,把改动的「爆炸半径」量化出来。

把这套流程固化成一个 Skill,Claude Code 就能在你输入「评估这次改动的影响」时自动跑完整链路,而不是每次都从头口述一遍分析方法。

第一步:规划三层渐进式披露结构

Claude Code 的 Skill 采用渐进式披露(Progressive Disclosure)设计——上下文按需加载,避免一次性把所有细节塞进模型窗口。一个进阶 Skill 通常分三层:

flowchart TB
    A["① metadata 层<br/>name + description<br/>常驻系统提示词"] --> B{模型判断<br/>是否触发?}
    B -->|是| C["② SKILL.md 正文<br/>触发后加载<br/>核心流程与约束"]
    C --> D{需要执行脚本<br/>或查阅规则?}
    D -->|是| E["③ 资源层<br/>scripts/ 与 references/<br/>按需读取"]
    B -->|否| F["不加载,零成本"]
    E --> G["返回结构化结果"]
    D -->|否| G

设计目录结构如下:

change-impact-analysis/
├── SKILL.md                      # 主指令:触发场景 + 工作流程
├── scripts/
│   ├── extract-changes.sh        # 从 diff 提取变更的导出符号
│   ├── trace-references.sh       # 反向追踪符号在仓库中的引用
│   ├── find-affected-tests.sh    # 定位需要回归的测试文件
│   └── score-risk.sh             # 计算风险评分
└── references/
    ├── destructive-patterns.md   # 破坏性变更判定规则
    └── report-template.md        # 输出报告模板

关键点:SKILL.md 保持精简(控制在 200 行内),把判定规则、报告长模板等「重内容」下沉到 references/,让模型在真正需要时才去读——这就是渐进式披露省 token 的核心。

第二步:写 SKILL.md 主指令

创建 .claude/skills/change-impact-analysis/SKILL.md:

---
name: change-impact-analysis
description: 评估一次代码改动的"爆炸半径"。当用户提交/比较改动、做 PR review、
  问"这次改动会影响什么"、"哪些测试要回归"、"这个改动安不安全"时使用。
  解析 git diff,反向追踪符号引用,定位受影响测试,输出带 Mermaid 依赖图的风险报告。
allowed-tools: Bash(git diff:*), Bash(git log:*), Bash(git show:*), Bash(bash:*.sh),
  Bash(rg:*), Read, Grep, Glob
---

# 变更影响分析

当被触发时,按以下流程评估当前仓库相对基线分支的改动影响。

## 输入

- 基线分支:默认 `origin/main`。用户指定其他分支则用其指定值。
- 可选:用户给定的具体 commit 范围(如 `HEAD~3..HEAD`)。

## 执行流程

1. **提取变更实体**
   运行 `bash scripts/extract-changes.sh <base>`,得到被删除/修改的导出符号
   (函数、类、export 常量)。重点关注符号定义行的删除——这是破坏性变更的高危信号。

2. **反向追踪引用**
   把上一步的符号写入临时文件,运行 `bash scripts/trace-references.sh <symbols.txt> <root>`
   找出引用这些符号的所有文件,剔除定义文件自身。

3. **定位受影响测试**
   运行 `bash scripts/find-affected-tests.sh`,匹配受影响文件对应的测试
   (约定 `foo.ts` → `foo.test.ts` / `foo.spec.ts` / `test_foo.py`)。

4. **判定破坏性变更**
   读 `references/destructive-patterns.md`,逐条核对当前 diff 是否命中:
   导出符号被删除、函数签名改变、返回类型收窄、公开常量值变更等。

5. **计算风险评分**
   运行 `bash scripts/score-risk.sh <affected> <tests> <breaking(0|1)>`。

6. **产出报告**
   严格按 `references/report-template.md` 的结构输出,含一张 Mermaid 影响图。

## 约束

- 只读分析,绝不修改任何源文件或测试文件。
- 若 diff 为空,直接回报"无改动",不要编造影响。
- 报告中每个受影响文件必须给出被引用的符号名作为依据,禁止空泛列举。

几个写法要点值得展开:

description 是「触发条件」而非「功能简介」。 它常驻在系统提示词里,决定模型何时主动调用本 Skill。写「当用户问 X、做 Y 时使用」比写「这是一个分析工具」触发率高得多。这是新手最常踩的坑。

allowed-tools 用白名单收窄边界。 这里只放开只读的 git 命令和脚本执行,把 Edit/Write 排除在外——影响分析天然是只读操作,白名单从机制上杜绝了「分析着分析着把代码改了」的越权风险。

第三步:编写四个分析脚本

把繁重的、确定性的工作交给 awk/rg/git,模型只负责调度与解读——这是「脚本 Skill」模式,比让模型逐文件 Read 再推理稳定得多。

scripts/extract-changes.sh —— 从 diff 提取变更的导出符号:

#!/usr/bin/env bash
# extract-changes.sh: 从 diff 提取被删除/修改的导出符号
# 用法: bash extract-changes.sh [base=origin/main]
set -euo pipefail
BASE="${1:-origin/main}"
ROOT="$(git rev-parse --show-toplevel)"

git diff "${BASE}...HEAD" --unified=0 --diff-filter=d |
  awk '
    /^\+\+\+ / { next }            # skip file headers
    /^[-+]/ && !/^[-+][-+][-+]/ {  # only real +/- diff lines
      line = $0
      # match exported / defined symbols across JS/TS/Python/Go-ish
      if (line ~ /(export[[:space:]]+(async[[:space:]]+)?function|export[[:space:]]+(default[[:space:]]+)?(const|class)|function[[:space:]]+|def[[:space:]]+|func[[:space:]]+|class[[:space:]]+)/) {
        # strip the leading +/-, collapse spaces
        sub(/^[-+]/, "", line); gsub(/[[:space:]]+/, " ", line)
        print line
      }
    }
  '

scripts/trace-references.sh —— 反向追踪符号引用:

#!/usr/bin/env bash
# trace-references.sh: 对符号列表反向追踪引用文件
# 用法: bash trace-references.sh <symbols_file> [root=.]
set -euo pipefail
SYMS="$1"; ROOT="${2:-.}"
[ -f "$SYMS" ] || { echo "symbols file not found" >&2; exit 1; }

while IFS= read -r raw; do
  # extract the identifier after the keyword (function|def|const|class ...)
  sym="$(printf '%s' "$raw" | grep -oE '[A-Za-z_][A-Za-z0-9_]*' | sed -n '2p')"
  [ -z "$sym" ] && sym="$(printf '%s' "$raw" | grep -oE '[A-Za-z_][A-Za-z0-9_]*' | head -1)"
  [ -z "$sym" ] && continue
  # whole-word match, list files only
  rg -l --no-ignore-vcs -w "$sym" "$ROOT" 2>/dev/null || true
done < "$SYMS" | sort -u

scripts/find-affected-tests.sh —— 定位对应测试:

#!/usr/bin/env bash
# find-affected-tests.sh: 受影响文件 → 对应测试文件
# 用法: bash find-affected-tests.sh <affected_files_file>
set -euo pipefail
IN="$1"
while IFS= read -r f; do
  [ -z "$f" ] && continue
  base="${f%.*}"; ext="${f##*.}"
  # 常见测试命名约定
  for pat in "${base}.test.${ext}" "${base}.spec.${ext}" \
             "$(dirname "$f")/__tests__/$(basename "${base}").test.${ext}" \
             "tests/test_$(basename "${base}").py"; do
    [ -f "$pat" ] && echo "$pat"
  done
done < "$IN" | sort -u

scripts/score-risk.sh —— 风险评分:

#!/usr/bin/env bash
# score-risk.sh: 受影响数 * 3 + 受影响测试 * 2 + 破坏性 * 20
set -euo pipefail
AFFECTED="${1:-0}"; TESTS="${2:-0}"; BREAKING="${3:-0}"
echo $(( AFFECTED * 3 + TESTS * 2 + BREAKING * 20 ))

预期结果:四个脚本都接受标准输入输出,可独立测试。例如在一个改动了 utils/format.ts 导出函数的仓库里:

$ bash scripts/extract-changes.sh origin/main
export function formatPrice(amount: number, currency: string)
$ bash scripts/extract-changes.sh origin/main > /tmp/syms.txt
$ bash scripts/trace-references.sh /tmp/syms.txt .
src/cart/summary.ts
src/invoice/print.ts
$ bash scripts/score-risk.sh 2 1 0
8

常见问题

第四步:编写引用文件

references/destructive-patterns.md —— 破坏性变更判定清单(只在与破坏性判定相关时才加载):

# 破坏性变更(Breaking Change)判定清单

命中以下任一条,即视为破坏性变更(`breaking=1`):

1. **导出符号被删除**:`export function/const/class` 从 diff 中消失。
2. **函数签名改变**:参数数量减少、参数顺序变化、必填参数新增。
3. **返回类型收窄**:`string | undefined` → `string`,或 `any` → 具体类型。
4. **公开常量值变更**:`export const MAX = 100` 变为其他值。
5. **HTTP 接口契约变更**:路由路径、状态码、请求/响应字段移除。

以下不算破坏性:新增导出、内部实现重构、新增可选参数、放宽类型。

references/report-template.md —— 报告模板(控制输出一致性):

# 变更影响分析报告

**基线**:<base> → **HEAD** **风险评分**:<score>(破坏性:<yes/no>)

## 改动概览
- 变更文件 N 个,删除 X 行 / 新增 Y 行
- 变更的导出符号:`sym1`, `sym2`

## 影响范围(爆炸半径)
| 文件 | 引用的符号 | 路径 |
|------|-----------|------|
| src/a.ts | sym1 | import/调用 |

## 需要回归的测试
- a.test.ts

## 影响关系图
```mermaid
graph LR
  A["utils/format.ts<br/>(改)"] --> B["cart/summary.ts"]
  A --> C["invoice/print.ts"]

结论与建议

(一句话结论 + 是否建议人工 review 重点模块)

## 第五步:实战演练——分析一次真实的破坏性改动

准备一个最小仓库模拟场景:

```bash
mkdir demo-cia && cd demo-cia && git init -q
npm init -y >/dev/null 2>&1

# 初始版本:一个导出函数
cat > utils/format.ts <<'EOF'
export function formatPrice(amount: number, currency = 'USD') {
  return `${currency} ${amount.toFixed(2)}`;
}
EOF

# 两处调用
cat > cart/summary.ts <<'EOF'
import { formatPrice } from '../utils/format';
export const line = (a: number) => formatPrice(a);
EOF
cat > invoice/print.ts <<'EOF'
import { formatPrice } from '../utils/format';
export const bill = (a: number) => formatPrice(a, 'EUR');
EOF
git add -A && git commit -qm "init" && git branch -M main

# 现在做一个破坏性改动:删掉默认参数,改成必填
cat > utils/format.ts <<'EOF'
export function formatPrice(amount: number, currency: string) {
  return `${currency} ${amount.toFixed(2)}`;
}
EOF
git add -A && git commit -qm "breaking: currency now required"

在 Claude Code 里把 Skill 放好后,直接对话触发:

帮我评估这次改动的影响。

模型会自动按流程执行,最终产出类似报告:

# 变更影响分析报告
基线:origin/main → HEAD 风险评分:22(破坏性:是)

## 改动概览
- 变更文件 1 个,删除 1 行 / 新增 1 行
- 变更的导出符号:formatPrice

## 影响范围
| 文件 | 引用符号 |
|------|---------|
| cart/summary.ts | formatPrice(无第二实参,将编译失败)|
| invoice/print.ts | formatPrice(已传 'EUR',安全)|

## 需要回归的测试
- 暂无对应测试文件(建议补 utils/format 的单测)

## 影响关系图
graph LR
  A["utils/format.ts (改)"] --> B["cart/summary.ts ⚠"]
  A --> C["invoice/print.ts ✓"]

## 结论
破坏性变更:formatPrice 第二参数从有默认值变为必填。
cart/summary.ts 未传 currency,构建会直接失败,必须先修。

注意模型不是机械跑脚本——它在第 4 步对照 destructive-patterns.md 判定出「必填参数新增」属于破坏性,并在解读阶段点出 summary.ts 因缺参会编译失败这一语义级结论。脚本负责确定性检索,模型负责语义判断,二者分工。

进阶技巧

让 Skill 自动触发而非手动喊。 把 description 里的触发词覆盖到「PR review」「提交前检查」「这个改动安全吗」等高频说法,命中率会显著上升。可在团队约定里把「提 PR 前先做影响分析」写进 CLAUDE.md,配合自动触发形成肌肉记忆。

用 context: fork 隔离重型分析。 若分析过程会产生大量中间输出污染主上下文,可在 frontmatter 加 context: fork,让本次 Skill 执行在独立上下文里跑完再回传结论。

缓存基线 diff。 大仓库 git diff origin/main...HEAD 较慢,可在脚本里加 git rev-parse HEAD 做缓存键,避免重复全量 diff。

接 hook 做自动门禁。 用 PreToolUse hook 在执行 git commit/git push 前自动触发本 Skill,当风险评分超阈值时阻断提交——把分析从「事后报告」升级为「事前护栏」。

训练营适用性评估

维度 评估
适合人群 中级及以上前端/后端工程师,有 PR review 经验
训练营类型 工程效能、AI 辅助研发、代码质量训练营
预计教学时长 讲解 45 分钟 + 动手实操 90 分钟
难度 ★★★☆☆(脚本调试是主要耗时点)
复用价值 高——可原样迁入任何 Git 仓库即用

参考来源

  1. Extend Claude with skills — Claude Code Docs — 官方 Skill 开发规范,含 frontmatter 字段与渐进式披露机制
  2. Claude Code Skill Frontmatter: Every YAML Option Explained — name/description/allowed-tools/context 等字段的逐项权威说明
  3. Blast Radius Analysis: Knowing What You'll Break Before You Ship — 变更影响分析(爆炸半径)的工程方法论与风险量化思路
  4. Test impact analysis: from a diff to the right tests to run — 测试影响分析(TIA):从 diff 反推需回归测试的标准做法
  5. Claude Code Skills 渐进式披露实测 — metadata / SKILL.md / resources 三层加载机制与省 token 实测

[!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学通信知识星球海报-扫码加入