.claude/skills/guard-clause-validation/SKILL.md
Use when writing any public method that receives parameters, adding guard clauses to existing code, or when a NullReferenceException or ArgumentException is bubbling from deep in the call stack and you need to standardize validation at the entry boundary. Covers ArgumentNullException.ThrowIfNull, ArgumentException.ThrowIfNullOrWhiteSpace, ArgumentOutOfRangeException range guards, a custom Guard static helper with CallerArgumentExpression, and Design by Contract precondition patterns. Domain: Defensive Programming, API Design. Level: Foundational. Tags: validation, guard-clauses, defensive-programming, fail-fast, argument-validation.
npx skillsauth add klod68/littlerae guard-clause-validationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Methods receive invalid arguments and fail deep inside the call stack with cryptic errors (NullReferenceException on line 47 of an internal method). The root cause — a bad input — is obscured.
Validate all incoming arguments at the top of public methods. Throw precise, descriptive exceptions immediately. Never let bad data travel deeper into the system.
Guard the gate, not the hallway. Validate at the public API boundary; internal methods can trust their inputs.
public void ProcessOrder(Order order, string customerId, int quantity)
{
ArgumentNullException.ThrowIfNull(order);
ArgumentException.ThrowIfNullOrWhiteSpace(customerId);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(quantity);
// Safe to proceed — all arguments validated
_repository.Save(order);
}
Guard static helper for richer checks/// <summary>
/// Static guard clause helper for argument validation.
/// </summary>
public static class Guard
{
/// <summary>Throws if <paramref name="value"/> is null.</summary>
public static T NotNull<T>(T? value, string paramName) where T : class
=> value ?? throw new ArgumentNullException(paramName);
/// <summary>Throws if <paramref name="value"/> is null or whitespace.</summary>
public static string NotNullOrEmpty(string? value, string paramName)
=> string.IsNullOrWhiteSpace(value)
? throw new ArgumentException("Value cannot be null or empty.", paramName)
: value;
/// <summary>Throws if <paramref name="value"/> is not within the specified range.</summary>
public static int InRange(int value, int min, int max, string paramName)
=> value < min || value > max
? throw new ArgumentOutOfRangeException(paramName, value, $"Must be between {min} and {max}.")
: value;
/// <summary>Throws if the collection is null or empty.</summary>
public static IReadOnlyCollection<T> NotEmpty<T>(IReadOnlyCollection<T>? collection, string paramName)
=> collection is null || collection.Count == 0
? throw new ArgumentException("Collection cannot be null or empty.", paramName)
: collection;
/// <summary>Throws if the condition is false.</summary>
public static void Requires(bool condition, string message)
{
if (!condition)
throw new ArgumentException(message);
}
}
public IPagedResult<T> Paginate<T>(IReadOnlyCollection<T> items, int page, int pageSize)
{
Guard.NotEmpty(items, nameof(items));
Guard.InRange(page, 1, int.MaxValue, nameof(page));
Guard.InRange(pageSize, 1, 500, nameof(pageSize));
return new PagedResult<T>(items, page, pageSize);
}
| Check | .NET Built-in (6+) | Custom Guard |
|-------|-------------------|-------------|
| Null reference | ArgumentNullException.ThrowIfNull | Guard.NotNull |
| Null/empty string | ArgumentException.ThrowIfNullOrWhiteSpace | Guard.NotNullOrEmpty |
| Negative number | ArgumentOutOfRangeException.ThrowIfNegative | Guard.InRange |
| Range check | ArgumentOutOfRangeException.ThrowIfGreaterThan | Guard.InRange |
| Empty collection | (none built-in) | Guard.NotEmpty |
| Business rule | (none built-in) | Guard.Requires |
| Enum not defined | (none built-in) | Guard.DefinedEnum |
| Layer | Guard? | Example | |-------|--------|---------| | Public API methods | Always | Factory methods, service methods | | Internal methods called from guarded publics | Rarely | Inputs already validated upstream | | Constructor parameters | Always | DI-injected dependencies | | Configuration values | Always | Settings read from config | | Event handlers / callbacks | Always | External input boundary |
required or init instead)ArgumentNullException for null, ArgumentOutOfRangeException for range, ArgumentException for format/content. Never throw base Exception.nameof(param) — it appears in the exception message and helps debugging.CallerArgumentExpression (C# 10+): For custom guards, use [CallerArgumentExpression] to auto-capture the expression name without nameof.public static T NotNull<T>(
T? value,
[CallerArgumentExpression(nameof(value))] string? expression = null) where T : class
=> value ?? throw new ArgumentNullException(expression);
// Usage — no nameof needed
Guard.NotNull(order); // Exception message includes "order" automatically
tools
Use when cross-cutting concerns (logging, metrics, validation, authorization) are tangled into command handlers or service methods, when building database command pipelines with reorderable concerns, or when HTTP client pipelines or message handlers need composable, independently-replaceable processing stages. Covers ICommandInterceptor interface, InterceptorPipeline with reverse-chain construction, zero-cost Empty sentinel to skip overhead when no interceptors are registered, and ConfigureAwait(false) discipline for library code. Domain: Architecture, Cross-Cutting Concerns. Level: Intermediate. Tags: interceptor, pipeline, middleware, decorator, cross-cutting-concerns.
development
Use when writing integration tests for Razor Pages, MVC, or Minimal API applications to validate routing, middleware, page rendering, and HTTP behavior without a browser or live server, or when adding fast smoke tests to a CI pipeline. Covers WebApplicationFactory<Program> setup with public partial class Program, in-memory test server, AngleSharp HTML parsing, CSS selector assertions, redirect and status code testing, and a shared static fixture pattern for minimal per-test startup overhead. Domain: Testing, ASP.NET Core. Level: Intermediate. Tags: integration-testing, webapplicationfactory, razor-pages, anglesharp, http-testing.
development
Use when designing indexes for new tables, diagnosing slow queries that are not using indexes efficiently, reviewing index fragmentation and maintenance, or when the current indexing strategy results in key lookups, table scans, or missing index warnings. Covers clustered index key selection (narrow, unique, ever-increasing), non-clustered index design for query patterns, covering indexes with INCLUDE columns, filtered indexes for subset queries, composite index column ordering, DMV-based monitoring for missing and unused indexes, and rebuild vs reorganize maintenance thresholds. Domain: Database, Performance. Level: Intermediate. Tags: index, sql-server, covering-index, filtered-index, performance, dmv, maintenance.
development
Use when building a searchable in-memory catalog or registry for documentation sites, admin panels, or type/API browsers where you need keyword matching, fuzzy search, and ranked results without an external search engine or database. Covers RegistryService with weighted scoring across name, description, keywords, and method names; Levenshtein fuzzy matching; synonym expansion; category and subcategory filtering; and singleton DI registration for datasets of hundreds to low thousands of items. Domain: Search, Data Access Patterns. Level: Intermediate. Tags: search, registry, fuzzy-matching, in-memory, catalog, filtering.