Claude Code Skill 开发实战:从50个踩坑到可复用工程体系
1. 从 50 个 Skill 里爬出来的血泪账先交代背景。过去大半年我陆陆续续写了 50 个 Claude Code Skill覆盖代码生成、接口文档、数据库迁移、前端组件、测试用例、部署脚本这些日常活儿。写到最后我回头一盘点发现前 30 个基本等于白写——不是不能用而是用起来别扭、维护成本高、复用率低最后大部分都被我自己弃用了。这篇文章就是把这 50 个 Skill 的踩坑过程摊开讲。核心关键词是Claude Code、Skill、SKILL.md、MCP顺带会聊到Spring Boot场景下的落地案例。适合谁看三类人一是刚开始接触 Claude Code Skill、还在纠结怎么写第一个 SKILL.md 的人二是已经写了一堆 Skill 但发现复用率上不去、维护越来越累的人三是想把 Skill 和 MCP 结合起来做工程化落地的人。不管你是刚上手还是已经踩过几脚泥这篇应该都能帮你少走点弯路。我先把结论摆前面省得你看到一半才反应过来Skill 的价值不在于能跑而在于稳定复用。前 30 个 Skill 之所以白写根本原因是我把它们当成了一次性脚本来写而不是当成可维护的工程资产来设计。这个认知转变是我写完第 31 个 Skill 之后才真正想明白的。下面我按四个部分展开先讲整体设计思路的转变再拆核心细节和实操要点然后是完整的实操流程最后是我整理的常见问题排查表。每一部分都是真金白银换来的不是从文档里抄的。2. 整体设计思路为什么前 30 个 Skill 会白写2.1 把 Skill 当脚本写是最大的坑我最早写 Skill 的思路特别朴素有个重复性任务就写个 Skill 把它固化下来。比如生成 Spring Boot Controller 模板、把 MyBatis 的 XML 转成注解、根据实体类生成建表 SQL。每个 Skill 单独看都能用但问题在于——它们之间没有共享上下文没有统一的输入输出约定没有版本管理。结果就是写第 5 个 Skill 的时候我复制了第 3 个的 SKILL.md 结构写第 12 个的时候又复制了第 8 个的。到第 20 个的时候我发现有 6 个 Skill 的 prompt 里都重复写了同一段项目使用 Spring Boot 3.x MyBatis-Plus包名统一为 com.xxx的上下文。这段上下文一旦要改比如项目升级到 Spring Boot 3.2我得挨个改 6 个文件。这就是典型的脚本思维每个 Skill 自包含不考虑复用和抽象。而正确的做法应该是工程思维把公共上下文抽出来把输入输出标准化把 Skill 当成有生命周期的资产来管理。2.2 SKILL.md 的结构决定了 Skill 的上限很多人写 SKILL.md 就是随手写一段 prompt前面加个标题就完事了。我前 30 个 Skill 基本都是这个路子。后来我才意识到SKILL.md 的结构直接决定了这个 Skill 能不能被稳定触发、能不能被复用、能不能被维护。一个合格的 SKILL.md 至少应该包含这几块触发条件什么时候用这个 Skill、输入约定用户需要提供什么、执行步骤Skill 内部怎么处理、输出格式产出什么、边界说明什么情况下不该用。我前 30 个 Skill 里有超过一半只写了执行步骤触发条件和边界说明基本空白。结果就是 Claude Code 经常在不该触发的时候触发或者触发了但输入不完整导致输出乱七八糟。2.3 为什么是 30 这个数字你可能会问为什么偏偏是前 30 个白写不是前 20 或前 40说实话这个数字不是精确的是我复盘时的一个大致分界。前 30 个 Skill 我基本是想到就写没有统一规划从第 31 个开始我做了三件事建立公共上下文库、统一 SKILL.md 模板、引入 MCP 做外部能力补充。这三件事做完之后后面 20 个 Skill 的复用率和稳定性明显上了一个台阶。所以30更像是一个认知拐点的标记而不是一个精确的统计数字。如果你现在正处在想到就写的阶段那这篇文章就是写给你的。2.4 Skill 和 MCP 的分工要提前想清楚这是我在第 35 个 Skill 左右才想明白的事。Skill 负责流程编排和上下文注入MCP 负责外部能力调用。两者分工不清就会导致 Skill 里塞了一堆本该由 MCP 做的事或者 MCP 配置了一堆本该由 Skill 处理的逻辑。举个 Spring Boot 场景的例子我要做一个根据需求描述生成完整 CRUD 模块的 Skill。这个 Skill 需要读项目现有的实体类、需要查数据库表结构、需要写文件。读实体类和写文件是本地能力Skill 自己就能做但查数据库表结构这件事如果项目用的是远程数据库就需要通过 MCP 去调用数据库工具。如果我把查数据库这件事也硬塞进 Skill 的 prompt 里那这个 Skill 就绑死了特定的数据库环境换个项目就废了。正确的做法是Skill 负责什么时候查、查什么、拿到结果怎么用MCP 负责实际去查。这样 Skill 是可移植的MCP 是可替换的。3. 核心细节解析SKILL.md 到底该怎么写3.1 触发条件要写得像路由规则触发条件是 SKILL.md 里最容易被忽视、但最重要的一块。我前 30 个 Skill 里触发条件基本就是一句话当用户需要生成 Controller 时使用。这种写法太模糊了Claude Code 根本判断不准。好的触发条件应该像路由规则一样精确。比如## 触发条件 当满足以下全部条件时触发 1. 用户明确提到生成 Controller、创建接口、新增 REST 接口之一 2. 当前工作目录下存在 pom.xml 或 build.gradle 3. 用户提供了实体类名或表名 不满足任一条件时先向用户确认不要直接执行。这样写的好处是Claude Code 在判断是否触发时有明确的依据不会因为用户随口提了一句接口就贸然触发。我实测下来加了精确触发条件之后误触发率从大概三成降到了不到一成。3.2 输入约定要明确缺什么就问输入约定这块我踩过的坑是Skill 假设用户会提供完整信息但实际用户往往只给一半。比如我写过一个根据表名生成 MyBatis Mapper的 Skill假设用户会提供表名和字段列表。结果用户经常只给表名Skill 就开始瞎编字段生成的 Mapper 完全不能用。后来我改成这样## 输入约定 必需输入 - 表名必填 - 字段列表必填格式字段名 类型 注释 可选输入 - 包名默认 com.example.mapper - 是否生成 XML默认否 如果用户未提供必需输入逐项询问不要自行假设。关键就是那句不要自行假设。Claude Code 很聪明聪明到会帮你脑补缺失信息但脑补出来的东西往往不对。明确告诉它缺什么就问比让它自由发挥靠谱得多。3.3 执行步骤要拆到可验证的粒度执行步骤是 SKILL.md 的主体也是最容易写得太粗或太细的地方。写太粗Claude Code 不知道具体怎么做写太细又变成了死板的脚本失去灵活性。我的经验是拆到每一步都有明确产出、且产出可验证的粒度。比如生成 CRUD 模块这个 Skill我拆成这几步读取实体类提取字段列表产出字段清单可验证字段数和实体类一致根据字段清单生成建表 SQL产出SQL 文件可验证能执行不报错生成 Mapper 接口产出Java 文件可验证编译通过生成 Service 层产出Java 文件可验证编译通过生成 Controller 层产出Java 文件可验证编译通过每一步都有产出每一步都能验证。这样即使中间某一步出错也能快速定位是哪一步的问题而不是面对一个整体跑不通的黑盒。3.4 输出格式要固定方便下游消费输出格式这块我前 30 个 Skill 基本没管导致每个 Skill 的输出风格都不一样。有的输出 Markdown有的输出纯文本有的直接输出代码块。结果就是这些 Skill 之间没法串联——A Skill 的输出没法直接喂给 B Skill。后来我统一了输出格式所有 Skill 的输出都遵循摘要 详情 下一步建议三段式。摘要用一两句话说明做了什么详情用代码块或表格展示具体产出下一步建议告诉用户接下来可以做什么。这样不仅人看着舒服Skill 之间也能互相消费输出。3.5 边界说明是防呆设计边界说明是我最后才补上的一块但补上之后效果立竿见影。所谓边界说明就是明确告诉 Claude Code什么情况下不该用这个 Skill。比如生成 CRUD 模块这个 Skill边界说明写的是## 边界说明 以下情况不要使用本 Skill - 项目不是 Spring Boot 项目 - 实体类使用了 JPA 注解而非 MyBatis-Plus 注解 - 用户要求生成的是 GraphQL 接口而非 REST 接口 遇到以上情况向用户说明原因并建议替代方案。这段说明的价值在于它防止了 Skill 在不适用的场景下被强行触发避免了用错工具导致的返工。我实测下来加了边界说明之后因为Skill 用错场景导致的返工减少了大概一半。4. 实操过程从零搭一个可复用的 Skill 体系4.1 第一步建立公共上下文库这是我从第 31 个 Skill 开始做的第一件事。具体做法是在项目根目录建一个.claude/context/目录里面放几个公共上下文文件比如project.md项目技术栈、包名约定、代码风格、database.md数据库连接信息、表命名规范、api.md接口规范、返回格式约定。然后在每个 SKILL.md 里通过引用这些文件来注入上下文而不是把上下文硬编码在 Skill 里。比如## 上下文 执行前先读取 .claude/context/project.md 和 .claude/context/api.md 按照其中的约定执行。如果文件不存在向用户确认项目约定。这样做的好处是项目约定变了只需要改一处所有 Skill 自动生效。我实测下来项目从 Spring Boot 2.7 升级到 3.2 的时候只改了project.md一个文件所有 Skill 就都适配了省了大量重复劳动。4.2 第二步统一 SKILL.md 模板第二件事是统一模板。我定了一个标准模板所有新 Skill 都按这个模板写# Skill 名称 ## 触发条件 精确的路由规则 ## 输入约定 必需输入 可选输入 缺失处理 ## 上下文 需要读取的公共上下文文件 ## 执行步骤 拆到可验证粒度 ## 输出格式 摘要 详情 下一步建议 ## 边界说明 什么情况下不该用这个模板看起来简单但它强制我在写每个 Skill 的时候都思考这七个问题。前 30 个 Skill 之所以白写很大程度上就是因为没有这个模板写的时候想到哪写到哪漏掉了很多关键信息。4.3 第三步引入 MCP 做能力补充第三件事是引入 MCP。MCP 的全称是 Model Context Protocol简单说就是一套让 AI 调用外部工具的协议。在 Claude Code 里MCP 可以让 Skill 具备访问外部系统的能力比如查数据库、调 API、读远程文件。我在 Spring Boot 项目里最常用的 MCP 配置是数据库查询。配置好之后Skill 就可以通过 MCP 去查真实的表结构而不是靠用户描述或者靠猜。具体配置大概长这样以常见的数据库 MCP 为例{ mcpServers: { database: { command: npx, args: [-y, modelcontextprotocol/server-postgres], env: { DATABASE_URL: postgresql://user:passlocalhost:5432/mydb } } } }配置好之后Skill 里就可以这样用## 执行步骤 1. 通过 MCP 的 database 工具查询目标表结构 2. 根据表结构生成实体类 3. 根据实体类生成 Mapper、Service、Controller这里的关键是Skill 只负责调用 MCP不负责实现 MCP。这样 Skill 是可移植的换个数据库只需要换 MCP 配置Skill 本身不用改。4.4 第四步建立 Skill 版本管理第四件事是版本管理。我前 30 个 Skill 基本没有版本概念改了就直接覆盖出了问题想回滚都回不去。后来我给每个 Skill 加了版本号放在 SKILL.md 的头部--- name: generate-crud version: 1.2.0 last_updated: 2024-05-20 ---版本号遵循语义化版本规范大版本号变了说明有不兼容改动小版本号变了说明加了功能补丁号变了说明只是修了 bug。这样我在用 Skill 的时候能一眼看出这个 Skill 是不是最新版改动大不大。4.5 第五步写测试用例验证 Skill第五件事是写测试用例。这是我从第 40 个 Skill 开始才做的但做了之后发现太值了。具体做法是给每个 Skill 准备 2-3 个测试用例覆盖正常场景、边界场景、异常场景。比如生成 CRUD 模块这个 Skill我准备了三个测试用例用例编号场景输入预期输出TC-01正常场景完整实体类 表名生成 4 个文件编译通过TC-02边界场景实体类只有 1 个字段生成 4 个文件编译通过TC-03异常场景实体类不存在提示错误不生成文件每次改完 Skill我都跑一遍这三个用例确保没有引入回归问题。这个习惯帮我避免了好几次改了一个地方坏了另一个地方的事故。4.6 第六步Skill 之间的串联第六件事是让 Skill 之间能串联。单个 Skill 再强能力也有限但多个 Skill 串起来就能完成复杂任务。串联的关键是统一输出格式让 A Skill 的输出能直接作为 B Skill 的输入。比如我有一个需求分析Skill输出是结构化的需求清单还有一个代码生成Skill输入是需求清单。这两个 Skill 串起来就能实现从需求描述到代码生成的端到端流程。我实测下来这个串联流程能把一个中等复杂度的 CRUD 模块开发时间从半天压缩到半小时左右。5. 常见问题与排查技巧实录5.1 Skill 不触发怎么办这是最常见的问题。Skill 写好了但 Claude Code 就是不触发。排查思路按顺序来第一检查触发条件是不是写得太窄。比如你写当用户说生成 Controller时触发但用户实际说的是帮我写个接口那就触发不了。解决办法是把触发条件写宽一点覆盖同义词。第二检查 SKILL.md 的位置对不对。Claude Code 对 Skill 文件的位置有要求放错地方就加载不到。一般是放在.claude/skills/目录下每个 Skill 一个子目录子目录里放 SKILL.md。第三检查文件编码和格式。SKILL.md 必须是 UTF-8 编码Markdown 格式要正确。我有一次因为文件里有个不可见字符导致整个 Skill 加载失败排查了半天才发现。5.2 Skill 触发了但输出不对这个问题比不触发更隐蔽。Skill 触发了但输出质量差、格式乱、内容不对。排查思路第一检查输入是不是完整。很多时候输出不对是因为输入不全Claude Code 自己脑补了。解决办法是在 SKILL.md 里明确写缺什么就问。第二检查上下文是不是注入成功。如果 Skill 依赖公共上下文文件但文件没读到输出就会跑偏。可以在 SKILL.md 里加一步确认上下文已读取读不到就报错。第三检查执行步骤是不是太粗。步骤太粗Claude Code 就会自由发挥发挥出来的东西往往不符合预期。解决办法是把步骤拆细每一步都有明确产出。5.3 Skill 之间冲突怎么办当你写的 Skill 多了难免会遇到两个 Skill 都想触发的情况。比如生成 Controller和生成 CRUD 模块这两个 Skill用户说生成用户模块的接口时两个都可能触发。解决办法是在触发条件里加优先级和互斥规则。比如## 触发条件 优先级高 互斥当 generate-crud 已触发时本 Skill 不触发或者在更上层的 Skill 里做路由根据用户意图分发给不同的子 Skill。我现在的做法是建一个总入口Skill所有请求先经过它由它判断该走哪个子 Skill。5.4 MCP 调用失败怎么排查MCP 调用失败的原因比较多我整理了一个排查表现象可能原因排查方法MCP 工具列表为空MCP 服务没启动检查 MCP 配置文件的 command 和 args调用超时网络问题或服务响应慢检查网络连接看 MCP 服务日志返回权限错误认证信息不对检查 MCP 配置里的 env 变量返回数据格式不对MCP 服务版本不匹配检查 MCP 服务版本和协议版本我踩过最坑的一次是 MCP 配置文件里路径写错了导致服务启动失败但 Claude Code 没有任何提示只是默默不加载。后来我养成了习惯每次改完 MCP 配置先手动跑一遍 MCP 服务确认能启动再集成到 Skill 里。5.5 Skill 维护成本怎么降Skill 写多了维护成本会指数级上升。降低维护成本的核心是抽象和分层第一层是公共上下文所有 Skill 共享改一处生效全部。第二层是基础 Skill只做单一职责的事比如读实体类、写文件。第三层是组合 Skill通过串联基础 Skill 完成复杂任务。这样改基础 Skill 的时候组合 Skill 自动受益不用挨个改。我实测下来做了分层之后维护 50 个 Skill 的成本大概相当于之前维护 15 个的成本。这个投入产出比还是很划算的。5.6 几个独家避坑技巧最后分享几个我踩坑踩出来的独家技巧技巧一Skill 名称用动词开头。比如generate-crud、parse-entity、validate-api这样一看就知道这个 Skill 是干什么的。我早期用名词命名比如crud-helper、entity-tool结果自己都记不清哪个是哪个。技巧二SKILL.md 里加示例。给每个 Skill 加一两个输入输出示例Claude Code 看了示例之后输出质量明显提升。示例比描述管用这是我在写了 40 多个 Skill 之后才总结出来的。技巧三定期清理废弃 Skill。我每两个月会盘一次 Skill 列表把三个月没用过的 Skill 归档。Skill 不是越多越好多了反而会互相干扰。我现在保留的活跃 Skill 大概 20 个比 50 个的时候好用多了。技巧四用 Git 管理 Skill。Skill 也是代码也该进版本控制。我用 Git 管理所有 SKILL.md每次改动都有记录出问题能回滚还能看到演进历史。技巧五Skill 的输出尽量结构化。能用 JSON 就用 JSON能用表格就用表格避免大段自然语言。结构化输出方便下游消费也方便你自己检查。6. 我在实际项目里的落地体会聊了这么多方法论最后说点实际的。我在一个 Spring Boot MyBatis 的多商户项目里用这套 Skill 体系做了完整的落地。项目大概有 30 多个实体类每个实体类都要生成 CRUD 模块。用传统方式一个模块大概要半天用 Skill 串联的方式一个模块压缩到 20 分钟左右而且生成的代码风格统一review 起来省心很多。但我也得说实话Skill 不是银弹。它擅长的是重复性高、规则明确的任务对于需要创造性思考的任务Skill 反而会限制发挥。我现在的做法是重复性任务用 Skill 固化创造性任务还是手动做两者结合效率最高。另外一点体会是写 Skill 的过程本身就是梳理业务逻辑的过程。很多平时没想清楚的细节在写 SKILL.md 的时候被迫想清楚了。所以哪怕你最后不用这个 Skill写的过程也是有价值的。如果你现在正准备写第一个 Skill我的建议是别急着写先花半小时想清楚触发条件、输入约定、边界说明这三块。这三块想清楚了Skill 就成功了一半。至于执行步骤反而是最容易补的。