Shipmind Labs

If your order service knows the difference between an SMS and a push, the notification layer has already leaked.

It starts small. One if-statement for the channel. Then a second one for the provider that needs a different payload shape. Then retry logic in the checkout handler, because that provider times out. A year later, adding a fourth channel means a diff across every feature that ever notified a user, and testing any of it means mocking three vendor SDKs.

We kept hitting this while fanning out notifications to messengers, mobile and web push, so we settled on one rule: the calling code says who, what event, and what data. Nothing else.

Behind that single dispatch call sit provider adapters, one per channel, each owning its own payload translation, its own failure modes, its own retries. Delivery runs in the background, so a slow provider never shows up as a slow API response. We packaged the pattern as an open-source project we call notify-dispatch.

The part that actually decides whether this holds: the dispatch signature has to stay narrow. The moment it grows optional channel-specific fields, you have rebuilt the branch, just further from the code that needed it.

The honest test is adding a channel. If that is a new adapter plus config and nothing else, the abstraction is real.

Where did yours break first — the payload shape, or the per-provider failure semantics?

Was this useful?

Building something similar?

or email hello@shipmindlabs.com