Skills Design Patterns and Best Practices

Skill design patterns distilled from extensive practice help you make sound decisions quickly when facing new requirements.

This article summarizes the 7 most commonly used design patterns and their applicable scenarios.


Pattern 1: Single Responsibility Skill

Each Skill does one thing and does it well.

The more focused the functionality, the more precise the description, the higher the trigger accuracy, and the easier the maintenance.

ApproachExample
Correct: Single responsibilitypdf-to-text (only handles text extraction)
Incorrect: Mixed responsibilitiespdf-tools (reading, conversion, merging, watermarking... all in one place)

If you find that a Skill's description needs to be very long to clarify what it "does not do," this usually means it has taken on too many responsibilities and should be split.


Pattern 2: Defensive Input Checking

Perform all input validation centrally at the Skill script entry point, and only enter business logic after validation passes.

Example

# File path: scripts/defensive_entry.py
# Pattern: Defensive input checking - centralized validation at the entry point, release after passing

import sys
import os
import json

def validate(args) -> list:
    """Centrally validate all inputs and return a list of errors (empty list means all passed)"""
    errors = []

    if not args.file:
        errors.append("Missing argument --file")
    elif not os.path.exists(args.file):
        errors.append(f"File does not exist: {args.file}")
    elif os.path.getsize(args.file) == 0:
        errors.append("File content is empty")

    if args.limit < 1:
        errors.append("--limit must be greater than 0")

    return errors

def run(args):
    """Business logic entry point, only called after validation passes"""
    # Perform actual processing...
    return {"status": "success", "file": args.file}

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument("--file",  required=False, default="")
    parser.add_argument("--limit", type=int, default=100)
    args = parser.parse_args()

    # 1. Centralized validation
    errors = validate(args)
    if errors:
        result = {"status": "input_error", "errors": errors}
        print(json.dumps(result, ensure_ascii=False))
        sys.exit(1)

    # 2. Execute business logic after validation passes
    result = run(args)
    print(json.dumps(result, ensure_ascii=False))

Pattern 3: Progressive Output

For Skills that take longer than 5 seconds, progress should be output in stages, rather than leaving the user to face silent waiting.

## 执行流程(渐进式输出示例)

### 步骤一:读取文件
运行读取脚本后,立即告知用户:
> 已读取文件:example_data.csv(1,024 行,8 列)正在进行数据清洗...

### 步骤二:数据清洗
清洗完成后,告知用户:
> 数据清洗完成:删除重复行 3 条,空值填充 12 处。正在生成报告...

### 步骤三:生成报告
报告生成后,展示下载链接并说明内容:
> 报告已生成,包含统计摘要和数据质量评估。

Pattern 4: Idempotent Operations

A Skill's execution results should be idempotent: for the same input, no matter how many times it is executed, the output should be consistent.

Non-idempotent operations (such as appending content on every execution) can cause confusion when users retry.

Example

# File path: scripts/idempotent_output.py
import os
from datetime import datetime

OUTPUT_DIR = "/mnt/user-data/outputs"

def get_output_path(base_name: str, suffix: str = ".xlsx") -> str:
    """
Generate a deterministic output path

Idempotent approach: overwrite the same-named file on every execution (instead of appending or generating a new file)
Ensure the same input always corresponds to the same output file
    """

    # Method A: Fixed filename (same input always overwrites)
    return os.path.join(OUTPUT_DIR, f"{base_name}_report{suffix}")

    # Method B (timestamp): generate a new file each time (non-idempotent, but preserves history)
    # ts = datetime.now().strftime("%Y%m%d_%H%M%S")
    # return os.path.join(OUTPUT_DIR, f"{base_name}_{ts}{suffix}")

Pattern 5: Explicit Completion Signal

After a Skill completes execution, it should give a clear "completion signal" so the user knows the task has ended and does not need to keep waiting.

## 任务完成后的输出规范

无论成功还是失败,最后一步必须输出一个明确的状态行:

成功时:
> 任务完成:报告已生成,共处理 1,024 行数据,耗时约 3 秒。

失败时:
> 任务中断:在"数据清洗"步骤遇到错误(原因:文件编码不是 UTF-8)。
> 建议:将文件另存为 UTF-8 格式后重新上传。

Pattern 6: Version Compatibility Declaration

Declare compatible model versions in the frontmatter of SKILL.md to prevent unexpected behavior in unsupported environments.

---
name: data-analyzer
version: 1.2.0
description: 分析 CSV/Excel 数据,生成统计报告。
compatibility:
  claude_models:
    - claude-sonnet-4-20250514   # 经过验证的模型版本
    - claude-opus-4              # 同样支持
  python: ">=3.8"
  platforms:
    - linux    # Claude 计算机使用环境
---

Pattern 7: Fail Fast

Once an error that prevents continuation is detected, stop immediately and report clearly, rather than continuing execution with the error and producing meaningless output.

Example

# File path: scripts/fail_fast.py
import sys
import json

def assert_condition(condition: bool, message: str, hint: str = ""):
    """If conditions are not met, immediately output an error and exit (Fail Fast pattern)"""
    if not condition:
        result = {
            "status":  "error",
            "message": message,
            "hint":    hint
        }
        print(json.dumps(result, ensure_ascii=False))
        sys.exit(1)

# Perform all prerequisite checks centrally at the beginning of the script
import os
file_path = sys.argv[1] if len(sys.argv) > 1 else ""

assert_condition(bool(file_path),
                 "Missing file path argument",
                 "Usage: python script.py <file path>")

assert_condition(os.path.exists(file_path),
                 f"File does not exist: {file_path}",
                 "Please check whether the path is correct, or re-upload the file")

assert_condition(file_path.endswith(".csv"),
                 "File format not supported, expected .csv",
                 f"Actual file: {file_path}")

# All checks passed, execute the actual logic
print(json.dumps({"status": "ok", "file": file_path}))

Pattern Quick Reference

PatternCore PrincipleProblem Solved
Single ResponsibilityOne Skill does one thingInaccurate triggering, hard to maintain
Defensive Input CheckingCentralized validation at entry pointErrors only reported midway through execution
Progressive OutputReport progress in stagesUsers don't know if it's running
Idempotent OperationsSame input, same outputRetries produce confusing results
Explicit Completion SignalProvide a final state notification when doneUsers don't know whether the task is complete
Version Compatibility DeclarationDeclare supported runtime environmentsAbnormal behavior when environment doesn't match
Fail FastStop immediately upon errorContinuing with errors produces invalid output
Other Extensions