MCP HubMCP Hub
스킬 목록으로 돌아가기

fail-early-pattern

pjt222
업데이트됨 Yesterday
3 조회
17
2
17
GitHub에서 보기
테스팅aiapidesign

정보

이 스킬은 가드 절과 단언문을 사용해 입력을 검증하고 오류를 즉시 감지하는 '빠른 실패' 패턴을 구현합니다. R 언어의 실용적 예시와 다국어 가이드를 통해 견고한 함수 작성과 API 강화 방법을 제공합니다. 외부 입력 수신, 무음 실패 리팩터링, 오류 처리 품질 코드 검토 시 활용하세요.

빠른 설치

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에서 이 명령을 복사하여 붙여넣어 스킬을 설치하세요

문서

早敗

若物將敗,宜盡早、盡響、盡帶語境而敗。此技定早敗之模式:於系統邊界驗輸入、以 guard 子句於壞態擴前拒之、書答敗、於何處何以何以修之錯誤訊息。

適用時機

  • 書或審受外部輸入之函數(使用者數據、API 回應、檔內容)
  • CRAN 提交前為包函數加輸入驗
  • 重構默生誤結而非報錯之代碼
  • 審 pull request 之錯處品質
  • 加固內部 API 以禦誤參

輸入

  • 必要:欲施此模式之函數或模組
  • 必要:信任邊界之識(外部數據入處)
  • 選擇性:待重構之既有錯處代碼
  • 選擇性:目標語言(預設:R;亦適於 Python、TypeScript、Rust)

步驟

步驟一:識信任邊界

映外部數據入系統之處。此為需驗之點:

  • 公 API 函數(R 包中所匯出之函數)
  • 面對使用者之參數
  • 檔 I/O(讀配置、數據檔、使用者上傳)
  • 網絡回應(API 呼、DB 查詢)
  • 環境變量與系統配置

僅由爾自身已驗代碼呼之內部輔助函數通常無需冗餘之驗。

預期: 不信任數據跨入爾代碼之入口清單。

失敗時: 若邊界不明,自日誌或錯報中之錯倒追至壞數據首入處。

步驟二:於入口加 guard 子句

於各公函數之頂驗輸入,先於任何工。

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 — 於包所偏好):

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}}.")
  }
  # ...
}

通用(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}`);
  }
  // ...
}

預期: 每公函數以 guard 子句啟,於任何副作用或算前拒誤輸入。

失敗時: 若驗邏輯漸長(>15 行 guard),抽 validate_* 輔或用 stopifnot() 為簡類斷言。

步驟三:書有意之錯誤訊息

每錯訊息宜答四問:

  1. 敗——何參數或操作
  2. 於何處——函數名或語境(用 cli::cli_abort 自動)
  3. 何以——期何與受何
  4. 何以修——於修不顯時

善訊:

# 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)}}"
))

惡訊:

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

預期: 錯訊自文檔——初見錯之開發者不讀源碼即可診並修。

失敗時: 審近三 bug 報告。若有需讀源碼方解者,其錯訊需改。

步驟四:偏 stop() 而避 warning()

於函數不能生正確結果時用 stop()(或 cli::cli_abort())。唯於函數仍可生有意之結果而呼者宜知之慮時用 warning()

約則: 若使用者可默得誤答,則此為 stop(),非 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() 用於將生誤結之條件;warning() 保於降級而仍有效之結果。

失敗時: 審既有之 warning() 呼。若函數於警後返無義之物,改為 stop()

步驟五:以斷言持內部不變

於「正確代碼中不當生」之條件,用斷言。此於開發中捕程式員之錯:

# 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)
  }
  # ...
}

預期: 內部不變已斷,故 bug 於違處立顯,非三呼後帶隱晦錯。

失敗時:stopifnot() 之訊過隱,改用附語境之顯 if/stop

步驟六:重構反模式

識並修此常反模式:

反模式一:空 tryCatch(吞錯)

# 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)
  }
)

反模式二:預設值遮誤輸入

# 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
}

反模式三:以 suppressWarnings 為修

# 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)

反模式四:通捕異常處理

# 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
)

預期: 反模式已換為顯驗或具體錯處。

失敗時: 若移 tryCatch 致連鎖敗,則上游代碼有驗之罅。修源,非症。

步驟七:驗早敗之重構

執測試以確錯路正行:

# 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()"

預期: 所有測試過。錯路測試確誤輸入觸發所期之錯訊。

失敗時: 若既有測試倚默敗(如於誤輸入返 NULL),更之以期新之錯。

驗證

  • 每公函數於工前驗其輸入
  • 錯訊答:何敗、於何處、何以、何以修
  • stop() 用於生誤結之條件
  • warning() 唯用於降級而仍有效之結果
  • 無默吞錯之空 tryCatch
  • 無以 suppressWarnings() 代正驗
  • 無默遮誤輸入之預設值
  • 內部不變用 stopifnot() 或顯斷言
  • 每驗 guard 有錯路測試
  • 重構後測試套件過

常見陷阱

  • 驗過深:於信任邊界(公 API)驗,非於每內部輔助。過驗加噪害效能。
  • 無語境之錯訊"Invalid input" 迫呼者猜。恒含參名、期類/範圍、實所受之值。
  • 本意 stop() 而用 warning():若函數於警後返垃圾,則呼者默得誤答。用 stop() 令呼者決如何處。
  • 於 tryCatch 吞錯tryCatch(..., error = function(e) NULL) 藏 bug。若必捕,記或以加語境再拋。
  • 忘 call. = FALSE:於 R,stop("msg") 預設含呼,於終端使用者為噪。於面對使用者之函數用 call. = FALSEcli::cli_abort() 自動為之。
  • 於測試而非代碼中驗:測試驗行為而不護生產呼者。驗屬函數本身。
  • 混合系統之誤 R 二進制:於 WSL 或 Docker,Rscript 或解為跨平台包裝而非原生 R。以 which Rscript && Rscript --version 察。偏原生 R 二進制(如 Linux/WSL 之 /usr/local/bin/Rscript)以求可靠。見 設環境 查 R 路徑配置。

相關技能

  • write-testthat-tests - 書驗錯路之測試
  • review-pull-request - 審代碼缺驗與默敗
  • review-software-architecture - 於系統層評錯處策略
  • create-skill - 循 agentskills.io 標準建新技能
  • security-audit-codebase - 與輸入驗重疊之以安全為焦之審

GitHub 저장소

pjt222/agent-almanac
경로: i18n/wenyan-lite/skills/fail-early-pattern
0
agentsagentskillsai-assisted-developmentclaude-codeskillsteams

연관 스킬

evaluating-llms-harness

테스팅

이 Claude Skill은 MMLU, GSM8K를 포함한 60개 이상의 표준화된 학술 과제에서 LLM 성능을 벤치마크하기 위해 lm-evaluation-harness를 실행합니다. 개발자들이 모델 품질을 비교하고, 학습 진행 상황을 추적하거나 학술 결과를 보고할 수 있도록 설계되었습니다. 이 도구는 HuggingFace와 vLLM 모델을 포함한 다양한 백엔드를 지원합니다.

스킬 보기

cloudflare-cron-triggers

테스팅

이 스킬은 cron 표현식을 사용하여 Worker를 스케줄링하기 위한 Cloudflare Cron Triggers 구현에 관한 포괄적인 지식을 제공합니다. 주기적 작업, 유지보수 작업, 자동화된 워크플로우 설정 방법을 다루며, 잘못된 cron 표현식이나 시간대 문제 같은 일반적인 이슈들을 해결하는 방법을 포함합니다. 개발자들은 이를 통해 스케줄된 핸들러 구성, cron 트리거 테스트, Workflows 및 Green Compute와의 연동 작업을 수행할 수 있습니다.

스킬 보기

webapp-testing

테스팅

이 Claude Skill은 Python 스크립트를 통해 로컬 웹 애플리케이션을 테스트하기 위한 Playwright 기반 툴킷을 제공합니다. 프론트엔드 검증, UI 디버깅, 스크린샷 캡처, 로그 확인 기능을 지원하며 서버 라이프사이클을 관리합니다. 브라우저 자동화 작업에 사용하되 컨텍스트 오염을 방지하기 위해 소스 코드를 읽지 않고 스크립트를 직접 실행하세요.

스킬 보기

finishing-a-development-branch

테스팅

이 스킬은 테스트 통과를 확인한 후 체계적인 통합 옵션을 제시하여 개발자가 완성된 작업을 마무리하도록 돕습니다. 구현이 완료된 후 머지, PR 생성, 브랜치 정리와 같은 워크플로우를 안내합니다. 코드가 준비되고 테스트가 완료되었을 때 개발 프로세스를 체계적으로 마무리하기 위해 사용하세요.

스킬 보기