# Guard Clause

## Problem

Deeply nested `if-else` chains make functions hard to read. The "happy path" is buried inside multiple layers of indentation. Validation logic is scattered throughout the function body, and it's easy to miss a case.

## Solution

Place validation checks at the **top** of a function. Each check tests for an invalid condition and immediately returns an error or raises an exception. Once all guards pass, the remaining code is the clean, un-nested happy path.

```
function process(input):
    if input is null:       return error("input required")
    if input.value < 0:     return error("value must be non-negative")
    if not authorized():    return error("not authorized")

    // Happy path — flat, readable
    result = compute(input)
    return success(result)
```

## When to Use

- Functions with multiple preconditions.
- Any public API boundary (HTTP handlers, SDK methods, CLI commands).
- When deeply nested conditionals are hurting readability.

## When to Avoid

- Simple functions with zero or one precondition — a guard adds noise.
- When validation is better centralised in a middleware or decorator.
- Performance-critical inner loops where early returns complicate branch prediction (rare).

## Pseudocode

```
function create_user(name, email, age):
    if name is empty:       raise "name required"
    if email is empty:      raise "email required"
    if not valid_email(email): raise "invalid email format"
    if age < 0 or age > 150: raise "age out of range"

    user = User(name, email, age)
    save(user)
    return user
```

## Python

```python
"""Guard Clause pattern in Python."""
import re


class ValidationError(Exception):
    pass


def create_user(name: str, email: str, age: int) -> dict:
    """Create a user with guard clauses at the top."""
    # Guard: name required
    if not name or not name.strip():
        raise ValidationError("name is required")

    # Guard: email required and valid
    if not email or not email.strip():
        raise ValidationError("email is required")
    if not re.match(r"^[^@]+@[^@]+\.[^@]+$", email):
        raise ValidationError(f"invalid email format: {email}")

    # Guard: age in range
    if not isinstance(age, int) or age < 0 or age > 150:
        raise ValidationError(f"age out of range: {age}")

    # Happy path — flat, clean
    user = {"name": name.strip(), "email": email.strip().lower(), "age": age}
    print(f"  Created user: {user}")
    return user


def test_valid_user():
    user = create_user("Alice", "alice@example.com", 30)
    assert user["name"] == "Alice"
    print("PASS: valid user created")


def test_empty_name_rejected():
    try:
        create_user("", "a@b.com", 25)
        assert False, "Expected ValidationError"
    except ValidationError as e:
        assert "name is required" in str(e)
        print(f"PASS: empty name rejected: {e}")


def test_invalid_email_rejected():
    try:
        create_user("Bob", "not-an-email", 25)
        assert False, "Expected ValidationError"
    except ValidationError as e:
        assert "invalid email" in str(e)
        print(f"PASS: invalid email rejected: {e}")


def test_negative_age_rejected():
    try:
        create_user("Carol", "c@d.com", -5)
        assert False, "Expected ValidationError"
    except ValidationError as e:
        assert "age out of range" in str(e)
        print(f"PASS: negative age rejected: {e}")


def test_boundary_age_accepted():
    user = create_user("Dave", "d@e.com", 0)
    assert user["age"] == 0
    print("PASS: age 0 accepted (boundary)")


if __name__ == "__main__":
    test_valid_user()
    test_empty_name_rejected()
    test_invalid_email_rejected()
    test_negative_age_rejected()
    test_boundary_age_accepted()
    print("\nAll Guard Clause tests passed.")
```

## Go

```go
// guard_clause.go
package main

import (
	"fmt"
	"regexp"
	"strings"
)

// ── Domain ───────────────────────────────────────────────────

type User struct {
	Name  string
	Email string
	Age   int
}

var emailRe = regexp.MustCompile(`^[^@]+@[^@]+\.[^@]+$`)

func CreateUser(name, email string, age int) (*User, error) {
	// Guard: name required
	if strings.TrimSpace(name) == "" {
		return nil, fmt.Errorf("name is required")
	}

	// Guard: email required and valid
	if strings.TrimSpace(email) == "" {
		return nil, fmt.Errorf("email is required")
	}
	if !emailRe.MatchString(email) {
		return nil, fmt.Errorf("invalid email format: %s", email)
	}

	// Guard: age in range
	if age < 0 || age > 150 {
		return nil, fmt.Errorf("age out of range: %d", age)
	}

	// Happy path
	user := &User{
		Name:  strings.TrimSpace(name),
		Email: strings.ToLower(strings.TrimSpace(email)),
		Age:   age,
	}
	fmt.Printf("  Created user: %+v\n", user)
	return user, nil
}

// ── Tests ────────────────────────────────────────────────────

func testValidUser() {
	user, err := CreateUser("Alice", "alice@example.com", 30)
	if err != nil {
		panic(fmt.Sprintf("unexpected error: %v", err))
	}
	if user.Name != "Alice" {
		panic("name mismatch")
	}
	fmt.Println("PASS: valid user created")
}

func testEmptyNameRejected() {
	_, err := CreateUser("", "a@b.com", 25)
	if err == nil {
		panic("expected error")
	}
	fmt.Printf("PASS: empty name rejected: %v\n", err)
}

func testInvalidEmailRejected() {
	_, err := CreateUser("Bob", "not-an-email", 25)
	if err == nil {
		panic("expected error")
	}
	fmt.Printf("PASS: invalid email rejected: %v\n", err)
}

func testNegativeAgeRejected() {
	_, err := CreateUser("Carol", "c@d.com", -5)
	if err == nil {
		panic("expected error")
	}
	fmt.Printf("PASS: negative age rejected: %v\n", err)
}

func main() {
	testValidUser()
	testEmptyNameRejected()
	testInvalidEmailRejected()
	testNegativeAgeRejected()
	fmt.Println("\nAll Guard Clause tests passed.")
}
```

## JavaScript

```javascript
// guard_clause.js

function createUser(name, email, age) {
  // Guard: name required
  if (!name || !name.trim()) {
    throw new Error("name is required");
  }

  // Guard: email required and valid
  if (!email || !email.trim()) {
    throw new Error("email is required");
  }
  if (!/^[^@]+@[^@]+\.[^@]+$/.test(email)) {
    throw new Error(`invalid email format: ${email}`);
  }

  // Guard: age in range
  if (!Number.isInteger(age) || age < 0 || age > 150) {
    throw new Error(`age out of range: ${age}`);
  }

  // Happy path
  const user = { name: name.trim(), email: email.trim().toLowerCase(), age };
  console.log(`  Created user: ${JSON.stringify(user)}`);
  return user;
}

// ── Tests ────────────────────────────────────────────────────

function testValidUser() {
  const user = createUser("Alice", "alice@example.com", 30);
  console.assert(user.name === "Alice");
  console.log("PASS: valid user created");
}

function testEmptyNameRejected() {
  try {
    createUser("", "a@b.com", 25);
    throw new Error("Expected error");
  } catch (e) {
    console.assert(e.message.includes("name is required"));
    console.log(`PASS: empty name rejected: ${e.message}`);
  }
}

function testInvalidEmailRejected() {
  try {
    createUser("Bob", "not-an-email", 25);
    throw new Error("Expected error");
  } catch (e) {
    console.assert(e.message.includes("invalid email"));
    console.log(`PASS: invalid email rejected: ${e.message}`);
  }
}

function testNegativeAgeRejected() {
  try {
    createUser("Carol", "c@d.com", -5);
    throw new Error("Expected error");
  } catch (e) {
    console.assert(e.message.includes("age out of range"));
    console.log(`PASS: negative age rejected: ${e.message}`);
  }
}

function testBoundaryAgeAccepted() {
  const user = createUser("Dave", "d@e.com", 0);
  console.assert(user.age === 0);
  console.log("PASS: age 0 accepted (boundary)");
}

testValidUser();
testEmptyNameRejected();
testInvalidEmailRejected();
testNegativeAgeRejected();
testBoundaryAgeAccepted();
console.log("\nAll Guard Clause tests passed.");
```

## Related Patterns

- **Fail-Fast** — guard clauses are the primary mechanism for fail-fast validation at function boundaries.
- **Specification** — a specification object encapsulates complex validation rules that a guard clause can invoke.
- **Result Type** — instead of throwing, a guard clause can return a Result type to make failure explicit without exceptions.
