返回技能列表

fail-early-pattern

pjt222
更新于 2 days ago
5 次查看
17
2
17
在 GitHub 上查看
测试aiapidesign

关于

This skill teaches developers to implement the fail-fast pattern by validating inputs and reporting errors immediately using guard clauses and assertions. It provides R-focused examples and polyglot guidance for writing robust functions, hardening APIs, and refactoring error-prone code. Use it when accepting external input, preparing for CRAN submission, or reviewing code to prevent silent failures.

快速安装

Claude Code

推荐
主要方式
npx skills add pjt222/agent-almanac -a claude-code
插件命令备选方式
/plugin add https://github.com/pjt222/agent-almanac
Git 克隆备选方式
git clone https://github.com/pjt222/agent-almanac.git ~/.claude/skills/fail-early-pattern

在 Claude Code 中复制并粘贴此命令以安装该技能

技能文档

Fail Early

If fails → fail early, loud, w/ context. Codifies: validate inputs at boundaries, guard clauses reject bad state before propagates, err msgs answer what failed, where, why, how to fix.

Use When

  • Writing/reviewing fns accepting external input (user data, API, file)
  • Input validation before CRAN submission
  • Refactor silent wrong results → errors
  • Review PRs for err-handling quality
  • Harden internal APIs vs invalid args

In

  • Required: Fn/module to apply pattern
  • Required: Trust boundaries (where external data enters)
  • Optional: Existing err-handling to refactor
  • Optional: Target language (default R; also Python, TypeScript, Rust)

Do

Step 1: Trust Boundaries

Map external data entry. Points needing validation:

  • Public API fns (exported package)
  • User-facing params
  • File I/O (configs, data, uploads)
  • Net responses (APIs, DBs)
  • Env vars + system config

Internal helpers called only by validated code generally no redundant validation.

→ List entry points where untrusted data crosses.

If err: unclear → trace backwards from err logs/bug reports → find where bad data entered.

Step 2: Guard Clauses at Entry

Validate at top of each public fn before work.

R (base):

calculate_summary <- function(data, method = c("mean", "median", "trim"), trim_pct = 0.1) {
  # Guard: type check
  if (!is.data.frame(data)) {
    stop("'data' must be a data frame, not ", class(data)[[1]], call. = FALSE)
  }
  # Guard: non-empty
  if (nrow(data) == 0L) {
    stop("'data' must have at least one row", call. = FALSE)
  }
  # Guard: argument matching
  method <- match.arg(method)
  # Guard: range check
  if (!is.numeric(trim_pct) || trim_pct < 0 || trim_pct > 0.5) {
    stop("'trim_pct' must be a number between 0 and 0.5, got: ", trim_pct, call. = FALSE)
  }
  # --- All guards passed, begin real work ---
  # ...
}

R (rlang/cli — preferred for packages):

calculate_summary <- function(data, method = c("mean", "median", "trim"), trim_pct = 0.1) {
  rlang::check_required(data)
  if (!is.data.frame(data)) {
    cli::cli_abort("{.arg data} must be a data frame, not {.cls {class(data)}}.")
  }
  if (nrow(data) == 0L) {
    cli::cli_abort("{.arg data} must have at least one row.")
  }
  method <- rlang::arg_match(method)
  if (!is.numeric(trim_pct) || trim_pct < 0 || trim_pct > 0.5) {
    cli::cli_abort("{.arg trim_pct} must be between 0 and 0.5, not {.val {trim_pct}}.")
  }
  # ...
}

General (TypeScript):

function calculateSummary(data: DataFrame, method: Method, trimPct: number): Summary {
  if (data.rows.length === 0) {
    throw new Error(`data must have at least one row`);
  }
  if (trimPct < 0 || trimPct > 0.5) {
    throw new RangeError(`trimPct must be between 0 and 0.5, got: ${trimPct}`);
  }
  // ...
}

→ Every public fn opens w/ guards rejecting invalid before side effects/computation.

If err: validation long (>15 lines of guards) → extract validate_* helper or stopifnot() for simple type assertions.

Step 3: Meaningful Err Msgs

Every msg answers 4:

  1. What failed — param/op
  2. Where — fn name/context (auto w/ cli::cli_abort)
  3. Why — expected vs received
  4. How to fix — when fix non-obvious

Good:

# What + Why (expected vs. actual)
stop("'n' must be a positive integer, got: ", n, call. = FALSE)

# What + Why + How to fix
cli::cli_abort(c(
  "{.arg config_path} does not exist: {.file {config_path}}",
  "i" = "Create it with {.run create_config({.file {config_path}})}."
))

# What + context
cli::cli_abort(c(
  "Column {.val {col_name}} not found in {.arg data}.",
  "i" = "Available columns: {.val {names(data)}}"
))

Bad:

stop("Error")                    # What failed? No idea
stop("Invalid input")           # Which input? What's wrong with it?
stop(paste("Error in step", i)) # No actionable information

→ Msgs self-documenting — dev seeing first time diagnose + fix w/o reading source.

If err: review 3 most recent bug reports. Required reading source to understand → msgs need improvement.

Step 4: Prefer stop() vs warning()

stop() (or cli::cli_abort()) when can't produce correct result. warning() only when still meaningful but caller should know.

Rule: User could silently get wrong answer → stop() not warning().

# CORRECT: stop when result would be wrong
read_config <- function(path) {
  if (!file.exists(path)) {
    stop("Config file not found: ", path, call. = FALSE)
  }
  yaml::read_yaml(path)
}

# CORRECT: warn when result is still usable
summarize_data <- function(data) {
  if (any(is.na(data$value))) {
    warning(sum(is.na(data$value)), " NA values dropped from 'value' column", call. = FALSE)
    data <- data[!is.na(data$value), ]
  }
  # proceed with valid data
}

stop() for incorrect results; warning() for degraded-but-valid.

If err: audit existing warning() calls. Returns nonsense after → change to stop().

Step 5: Assertions for Internal Invariants

"Should never happen" → assertions. Catches programmer errs during dev:

# R: stopifnot for internal invariants
process_chunk <- function(chunk, total_size) {
  stopifnot(
    is.list(chunk),
    length(chunk) > 0,
    total_size > 0
  )
  # ...
}

# R: explicit assertion with context
merge_results <- function(left, right) {
  if (ncol(left) != ncol(right)) {
    stop("Internal error: column count mismatch (", ncol(left), " vs ", ncol(right),
         "). This is a bug — please report it.", call. = FALSE)
  }
  # ...
}

→ Invariants asserted → bugs surface immediately at violation site, not 3 calls later cryptic.

If err: stopifnot() msgs too cryptic → switch explicit if/stop w/ context.

Step 6: Refactor Anti-Patterns

Common anti-patterns:

Anti-pattern 1: Empty tryCatch (swallowing)

# BEFORE: Error silently disappears
result <- tryCatch(
  parse_data(input),
  error = function(e) NULL
)

# AFTER: Log, re-throw, or return a typed error
result <- tryCatch(
  parse_data(input),
  error = function(e) {
    cli::cli_abort("Failed to parse input: {e$message}", parent = e)
  }
)

Anti-pattern 2: Defaults masking bad input

# BEFORE: Caller never knows their input was ignored
process <- function(x = 10) {
  if (!is.numeric(x)) x <- 10  # silently replaces bad input
  x * 2
}

# AFTER: Tell the caller about the problem
process <- function(x = 10) {
  if (!is.numeric(x)) {
    stop("'x' must be numeric, got ", class(x)[[1]], call. = FALSE)
  }
  x * 2
}

Anti-pattern 3: suppressWarnings as fix

# BEFORE: Hiding the symptom instead of fixing the cause
result <- suppressWarnings(as.numeric(user_input))

# AFTER: Validate explicitly, handle the expected case
if (!grepl("^-?\\d+\\.?\\d*$", user_input)) {
  stop("Expected a number, got: '", user_input, "'", call. = FALSE)
}
result <- as.numeric(user_input)

Anti-pattern 4: Catch-all handlers

# BEFORE: Every error treated the same
tryCatch(
  complex_operation(),
  error = function(e) message("Something went wrong")
)

# AFTER: Handle specific conditions, let unexpected ones propagate
tryCatch(
  complex_operation(),
  custom_validation_error = function(e) {
    cli::cli_warn("Validation issue: {e$message}")
    fallback_value
  }
  # Unexpected errors propagate naturally
)

→ Anti-patterns replaced w/ explicit validation or specific err handling.

If err: remove tryCatch causes cascading → upstream has validation gap. Fix source not symptom.

Step 7: Validate Refactoring

Run tests to confirm err paths work:

# Verify error messages are triggered
testthat::expect_error(calculate_summary("not_a_df"), "must be a data frame")
testthat::expect_error(calculate_summary(data.frame()), "at least one row")
testthat::expect_error(calculate_summary(mtcars, trim_pct = 2), "between 0 and 0.5")

# Verify valid inputs still work
testthat::expect_no_error(calculate_summary(mtcars, method = "mean"))
# Run full test suite
Rscript -e "devtools::test()"

→ All tests pass. Err-path tests confirm bad input triggers expected msg.

If err: existing tests relied on silent failures (returning NULL on bad input) → update to expect new err.

Check

  • Every public fn validates inputs before work
  • Err msgs: what, where, why, how to fix
  • stop() for incorrect results
  • warning() only degraded-but-valid
  • No empty tryCatch swallowing
  • No suppressWarnings() as validation substitute
  • No defaults silently masking invalid
  • Invariants use stopifnot() or explicit assertions
  • Err-path tests per guard
  • Test suite passes post-refactor

Traps

  • Validate too deep: Validate at boundaries (public API), not every internal helper. Over-validation adds noise + hurts perf.
  • Msgs no context: "Invalid input" forces caller guess. Always param name, expected type/range, actual value.
  • warning() when mean stop(): Returns garbage after warn → wrong silently. Use stop(), let caller decide.
  • Swallow in tryCatch: tryCatch(..., error = function(e) NULL) hides bugs. Must catch → log or re-throw w/ context.
  • Forget call. = FALSE: R stop("msg") includes call by default — noisy end users. Use call. = FALSE user-facing. cli::cli_abort() does auto.
  • Validate in tests not code: Tests verify behavior not protect production. Validation in fn itself.
  • Wrong R binary hybrid systems: WSL/Docker, Rscript may resolve cross-platform wrapper not native R. Check which Rscript && Rscript --version. Prefer native (/usr/local/bin/Rscript Linux/WSL). See Setting Up Your Environment.

  • write-testthat-tests — tests verifying err paths
  • review-pull-request — review for missing validation + silent failures
  • review-software-architecture — err-handling strategy system level
  • create-skill — new skills following agentskills.io
  • security-audit-codebase — security-focused review overlapping validation

GitHub 仓库

pjt222/agent-almanac
路径: i18n/caveman-ultra/skills/fail-early-pattern
0
agentsagentskillsai-assisted-developmentclaude-codeskillsteams

相关推荐技能

evaluating-llms-harness

测试

该Skill通过60+个学术基准测试(如MMLU、GSM8K等)评估大语言模型质量,适用于模型对比、学术研究及训练进度追踪。它支持HuggingFace、vLLM和API接口,被EleutherAI等行业领先机构广泛采用。开发者可通过简单命令行快速对模型进行多任务批量评估。

查看技能

cloudflare-cron-triggers

测试

这个Claude Skill提供了关于Cloudflare Cron Triggers的完整知识库,用于通过cron表达式定时执行Workers。它支持配置周期性任务、维护作业和自动化工作流,并能处理常见的cron触发错误。开发者可以用它来设置定时任务、测试cron处理器,并集成Workflows和Green Compute功能。

查看技能

webapp-testing

测试

该Skill为开发者提供了基于Playwright的本地Web应用测试工具集,支持自动化测试前端功能、调试UI行为、捕获屏幕截图和查看浏览器日志。它包含管理服务器生命周期的辅助脚本,可直接作为黑盒工具运行而无需阅读源码。适用于需要快速验证本地Web应用界面和交互功能的开发场景。

查看技能

finishing-a-development-branch

测试

这个Skill用于开发分支完成后的集成决策,当代码实现完成且测试通过时,它会引导开发者选择合适的工作流。它首先验证测试状态,然后提供合并、创建PR或清理等结构化选项。核心价值在于确保代码质量的同时,标准化分支收尾流程。

查看技能