| name | defense-in-depth |
| description | Use when implementing validation or safety checks. Multi-layer validation approach prevents bugs through redundant safeguards. Makes bugs structurally impossible. |
Defense in Depth
Core Principle
Make bugs structurally impossible through multiple independent layers of validation.
Overview
Single-layer protection is insufficient. Defense in depth adds multiple independent validation layers so that if one fails, others catch the problem. Different code paths require different validations.
When to Use This Skill
- Implementing data validation
- Adding safety checks
- Preventing invalid state bugs
- Handling user input
- Processing external data
- Critical operations that must not fail
The Four Layers
Layer 1: Entry Point Validation
Purpose: Reject bad input at API boundaries
Where: Controllers, API endpoints, function entry points
What to validate:
- Type correctness
- Required fields present
- Format validity (email, phone, etc.)
- Basic constraints (min/max, length)
Example:
public function createUser(Request $request)
{
$validated = $request->validate([
'email' => 'required|email|max:255',
'password' => 'required|min:8',
'age' => 'required|integer|min:18',
]);
}
Layer 2: Business Logic Validation
Purpose: Enforce operational requirements
Where: Service classes, domain logic
What to validate:
- Business rules
- State transitions
- Relationships
- Domain constraints
Example:
public function createUser(array $data): User
{
if ($this->userRepository->emailExists($data['email'])) {
throw new ValidationException('Email already registered');
}
if ($data['age'] < 18) {
throw new ValidationException('Must be 18 or older');
}
}
Layer 3: Environment Guards
Purpose: Context-specific safety checks
Where: Before operations, database queries, external calls
What to check:
- Database connection exists
- Required services available
- File permissions correct
- Network connectivity
- Resource limits
Example:
public function processPayment(Payment $payment): void
{
if (app()->environment('production') && !config('payment.gateway_enabled')) {
throw new RuntimeException('Payment gateway not configured for production');
}
if ($payment->amount > config('payment.max_amount')) {
throw new RuntimeException('Payment exceeds maximum allowed amount');
}
}
Layer 4: Debug Instrumentation
Purpose: Forensic logging for debugging
Where: Throughout critical paths
What to log:
- Input values
- State transitions
- Decision points
- Output values
Example:
public function transferFunds(Account $from, Account $to, float $amount): void
{
Log::info('Transfer initiated', [
'from_account' => $from->id,
'to_account' => $to->id,
'amount' => $amount,
'from_balance_before' => $from->balance,
'to_balance_before' => $to->balance,
]);
Log::info('Transfer completed', [
'from_balance_after' => $from->balance,
'to_balance_after' => $to->balance,
]);
}
Complete Example: User Registration
public function register(RegisterRequest $request)
{
$validated = $request->validated();
$user = $this->userService->register($validated);
return response()->json(['user' => $user], 201);
}
public function register(array $data): User
{
if ($this->userRepository->emailExists($data['email'])) {
throw new DuplicateEmailException('Email already registered');
}
$domain = substr($data['email'], strpos([], ) + );
(->()) {
();
}
(([])) {
= ::([])->age;
( < ) {
();
}
}
->();
}
{
(!DB::()->()) {
();
}
(!()->()) {
();
}
::(, [
=> [],
=> (),
]);
= ::([
=> [],
=> ::([]),
]);
::(, [
=> ->id,
=> ->email,
]);
;
}
Why All Layers Matter
Scenario: Layer 1 Only
public function createUser(Request $request)
{
$validated = $request->validate(['email' => 'required|email']);
User::create($validated);
}
Scenario: All Layers
$validated = $request->validate(['email' => 'required|email']);
if ($this->emailExists($validated['email'])) {
throw new ValidationException('Duplicate email');
}
if (!DB::connection()->getPdo()) {
throw new DatabaseException('Database unavailable');
}
Log::info('Creating user', ['email' => $validated['email']]);
User::create($validated);
Common Validation Patterns
Email Validation (All Layers)
'email' => 'required|email'
- Unique in database
- Domain not blacklisted
- Not a disposable email service
- Email service available
- SMTP configured
- Log email (sanitized)
- Log validation results
Payment Processing (All Layers)
'amount' => 'required|numeric|min:0.01'
'currency' => 'required|in:USD,EUR,GBP'
- Amount within limits
- Account has sufficient funds
- Payment method valid
- Payment gateway available
- SSL certificate valid
- Fraud detection service up
- Log all payment attempts
- Log amounts and currencies
- Log success/failure
File Upload (All Layers)
'file' => 'required|file|max:10240|mimes:jpg,png,pdf'
- User has upload quota remaining
- File name not duplicate
- Content passes virus scan
- Disk space available
- Directory writable
- Virus scanner available
- Log file details
- Log storage location
- Log processing results
Real-World Impact
Example from Production:
Before Defense in Depth:
public function updateProfile(Request $request, User $user)
{
$data = $request->validate(['bio' => 'string|max:500']);
$user->update($data);
}
After Defense in Depth:
public function updateProfile(Request $request, User $user)
{
$data = $request->validate([
'bio' => 'string|max:500',
]);
if ($this->containsSensitiveData($data['bio'])) {
throw new ValidationException('Bio contains restricted content');
}
if (!$user->can('update', $user)) {
throw new UnauthorizedException();
}
Log::info('Profile update', [
'user_id' => $user->id,
'old_bio' => $user->bio,
'new_bio' => $data['bio'],
]);
$user->update($data);
Log::info('Profile updated successfully', [ => ->id]);
}
Integration with Other Skills
Use with:
test-driven-development - Write tests for each layer
code-review - Verify all layers present
systematic-debugging - Logs help identify which layer failed
Complements:
database-backup - Another safety layer
verification-before-completion - Validate defenses work
Checklist for Defense in Depth
For any data processing or critical operation:
Common Mistakes
Mistake 1: Only One Layer
public function createOrder(Request $request)
{
$validated = $request->validate(['product_id' => 'required']);
Order::create($validated);
}
Mistake 2: Duplicate Validation Logic
$request->validate(['email' => 'email']);
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { ... }
Mistake 3: No Logging
public function criticalOperation()
{
}
Log::info('Critical operation started');
Log::info('Critical operation completed');
Authority
This skill is based on:
- Security best practice: Defense in depth principle
- Industry standard: Multiple validation layers prevent bugs
- Real production experience: Single-layer validation fails
- Evidence-based: Reduces bugs by catching at multiple points
Social Proof: Major companies (Google, Amazon, Microsoft) use layered validation.
Your Commitment
When implementing validation:
Bottom Line: One layer of validation is not enough. Different layers catch different problems. Implement all four layers to make bugs structurally impossible.