## The shape

```ruby
Slack.for(account).post(message)
```

One entry point. Behind it, two implementations with the same methods, chosen by whether the
credential is set. Nothing that calls it knows or cares which it got. `app/adapters/billing.rb` is
the reference version, and `app/adapters/bot_check.rb` is the short one worth reading first.

The `add-integration` skill generates exactly this, including the fake and the spec. Use it rather
than writing the pattern from memory, because the part people get wrong is making the fake
deterministic rather than a stub that returns nil.

## Why not just stub it in the specs

Stubbing works and it rots. Each spec stubs the shape it happens to need, the stubs drift from the
real client's interface, and the suite goes green against an API that changed six months ago. A
single fake is one place that has to stay honest, and it is exercised by every spec that touches the
feature.

It also means development works with no account at all, which is a different and larger benefit: a
new clone can run the whole flow before anyone has requested a key.

## Never call the API from a controller or a model

That is the rule the pattern exists to enforce. A third-party call inside a controller cannot be
faked, so it has to be stubbed, which means it cannot be exercised, which means it breaks quietly.
The seam is the point.

## Make the fake behave, not just respond

A fake that returns `true` for everything passes tests and teaches you nothing. Model the failure
cases you actually care about: a rate limit, a missing record, an invalid token. Those are the
branches your error handling exists for and the ones that never run otherwise.

## Verify

```bash
bin/check
```

The suite should pass with no credentials set anywhere, on a machine with no network access to the
provider. If it does not, something is calling the real service, and that is the thing to find.

## Then test against the real one, once

Before launch, run the flow with a real key in a test or sandbox mode. The fake proves your code is
consistent; only the real service proves your assumptions about it were right.
