Software design beyond syntax. Fail-fast over fallbacks, explicit over implicit, composition over inheritance. Integrates with fn(args, deps) and Result type patterns.
Enforces software design principles like fail-fast, explicit dependencies, and immutability. Triggers when reviewing code to prevent nullish coalescing chains, type escape hatches, and vague naming.
/plugin marketplace add jagreehal/jagreehal-claude-skills/plugin install jagreehal-claude-skills@jagreehal-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Design rules that complement fn(args, deps), Result types, and validation boundaries.
| Rule | Enforcement |
|---|---|
| Fail-fast over fallbacks | No ?? chains - throw clear errors |
No any, no as | Type escape hatches defeat TypeScript |
| Make illegal states unrepresentable | Discriminated unions, not optional fields |
| Explicit dependencies | fn(args, deps), never new X() inside |
| Domain names only | Never data, utils, helpers, handler |
| No comments | Code should be self-explanatory |
| Immutable by default | Return new values, don't mutate |
Never use nullish coalescing chains:
// WRONG - Hides bugs, debugging nightmare
const name = user?.profile?.name ?? settings?.defaultName ?? 'Unknown';
// CORRECT - Fail immediately with context
if (!user) {
throw new Error(`Expected user, got null. Context: userId=${userId}`);
}
if (!user.profile) {
throw new Error(`User ${user.id} has no profile`);
}
return user.profile.name;
Error message format: Expected [X]. Got [Y]. Context: [debugging info]
Forbidden without explicit user approval:
// FORBIDDEN
const x: any = something;
const y = something as SomeType;
const z = something as unknown as OtherType;
// @ts-ignore
// @ts-expect-error
There is always a type-safe alternative:
// Instead of `as`, use type guards
function isUser(x: unknown): x is User {
return typeof x === 'object' && x !== null && 'id' in x;
}
if (isUser(data)) {
// data is User here
}
// Instead of `any`, use unknown + validation
const data: unknown = JSON.parse(input);
const user = UserSchema.parse(data); // Zod validates and types
Use discriminated unions, not optional fields:
// WRONG - Illegal states possible
type Order = {
status: string;
shippedDate?: Date; // Can be set when status !== 'shipped'
cancelReason?: string; // Can be set when status !== 'cancelled'
};
// CORRECT - Type system prevents illegal states
type Order =
| { status: 'pending'; items: Item[] }
| { status: 'shipped'; items: Item[]; shippedDate: Date; trackingNumber: string }
| { status: 'cancelled'; items: Item[]; cancelReason: string };
If a state combination shouldn't exist, make the type forbid it.
Forbidden generic names:
data, info, itemutils, helpers, common, sharedmanager, handler, processor, service (when vague)Use domain language:
// WRONG
class DataProcessor {
processData(data: any) { }
}
function handleItem(item: Item) { }
const utils = { formatThing };
// CORRECT
class OrderTotalCalculator {
calculate(order: Order): Money { }
}
function shipOrder(order: Order) { }
const priceFormatter = { formatCurrency };
Comments indicate failure to express intent in code:
// WRONG - Comment explains unclear code
// Check if user is admin and not suspended
if (user.role === 'admin' && !user.suspendedAt) { }
// CORRECT - Code is self-documenting
const isActiveAdmin = user.role === 'admin' && !user.suspendedAt;
if (isActiveAdmin) { }
// Or extract to function
function isActiveAdmin(user: User): boolean {
return user.role === 'admin' && !user.suspendedAt;
}
Acceptable comments:
// TODO: with ticket referenceReturn new values, don't mutate inputs:
// WRONG - Mutates input
function addItem(order: Order, item: Item): void {
order.items.push(item); // Caller's object changed!
}
// CORRECT - Returns new value
function addItem(order: Order, item: Item): Order {
return {
...order,
items: [...order.items, item],
};
}
Prefer:
const over let...) over mutationmap/filter/reduce over forEach with mutationWhen mutation IS acceptable:
// OK: Mutation in local scope for performance
function processLargeDataset(items: Item[]): ProcessedItem[] {
const results: ProcessedItem[] = []; // Local mutable array
for (const item of items) {
results.push(transform(item)); // Much faster than spread
}
return results; // Return immutable result
}
When a function uses another object's data more than its own, move the logic:
// FEATURE ENVY - obsessed with Order's internals
function calculateInvoiceTotal(order: Order): Money {
return order.items
.map(i => i.price * i.quantity)
.reduce((a, b) => a + b, 0)
+ order.taxRate * subtotal
+ order.shippingCost;
}
// CORRECT - Logic belongs on Order
class Order {
calculateTotal(): Money {
// Uses this.items, this.taxRate, this.shippingCost
}
}
function createInvoice(order: Order): Invoice {
return new Invoice(order.calculateTotal());
}
Detection: Count references to this vs external objects. More external? Feature envy.
Don't build for hypothetical future needs:
// WRONG - Speculative generalization
interface PaymentProcessor {
process(payment: Payment): Result<Receipt, PaymentError>;
refund(payment: Payment): Result<Receipt, PaymentError>;
partialRefund(payment: Payment, amount: Money): Result<Receipt, PaymentError>;
schedulePayment(payment: Payment, date: Date): Result<Receipt, PaymentError>;
recurringPayment(payment: Payment, schedule: Schedule): Result<Receipt, PaymentError>;
// ... 10 more methods "we might need"
}
// CORRECT - Build what you need now
interface PaymentProcessor {
process(payment: Payment): Result<Receipt, PaymentError>;
}
// Add refund() when requirements actually demand it
"But we might need it" is not a requirement.
// WRONG
function getStatus(user: User): string {
if (user.isAdmin) {
return 'admin';
} else {
return 'user';
}
}
// CORRECT - Early return
function getStatus(user: User): string {
if (user.isAdmin) return 'admin';
return 'user';
}
// WRONG - 4 levels deep
function process(orders: Order[]) {
for (const order of orders) {
for (const item of order.items) {
if (item.inStock) {
if (item.price > 0) {
// deeply nested
}
}
}
}
}
// CORRECT - Extract and flatten
function process(orders: Order[]) {
const items = orders.flatMap(o => o.items);
const validItems = items.filter(isValidItem);
validItems.forEach(processItem);
}
| Principle | Relates To |
|---|---|
| Explicit deps | fn-args-deps pattern |
| Type safety | strict-typescript, validation-boundary |
| Fail-fast | result-types (use err(), not throw) |
| Immutability | Result types are immutable |
| No comments | critical-peer challenges unclear code |
| Temptation | Instead |
|---|---|
Use ?? chain | Fail fast with clear error |
Use any or as | Fix the types properly |
Name it data or utils | Use domain language |
| Write a comment | Make code self-explanatory |
| Mutate a parameter | Return new value |
| Build "for later" | Build what you need now |
Add else branch | Use early return |
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.