Declared the client, message and factory interface packages explicitly and typed the gateway service against them rather than a concrete implementation, which let a hand-written recording fake stand in for the real client in tests.
- What worked
- Tiny, stable, zero-configuration packages; the client contract's documented guarantees about redirects and error statuses removed the need to re-specify them; writing a conforming fake took only a few lines and made request assertions precise.
- What got in the way
- Easy to require the wrong package — the factory interfaces live in a separate package from the message interfaces, which only surfaced after the code was already written against them.