ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Claude Code Skill开发实战:从50个废稿到20个精品的避坑指南

Claude Code Skill开发实战:从50个废稿到20个精品的避坑指南 1. 从50个Skill里爬出来的血泪教训我在过去三个月里陆陆续续写了超过50个Claude Code Skill。从最开始的兴奋到中期的迷茫再到后来的顿悟整个过程像极了我第一次接触Spring Boot时疯狂堆Controller层的状态——写得多不代表写得好能跑通不代表能维护。如果你正在用Claude Code或者刚听说Skill这个概念想试试水我建议你先别急着动手。因为我踩过的坑大概率你也会踩。前30个Skill我几乎全部推翻重写了原因很简单我把Skill当成了“提示词模板”来写而它本质上是一套可组合、可复用、有明确边界的能力单元。这篇文章我会把50个Skill的迭代过程拆开讲清楚Skill到底是什么、SKILL.md该怎么写、MCP在什么场景下必须上、以及为什么你写的Skill总是“看起来能用但实际不好用”。内容偏实操代码和配置都会给到适合已经装好Claude Code、准备认真搞一套Skill体系的开发者。2. Skill到底是什么别把它当提示词2.1 从“提示词模板”到“能力单元”的认知转变我最初写Skill的思路特别朴素把一段常用的提示词存成文件用的时候让Claude Code读一下。比如“帮我写一个Spring Boot的Controller”我把这段提示词写进SKILL.md觉得这就是Skill了。结果用了不到一周就发现三个致命问题第一触发不稳定。Claude Code有时候读了这个Skill有时候不读完全看它心情。我以为是模型问题后来才发现是我没搞清楚Skill的触发机制。第二上下文污染。一个Skill里塞了太多不相关的内容导致Claude Code在执行具体任务时被无关信息干扰输出质量反而下降。第三无法组合。我想把“写Controller”和“写Service”两个Skill串起来用发现根本串不起来因为每个Skill都是孤立的提示词块没有输入输出的契约。后来我重新理解了Skill的定位它不是一个提示词文件而是一个带有明确触发条件、输入输出定义、以及执行边界的原子能力。你可以把它类比成Spring Boot里的一个Service Bean——有明确的接口定义、有依赖注入、可以被其他Bean调用而不是一个到处复制粘贴的工具类。这个认知转变之后我推翻了前30个Skill开始用“能力单元”的思路重新设计。2.2 SKILL.md的结构到底该怎么组织一个能用的SKILL.md至少包含四个部分--- name: spring-boot-controller-generator description: 根据实体类生成标准的Spring Boot Controller层代码 trigger: 当用户提到生成Controller、写一个接口、创建REST API时触发 --- ## 能力描述 根据输入的实体类定义生成符合团队规范的Controller层代码。 ## 输入 - 实体类文件路径或类名 - 基础包名 - 是否需要Swagger注解 ## 输出 - 完整的Controller类代码 - 对应的单元测试骨架 ## 约束 - 必须使用构造器注入禁止Autowired字段注入 - 统一返回ResultT包装 - 分页查询必须使用PageHelper这里有几个关键点我踩过坑trigger字段不是可有可无的。我一开始不写trigger指望Claude Code自己判断什么时候用这个Skill。实测下来触发率不到30%。后来我把trigger写清楚触发率直接拉到80%以上。trigger的写法有讲究要覆盖用户可能的各种表达方式但也不能太宽泛否则会误触发。约束部分比能力描述更重要。能力描述告诉Claude Code“能做什么”约束告诉它“不能做什么”。我吃过亏没写约束的Skill生成出来的代码风格五花八门有的用字段注入有的用构造器注入有的返回ResponseEntity有的返回自定义Result根本没法统一。输入输出要明确。这不是写给Claude Code看的是写给你自己看的。当你写到第20个Skill的时候你会忘记每个Skill的输入输出是什么没有明确的契约定义组合使用就是灾难。2.3 为什么前30个Skill都白写了回过头看前30个Skill白写的原因可以归结为三类第一类粒度太细。我写了“生成getter方法”、“生成toString方法”这种Skill实际上Claude Code本身就能做根本不需要Skill。Skill应该封装的是有团队特色、有业务逻辑、有规范约束的能力而不是通用编程能力。第二类粒度太粗。我写了一个“生成完整CRUD模块”的Skill包含了Controller、Service、Mapper、XML、DTO、VO所有内容。结果这个Skill的SKILL.md写了2000多字Claude Code读完之后反而不知道该从哪里开始输出质量极差。后来我把它拆成了6个独立Skill每个负责一层通过组合来完成完整模块的生成。第三类没有版本管理。Skill是会迭代的团队规范会变技术栈会升级。我一开始把Skill直接放在项目目录里改了就改了没有版本记录。后来发现某个Skill被改坏之后根本回不到之前的版本。现在我用Git管理Skill目录每次修改都有commit记录。3. MCP不是银弹什么时候该用什么时候不该用3.1 MCP协议解决的是什么问题MCP全称Model Context Protocol简单说就是让Claude Code能够调用外部工具和数据的协议。你可以把它理解成给Claude Code装了一个“插件系统”通过MCP ServerClaude Code可以访问数据库、调用API、读取本地文件、操作IDE等等。我一开始听说MCP的时候特别兴奋觉得什么都能干了。于是我把数据库查询、文件操作、Git操作全部通过MCP来实现。结果发现两个问题性能问题。每次MCP调用都有网络开销和协议解析开销一个简单的文件读取通过MCP走一圈比直接让Claude Code读文件慢了不止一个数量级。复杂度问题。MCP Server需要单独部署和维护调试链路变长出问题的时候排查成本很高。后来我总结出一个判断标准如果这个能力Claude Code本身就能做就不要用MCP如果这个能力需要访问Claude Code无法直接访问的外部系统才考虑MCP。3.2 Skill和MCP的配合方式Skill和MCP不是二选一的关系而是配合关系。我的实践模式是这样的Skill负责定义流程和规范MCP负责提供外部能力。举个例子我有一个“数据库表结构同步”的Skill它的SKILL.md里定义了同步的流程先读取实体类再对比数据库表结构最后生成差异SQL。但“读取数据库表结构”这个动作Claude Code本身做不到需要通过MCP来调用数据库。所以这个Skill的架构是--- name: db-schema-sync description: 同步实体类与数据库表结构 trigger: 当用户提到同步表结构、对比数据库时触发 mcp: database-mcp-server --- ## 执行流程 1. 读取指定实体类文件解析字段定义 2. 通过MCP调用database-mcp-server的getTableSchema方法获取当前表结构 3. 对比字段差异生成ALTER语句 4. 输出差异报告和SQL脚本这样Skill负责流程编排MCP负责具体的外部调用各司其职。3.3 我踩过的MCP选型坑MCP Server的选型我踩过不少坑说几个典型的坑一用了社区版MCP Server但没看维护状态。有个MCP Server最后一次更新是半年前用的还是旧版协议接进来之后各种报错。后来我养成了习惯选MCP Server之前先看三个指标最近三个月是否有commit、issue响应速度、是否有明确的版本号。坑二MCP Server的权限给太大。有个数据库MCP Server我直接给了root权限结果Claude Code在执行一个查询Skill的时候误执行了一条DELETE语句。虽然是在测试环境但也吓出一身冷汗。现在我的原则是MCP Server的权限按最小必要原则给只读操作绝不给写权限。坑三没有做MCP调用的超时和重试。MCP调用失败的时候Claude Code会一直重试导致整个Skill执行卡死。后来我在Skill的约束里明确写了超时时间和重试次数超过就报错退出。4. 一个完整Skill的实操拆解4.1 需求场景Spring Boot接口自动生成我拿一个实际在用的Skill来拆解这个Skill的功能是根据MyBatis的Mapper XML文件自动生成对应的Service层和Controller层代码。这个需求来自我们团队的实际情况Mapper XML是手写的但Service和Controller的代码结构非常固定每次都要复制粘贴改半天。用Claude Code来做这件事效率提升非常明显。4.2 SKILL.md的完整写法--- name: mapper-to-service-controller description: 根据MyBatis Mapper XML生成Service和Controller层代码 trigger: 当用户提到根据Mapper生成Service、生成Controller层、从XML生成接口时触发 version: 2.3.0 author: 团队内部 --- ## 能力描述 读取指定的MyBatis Mapper XML文件解析其中的SQL语句和参数定义 自动生成符合团队规范的Service接口、Service实现类、Controller类。 ## 输入 - Mapper XML文件路径必填 - 实体类全限定名必填 - 基础包名必填如com.example.module - 是否生成Swagger注解可选默认true ## 输出 - XxxService.java接口 - XxxServiceImpl.java实现类 - XxxController.java控制器 - 生成结果报告包含生成的文件列表和注意事项 ## 执行流程 1. 读取并解析Mapper XML提取所有select/insert/update/delete语句 2. 根据statement id推断方法名和返回类型 3. 生成Service接口方法签名与Mapper方法一一对应 4. 生成Service实现类注入Mapper实现方法委托 5. 生成Controller类根据方法类型推断HTTP方法和路径 6. 输出生成报告 ## 约束 - Service接口必须继承IServiceTMyBatis-Plus规范 - Service实现类必须继承ServiceImplM, T - Controller必须使用RestController注解 - 统一返回ResultT包装 - 分页查询必须使用PageHelper.startPage - 禁止在Controller中写业务逻辑 - 所有public方法必须有Javadoc注释 ## 示例 输入UserMapper.xml实体类com.example.entity.User 输出 - UserService.java - UserServiceImpl.java - UserController.java4.3 关键参数的计算与选择这个Skill里有几个参数是我反复调整过的方法名推断规则。Mapper XML里的statement id通常是selectByPrimaryKey、insertSelective这种但Service方法名需要更业务化。我定义了一套映射规则Mapper方法前缀Service方法名HTTP方法selectByPrimaryKeygetByIdGETselectListlistGETinsertSelectivecreatePOSTupdateByPrimaryKeySelectiveupdatePUTdeleteByPrimaryKeydeleteDELETE这套规则不是拍脑袋定的是参考了RESTful规范和团队现有代码风格。你如果有自己的规范可以替换成自己的映射表。路径生成规则。Controller的RequestMapping路径我定义为/api/{模块名}/{实体名小写复数}比如User实体在system模块下路径就是/api/system/users。这里有个细节复数形式我用了一个简单的规则以y结尾的变ies以s结尾的加es其他加s。虽然不完美但覆盖了90%的场景。返回类型推断。selectByPrimaryKey返回单个对象selectList返回Listinsert/update/delete返回int或boolean。这个推断逻辑写在Skill的执行流程里Claude Code会根据statement的resultType和SQL类型自动判断。4.4 实操过程记录我拿一个真实的UserMapper.xml来跑这个Skill记录一下完整过程。第一步准备输入。UserMapper.xml里有5个statementselectByPrimaryKey、selectList、insertSelective、updateByPrimaryKeySelective、deleteByPrimaryKey。第二步触发Skill。我在Claude Code里输入“根据UserMapper.xml生成Service和Controller实体类是com.example.entity.User包名com.example.module.system”。第三步Claude Code读取Skill按照执行流程逐步操作。这里有个细节Claude Code会先读取Mapper XML文件解析出所有statement然后按照映射规则生成方法名。第四步生成代码。Service接口生成了5个方法getById、list、create、update、delete。Controller生成了5个接口路径分别是GET /api/system/users/{id}、GET /api/system/users、POST /api/system/users、PUT /api/system/users、DELETE /api/system/users/{id}。第五步输出报告。报告里列出了生成的文件、每个文件的方法数量、以及需要注意的地方比如selectList没有分页参数需要手动添加。整个过程大约30秒如果手动写这些代码至少需要15分钟。效率提升是显而易见的。5. 常见问题与排查技巧实录5.1 Skill不触发怎么办这是最高频的问题。我总结了排查步骤第一步检查trigger字段。trigger里的关键词是否覆盖了你的实际表达比如你写的是“生成Controller”但实际输入的是“帮我写一个接口”那就触发不了。解决办法是把trigger写得更宽泛或者用更通用的关键词。第二步检查Skill文件位置。Claude Code读取Skill有固定的目录结构放错位置就读不到。我一开始把Skill放在项目根目录后来发现应该放在.claude/skills/目录下。第三步检查SKILL.md格式。frontmatter的格式必须正确---不能少字段名不能拼错。我有个Skill因为把trigger写成了triggers排查了半小时才发现。第四步检查是否有冲突。如果两个Skill的trigger关键词重叠Claude Code可能不知道该用哪个。解决办法是让trigger更具体或者在Skill里写明优先级。5.2 生成结果不符合预期怎么调生成结果不符合预期通常有三个原因约束写得太模糊。比如你写“代码要规范”Claude Code不知道什么叫规范。要写成“必须使用构造器注入”、“必须返回Result ”、“必须添加Javadoc”。示例不够具体。SKILL.md里的示例要尽可能完整最好是一个可以直接运行的代码片段。我一开始只写“输入UserMapper.xml”太模糊了。后来改成完整的输入输出示例生成质量明显提升。执行流程有歧义。流程步骤要按顺序写清楚每一步的输入输出要明确。如果某一步依赖前一步的结果要写清楚依赖关系。5.3 常见问题速查表问题现象可能原因排查方法解决方案Skill不触发trigger关键词不匹配检查trigger字段扩大关键词覆盖范围生成代码风格不统一约束不明确检查约束部分添加具体的代码规范约束执行到一半卡住MCP调用超时检查MCP Server状态添加超时和重试配置输出内容太长Skill粒度太粗检查Skill职责拆分成多个细粒度Skill多个Skill冲突trigger重叠检查所有Skill的trigger调整trigger或设置优先级版本混乱没有版本管理检查Git记录用Git管理Skill目录5.4 独家避坑技巧技巧一Skill的description要写“人话”。description不仅是给Claude Code看的也是给你自己看的。当你有了50个Skill之后你会忘记每个Skill是干什么的。description要写得像给同事介绍一样清楚。技巧二每个Skill都要有测试用例。我在Skill目录下建了一个tests/文件夹每个Skill对应一个测试文件里面记录了输入和预期输出。每次修改Skill之后跑一遍测试用例确保没有破坏已有功能。技巧三Skill的版本号要跟Git tag对应。我在SKILL.md的frontmatter里写了version字段每次修改都递增版本号同时在Git里打tag。这样出问题的时候可以快速回滚到指定版本。技巧四不要在一个Skill里做太多事。我现在的原则是一个Skill只做一件事最多不超过三个步骤。如果需要更多步骤就拆成多个Skill通过组合来完成。技巧五定期清理不再使用的Skill。我每两周review一次Skill目录把不再使用的Skill归档到archive/目录。保持活跃Skill的数量在20个以内太多会导致触发混乱。6. 从30个废稿到20个精品的迭代路径6.1 我的Skill分类体系经过50个Skill的迭代我最终把Skill分成了四类代码生成类根据输入生成代码比如Controller生成、Service生成、DTO生成。这类Skill的特点是输入输出明确约束清晰。代码审查类检查代码是否符合规范比如命名规范检查、注解使用检查、日志规范检查。这类Skill需要定义明确的检查规则。流程编排类编排多个Skill的执行顺序比如“生成完整CRUD模块”就是编排了6个代码生成Skill。这类Skill本身不生成代码只负责调度。工具集成类通过MCP调用外部工具比如数据库操作、Git操作、API调用。这类Skill需要配置MCP Server。这四类Skill的SKILL.md写法各有侧重代码生成类重约束代码审查类重规则流程编排类重顺序工具集成类重配置。6.2 哪些Skill值得保留判断一个Skill是否值得保留我用三个标准使用频率。如果一个月用不到一次基本可以归档了。我有个“生成Dockerfile”的Skill三个月用了两次后来直接删了因为Claude Code本身就能做。不可替代性。如果Claude Code本身就能做而且做得不差那就不需要Skill。比如“生成getter/setter”这种Claude Code原生就能做写Skill纯属多余。团队特色。如果这个Skill封装的是团队特有的规范或流程那就值得保留。比如我们团队的Controller必须返回Result 这个规范是团队特有的写成Skill就很有价值。6.3 后续扩展方向这套Skill体系目前还在迭代中我计划往三个方向扩展方向一Skill的自动化测试。目前测试用例是手动跑的我打算写一个脚本自动遍历所有Skill的测试用例生成测试报告。方向二Skill的依赖管理。有些Skill依赖其他Skill的输出目前是手动保证顺序的。我打算在SKILL.md里增加depends_on字段自动解析依赖关系。方向三Skill的使用统计。我想统计每个Skill的触发次数、成功率、平均执行时间用数据来指导Skill的优化方向。这三个方向都不复杂但需要时间慢慢打磨。我的原则是不追求Skill的数量追求每个Skill的质量。一个高质量的Skill抵得上十个凑数的Skill。最后分享一个我最近才想明白的道理写Skill和写代码一样可读性比聪明更重要。一个SKILL.md写得清清楚楚的Skill比一个用了各种高级技巧但没人看得懂的Skill有价值得多。因为Skill是给人用的不是给机器看的。
返回列表