
Defensive Coding
- 1 installs
- Updated January 28, 2026
- cyarie/cyarie-claude-plugin
cyarie-claude-plugin
About
cyarie-claude-plugin. A curated collection of reusable skills, agents, and processes.
- # cyarie-claude-plugin A curated collection of reusable skills, agents, and pro
- # cyarie-claude-plugin. A curated collection of reusable skills, agents, and pro
Defensive Coding by the numbers
- 1 all-time installs (skills.sh)
- Ranked #1,750 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Data as of Jul 8, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cyarie/cyarie-claude-plugin --skill defensive-codingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| Last updated | January 28, 2026 |
| Repository | cyarie/cyarie-claude-plugin ↗ |
What it does
cyarie-claude-plugin
Files
Defensive Coding
Overview
Defensive coding makes bugs structurally impossible rather than temporarily fixed. The core principle: validate at every layer data passes through, and instrument for observability so failures are diagnosable. A bug isn't fixed until it's impossible.
When to Use
- Invalid data caused a bug deep in the call stack
- Data crosses system boundaries (API → service → storage)
- Multiple code paths reach vulnerable code
- Tests mock intermediate layers
- Debugging production issues is difficult
Core Pattern
Layer 1: Entry Point Validation
Purpose: Reject invalid input at API/system boundary — your first defense line.
def process_config(config_path: str) -> Config:
if not config_path:
raise ValueError("config_path is required")
path = Path(config_path)
if not path.exists():
raise FileNotFoundError(f"Config file not found: {config_path}")
if path.suffix != ".yaml":
raise ValueError(f"Expected .yaml file, got: {path.suffix}")
return _load_config(path)Layer 2: Business Logic Validation
Purpose: Ensure data makes sense for this specific operation.
def calculate_discount(price: float, discount_percent: float) -> float:
if price < 0:
raise ValueError(f"Price cannot be negative: {price=}")
if not 0 <= discount_percent <= 100:
raise ValueError(f"Discount must be 0-100: {discount_percent=}")
return price * (1 - discount_percent / 100)Layer 3: Environment Guards
Purpose: Prevent dangerous operations in specific contexts.
def initialize_repository(target_dir: str) -> None:
path = Path(target_dir).resolve()
# Guard: prevent git init outside temp dir in tests
if os.getenv("ENV") == "test":
if not path.is_relative_to(Path(tempfile.gettempdir())):
raise RuntimeError(f"Refusing init outside temp dir in test: {path}")
# Guard: never init in home directory root
if path == Path.home():
raise RuntimeError(f"Refusing init in home directory root: {path}")
_do_git_init(path)Layer 4: Observability
Purpose: Capture context for forensics when other layers fail.
logger = structlog.get_logger()
def process_order(order_id: str, items: list[dict]) -> OrderResult:
logger.info("order_started", order_id=order_id, item_count=len(items))
try:
result = _do_process(order_id, items)
logger.info("order_completed", order_id=order_id, total=result.total)
return result
except Exception:
logger.exception("order_failed", order_id=order_id)
raiseException Handling Patterns
Chain to Preserve Context
try:
config = load_config(path)
except FileNotFoundError as e:
raise ConfigurationError(f"Failed to load config: {path}") from eHandle at the Right Layer
# Bad — catching too early, losing context
def get_user(user_id: str) -> User:
try:
return db.fetch_user(user_id)
except DatabaseError:
return None # Caller has no idea why
# Good — let it propagate, handle at boundary
@router.get("/users/{user_id}")
def get_user_endpoint(user_id: str) -> Response:
try:
return user_service.get(user_id)
except UserNotFoundError:
return Response(status=404)Observability Checklist
- [ ] Structured JSON logging (not string concatenation)
- [ ] Correlation IDs for request tracing
- [ ] Log at boundaries: entry, exit, errors
- [ ] Include context: IDs, counts, durations
- [ ]
logger.exception()for stack traces on errors - [ ] Sensitive data excluded (passwords, tokens, PII)
Decision Heuristic
| Situation | Layers Needed |
|---|---|
| Public API, simple validation | 1 only |
| Data crosses multiple services | 1 + 2 + 4 |
| Destructive operations (delete, init, write) | 1 + 2 + 3 + 4 |
| Chasing hard-to-reproduce bug | 1 + 2 + 3 + 4 |
| Tests mock intermediate layers | At minimum: 1 + 3 |
Quick Reference
| Layer | Question | Typical Check |
|---|---|---|
| Entry | Is input valid? | Non-empty, exists, correct type |
| Business | Does it make sense here? | Required for operation, within bounds |
| Environment | Safe in this context? | Not in prod, inside temp dir, bulk guard |
| Observability | Can we diagnose failures? | Structured logs, correlation IDs, context |
Common Mistakes
| Mistake | Why It Fails | Correct Approach |
|---|---|---|
| One validation point | Other code paths bypass it | Add entry + business layers minimum |
Bare except: | Catches KeyboardInterrupt, hides bugs | Catch specific exceptions |
except Exception: pass | Silent failure, impossible to debug | Log and re-raise, or handle specifically |
| String logs | Can't query log aggregators | Use structured logging with fields |
| Logging sensitive data | Security/compliance violation | Exclude passwords, tokens, PII |
| No correlation ID | Can't trace across services | Add at entry point |
| Guards only in prod | Test pollution, accidental side effects | Add guards in test too |
Anti-Rationalizations
- "Validation at the entry point is enough" — Other code paths bypass it. Add business layer validation.
- "I'll add logging when there's a bug" — You won't have the context you need. Add it now.
- "Structured logging is overkill" — Until you're grepping gigabytes of logs at 3am.
- "Environment guards slow development" — Less than recovering from
rm -rfin the wrong directory.
Connection to Other Skills
- [howto-code-in-python](../howto-code-in-python/SKILL.md): Exception handling style
- [howto-program-functionally-ish](../howto-program-functionally-ish/SKILL.md): Pure logic (Layer 2) vs side effects (Layers 1, 3, 4)