# When to mock

Mock at **system boundaries** only:

- External APIs you do not control (Jira, Stripe, email, Infisical)
- Time and randomness
- Databases, sometimes, though a real local test instance is better where one exists
- The filesystem, sometimes

Do **not** mock your own modules, internal collaborators, or anything you control. A test that mocks
what you own is asserting on your own wiring rather than on behaviour, and it will break on the first
refactor that changes nothing a caller can see.

Before mocking any dependency, understand what it actually does. A mock built on a guess about a
dependency's side effects encodes the guess into the suite.

## Designing for substitutability

### Accept dependencies, do not construct them

```typescript
// Easy to substitute at the seam
function processPayment(order: Order, paymentClient: PaymentClient) {
  return paymentClient.charge(order.total);
}

// Welded in: nothing can stand in for the client
function processPayment(order: Order) {
  const client = new StripeClient(process.env.STRIPE_KEY);
  return client.charge(order.total);
}
```

### Prefer an SDK-shaped interface over a generic fetcher

One function per external operation, rather than a single generic call with conditional logic:

```typescript
// GOOD: each operation is independently substitutable
const api = {
  getUser: (id: string) => fetch(`/users/${id}`),
  getOrders: (userId: string) => fetch(`/users/${userId}/orders`),
  createOrder: (data: NewOrder) => fetch("/orders", { method: "POST", body: data }),
};

// BAD: the stand-in needs conditional logic to know what it is being asked for
const api = {
  fetch: (endpoint: string, options: RequestInit) => fetch(endpoint, options),
};
```

The SDK shape means each stand-in returns one specific type, no branching in test setup, an obvious
read of which endpoints a test exercises, and type safety per operation.

## One adapter is not a seam

Introducing a port because a single test wants to substitute at it is indirection, not design. One
adapter means a hypothetical seam; two means a real one, and production-plus-test counts as two. Call
the Skill tool with "codebase-design" when the seam placement itself is in question.
