<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nikita Navral</title>
    <description>The latest articles on DEV Community by Nikita Navral (@nikita_navral).</description>
    <link>https://dev.to/nikita_navral</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F956914%2F6265910d-ba2b-48fc-863e-6aed19eadf88.png</url>
      <title>DEV Community: Nikita Navral</title>
      <link>https://dev.to/nikita_navral</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nikita_navral"/>
    <language>en</language>
    <item>
      <title>Choosing The Right Message Queue Technology For Your Notification System</title>
      <dc:creator>Nikita Navral</dc:creator>
      <pubDate>Fri, 16 Oct 2026 10:57:47 +0000</pubDate>
      <link>https://dev.to/suprsend/choosing-the-right-message-queue-technology-for-your-notification-system-1ocl</link>
      <guid>https://dev.to/suprsend/choosing-the-right-message-queue-technology-for-your-notification-system-1ocl</guid>
      <description>&lt;p&gt;Your notification system works great at 100 messages a minute. Then your app goes viral, a flash sale hits, and suddenly your OTPs are stuck behind 2 million promotional pushes.&lt;/p&gt;

&lt;p&gt;Sooner or later, every notification system needs a message queue. The hard part is picking one. Kafka, RabbitMQ, SQS, Pub/Sub and Kinesis all "move messages," but they behave very differently when you need ordering, retries, replays or priorities.&lt;/p&gt;

&lt;p&gt;This guide walks through the factors that matter for notifications, compares the five most common options, and ends with a checklist you can use to decide.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, what do notifications need from a queue?
&lt;/h2&gt;

&lt;p&gt;Forget feature lists for a second. Here's what actually matters, with a real scenario for each.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scalability
&lt;/h3&gt;

&lt;p&gt;Your sports app sends a push whenever a team scores. Traffic is quiet until a derby match, then every user gets a notification within seconds. You need a queue that scales out without redesign.&lt;/p&gt;

&lt;h3&gt;
  
  
  Message retention
&lt;/h3&gt;

&lt;p&gt;You send transaction alerts in real time. One night your consumer crashes for 30 minutes. If the queue discards messages that aren't consumed, those users never hear about their money moving.&lt;/p&gt;

&lt;p&gt;With retention, messages wait in the queue (for an hour, a day, longer) and are processed when the consumer comes back.&lt;/p&gt;

&lt;h3&gt;
  
  
  Low latency
&lt;/h3&gt;

&lt;p&gt;A fraud team needs to be alerted the moment a suspicious transaction appears. Delay matters here.&lt;/p&gt;

&lt;p&gt;A caveat: Redis Pub/Sub is often cited for this, but it's fire-and-forget. If no subscriber is listening, the message is gone. For anything you can't afford to lose, use Redis Streams or a durable broker.&lt;/p&gt;

&lt;h3&gt;
  
  
  High throughput
&lt;/h3&gt;

&lt;p&gt;A food delivery app sends status updates ("being prepared", "on its way") for every order. Volume is high, and many consumers need to process it in parallel. This is where Kafka and Kinesis shine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Message ordering
&lt;/h3&gt;

&lt;p&gt;A trading platform sends market alerts. If "price dropped" arrives after "price recovered," users act on stale information. You need ordering guarantees, at least per user or per entity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prioritization
&lt;/h3&gt;

&lt;p&gt;A hospital monitoring system alerts nurses when a patient's heart rate crosses a threshold. That alert can't wait behind a batch of routine reports.&lt;/p&gt;

&lt;p&gt;One thing to know: &lt;strong&gt;most queues don't have native priority.&lt;/strong&gt; The usual pattern is separate queues or topics per priority:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;TOPICS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;critical&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;notif.critical&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# OTP, security, health alerts
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;notif.high&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;# transactional
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;low&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;notif.low&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;# promos, digests
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;TOPICS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;   &lt;span class="c1"&gt;# same user -&amp;gt; same partition -&amp;gt; ordered
&lt;/span&gt;    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each topic gets its own consumers and throughput limits, so a promo blast can't starve your OTPs. (RabbitMQ is the exception, with native priority queues.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Dead letter queues (DLQs)
&lt;/h3&gt;

&lt;p&gt;An e-commerce site sends order confirmations. Some email addresses are invalid and fail every time. Without a DLQ, those messages are retried forever or block the line. With a DLQ, failures move aside where you can inspect them and fix the cause.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"RedrivePolicy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deadLetterTargetArn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:sqs:us-east-1:123456789012:notif-dlq"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"maxReceiveCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's an SQS redrive policy: after 5 failed receives, the message goes to the DLQ.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five contenders, side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;RabbitMQ&lt;/th&gt;
&lt;th&gt;Kafka&lt;/th&gt;
&lt;th&gt;AWS SQS&lt;/th&gt;
&lt;th&gt;Google Pub/Sub&lt;/th&gt;
&lt;th&gt;AWS Kinesis&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Broker with exchanges and queues (AMQP)&lt;/td&gt;
&lt;td&gt;Distributed log, consumer groups&lt;/td&gt;
&lt;td&gt;Queue (pull)&lt;/td&gt;
&lt;td&gt;Topics and subscriptions (push or pull)&lt;/td&gt;
&lt;td&gt;Streams split into shards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ordering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;FIFO per queue&lt;/td&gt;
&lt;td&gt;Per partition&lt;/td&gt;
&lt;td&gt;FIFO queues, per message group&lt;/td&gt;
&lt;td&gt;Per ordering key (opt-in)&lt;/td&gt;
&lt;td&gt;Per shard / partition key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Throughput&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Moderate to high&lt;/td&gt;
&lt;td&gt;Very high&lt;/td&gt;
&lt;td&gt;Very high&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High (shard-bound)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Retention / replay&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Until acked (Streams plugin adds replay)&lt;/td&gt;
&lt;td&gt;Configurable, replayable&lt;/td&gt;
&lt;td&gt;Up to 14 days, no replay&lt;/td&gt;
&lt;td&gt;Configurable, can seek back&lt;/td&gt;
&lt;td&gt;24h up to 365 days, replayable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Native priority&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (use separate topics)&lt;/td&gt;
&lt;td&gt;No (use separate queues)&lt;/td&gt;
&lt;td&gt;No (use separate topics)&lt;/td&gt;
&lt;td&gt;No (use separate streams)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dead letter queue&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dead-letter exchanges&lt;/td&gt;
&lt;td&gt;DIY (a DLQ topic)&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;td&gt;No native DLQ (handle in the consumer)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ops burden&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Moderate (self-host or managed)&lt;/td&gt;
&lt;td&gt;Heavy if self-hosted, managed options exist&lt;/td&gt;
&lt;td&gt;Fully managed&lt;/td&gt;
&lt;td&gt;Fully managed&lt;/td&gt;
&lt;td&gt;Fully managed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Infra cost if self-hosted&lt;/td&gt;
&lt;td&gt;Infra cost if self-hosted&lt;/td&gt;
&lt;td&gt;Pay per request&lt;/td&gt;
&lt;td&gt;Pay per volume&lt;/td&gt;
&lt;td&gt;Pay per shard / volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Access control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;vhosts and permissions&lt;/td&gt;
&lt;td&gt;ACLs&lt;/td&gt;
&lt;td&gt;IAM&lt;/td&gt;
&lt;td&gt;IAM&lt;/td&gt;
&lt;td&gt;IAM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Limits like max message size change over time, so check each provider's docs before you design around them.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one should you pick?
&lt;/h2&gt;

&lt;p&gt;Here are three common situations.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 Startup with low-volume transactional notifications → RabbitMQ (or SQS)
&lt;/h3&gt;

&lt;p&gt;Easy to set up, simple to reason about, and flexible routing. It has native priority queues and dead-letter exchanges, so you get a lot of notification-friendly features without much ceremony. If you're all-in on AWS and want zero ops, SQS is a strong alternative.&lt;/p&gt;

&lt;h3&gt;
  
  
  📈 Mid-sized company, high volume, low latency → Kafka
&lt;/h3&gt;

&lt;p&gt;Distributed, high throughput, and great for fan-out: one event can feed email, push and analytics consumers at once. Replication and replay are strong, and you can reprocess history when something goes wrong. The trade-off is operational complexity, so consider a managed offering.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏢 Enterprise on AWS with many microservices → Kinesis (plus SNS/SQS where it fits)
&lt;/h3&gt;

&lt;p&gt;Fully managed, integrates natively with Lambda and the rest of AWS, and handles large volumes. Kinesis is a streaming service rather than a classic queue, so many teams pair it with SQS or SNS for simple fan-out and retry patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  At-least-once delivery means you need idempotency
&lt;/h2&gt;

&lt;p&gt;Almost every queue above delivers messages &lt;strong&gt;at least once&lt;/strong&gt;. Retries and rebalances can deliver the same message twice, and a user getting the same "payment received" push twice is a bug.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;already_processed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;      &lt;span class="c1"&gt;# e.g. Redis SETNX or DB unique key
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="nf"&gt;send_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;mark_processed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whichever queue you pick, build this in from day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Latency vs throughput: you can't max both
&lt;/h2&gt;

&lt;p&gt;Optimizing for one tends to cost you the other.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Low latency&lt;/strong&gt; needs fast ingest and delivery, which uses more CPU, memory and network per message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High throughput&lt;/strong&gt; relies on batching and parallelism, which can add a little delay per message.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RabbitMQ leans toward low-latency, real-time delivery. Kafka leans toward high-throughput streaming, but it's tunable (settings like batch size and linger time let you trade one for the other). Even then, hardware and architecture set the limits.&lt;/p&gt;

&lt;p&gt;A practical rule: use low-latency settings for your critical queue (OTPs, alerts), and throughput-optimized settings for the low-priority bulk queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  A checklist for deciding
&lt;/h2&gt;

&lt;p&gt;Run through these before you commit.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Define your numbers.&lt;/strong&gt; Message volume, latency target, reliability needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick your trade-off.&lt;/strong&gt; Latency or throughput first?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan for growth.&lt;/strong&gt; Can it scale horizontally as your user base grows?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check ecosystem fit.&lt;/strong&gt; Your languages, frameworks and existing cloud provider.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Count the operational cost.&lt;/strong&gt; Setup complexity, monitoring, and what your team already knows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check security.&lt;/strong&gt; Durability, access control and encryption in transit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budget it.&lt;/strong&gt; Infrastructure, managed service fees and maintenance time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prototype and load-test.&lt;/strong&gt; Don't trust benchmarks, including the ones in this post. Test with your own traffic.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Running multi-cloud or hybrid?
&lt;/h2&gt;

&lt;p&gt;A few things to watch for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multiple queue technologies.&lt;/strong&gt; Different components may use different brokers. Make sure your team can operate all of them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard protocols.&lt;/strong&gt; Prefer widely supported protocols like AMQP or MQTT where you can, and keep message formats consistent to avoid fragmentation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity vs flexibility.&lt;/strong&gt; Cross-environment connectivity adds moving parts. Make sure the flexibility is worth it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;There's no single best message queue, only the best fit for your volume, latency needs, team and cloud. Balance performance, reliability, ease of use and ecosystem, and revisit the decision as your notification system grows.&lt;/p&gt;

&lt;p&gt;And if you'd rather not build and operate this whole layer yourself (queues, priorities, retries, fallbacks), that's what we built &lt;a href="https://www.suprsend.com" rel="noopener noreferrer"&gt;SuprSend&lt;/a&gt; for. Full disclosure: we work on it, so take that into account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your turn:&lt;/strong&gt; which queue are you running notifications on today, and would you pick it again? Tell us in the comments. Horror stories welcome.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>scalability</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Introducing SuprSend Agent</title>
      <dc:creator>Nikita Navral</dc:creator>
      <pubDate>Thu, 15 Oct 2026 07:54:36 +0000</pubDate>
      <link>https://dev.to/suprsend/introducing-suprsend-agent-55p6</link>
      <guid>https://dev.to/suprsend/introducing-suprsend-agent-55p6</guid>
      <description>&lt;p&gt;Quick intro: we're the team behind &lt;a href="https://www.suprsend.com" rel="noopener noreferrer"&gt;SuprSend&lt;/a&gt;, a notification infrastructure platform. Today we're launching &lt;strong&gt;SuprSend Agent&lt;/strong&gt;, and we wanted to share what we built, how it's wired, and the guardrails we put around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 30-second version
&lt;/h2&gt;

&lt;p&gt;SuprSend Agent is an AI agent that lives &lt;strong&gt;inside the SuprSend dashboard&lt;/strong&gt;. You describe what you need in plain English, and it builds, configures and debugs your notification setup for you.&lt;/p&gt;

&lt;p&gt;It's connected to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the full SuprSend API surface&lt;/li&gt;
&lt;li&gt;the product documentation&lt;/li&gt;
&lt;li&gt;agent skills&lt;/li&gt;
&lt;li&gt;domain knowledge about how notifications actually work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So it doesn't just fire off API calls. It understands your use case, picks the right tools, applies the correct configuration, and tests the result.&lt;/p&gt;

&lt;p&gt;Try it: &lt;strong&gt;&lt;a href="https://auth.suprsend.com/?client_id=client_01HQTQ7EHH9GZTE1RGXEPN5RX2&amp;amp;redirect_uri=https%3A%2F%2Fapp.suprsend.com%2Fen%2Fcallback&amp;amp;authorization_session_id=01M4Z7KV9RD4ZWE09P14Q8H9Q2" rel="noopener noreferrer"&gt;SuprSend Agent&lt;/a&gt;&lt;/strong&gt; | Docs: &lt;strong&gt;&lt;a href="https://docs.suprsend.com/reference/agent?_gl=1*15vu7gt*_gcl_au*MjAzODM1MDA1MS4xNzkwMzIxNjUw*_ga*MTQ5OTQ3MjI2Ny4xNzkwMzIxNjUw*_ga_PPDYBESP2L*czE3OTIwNDk1NzIkbzEzJGcxJHQxNzkyMDQ5NTc5JGo1MyRsMCRoMTI2OTEwNDgw" rel="noopener noreferrer"&gt;read the docs&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we built it
&lt;/h2&gt;

&lt;p&gt;Notifications are deceptively deep. One message passes through several layers of logic: who gets it, on which channel, at what time, with what content, and what happens if the user does (or doesn't) act. Every layer has to be configured, tested and iterated.&lt;/p&gt;

&lt;p&gt;That depth is what lets a platform handle real-world use cases. It's also why it takes days to build a mental model before you configure anything.&lt;/p&gt;

&lt;p&gt;The agent shifts where that effort goes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New users&lt;/strong&gt; skip the upfront learning curve. Ask the agent how things work, and let it set up your first workflows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Existing users&lt;/strong&gt; skip the click-through. No more hopping between screens to change a vendor, check an object or tweak a template.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What you can do with it
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Ask anything (without opening another tab)
&lt;/h3&gt;

&lt;p&gt;Data modeling, how a feature works, what an error means, best practices, how to approach an integration. It answers from the docs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"How do preferences cascade from defaults to tenants to end users?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Manage your assets from a chat
&lt;/h3&gt;

&lt;p&gt;The agent can create, retrieve, update and commit across tenants, objects, preferences, vendors, translations and schemas.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Update the SMS vendor for tenant cypher and show me the current preference categories."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. Design and test a notification from one prompt
&lt;/h3&gt;

&lt;p&gt;This is the one we're most excited about. Normally you decide everything yourself: flow structure, timing, branching, channels, and how the content should read on each one.&lt;/p&gt;

&lt;p&gt;Now a prompt like this is enough:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Set up a trial expiry reminder for new users."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Map out the workflow nodes&lt;/li&gt;
&lt;li&gt;Set the time delays&lt;/li&gt;
&lt;li&gt;Add the branching logic&lt;/li&gt;
&lt;li&gt;Pick the right channels&lt;/li&gt;
&lt;li&gt;Design and personalize the templates with variables&lt;/li&gt;
&lt;li&gt;Test the result across every channel&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You describe the outcome. It handles the build.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Ask your analytics anything
&lt;/h3&gt;

&lt;p&gt;A fixed dashboard only answers the questions it was built for. The agent reads your notification data through a &lt;strong&gt;semantic layer over the data lake&lt;/strong&gt;, so you can ask things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which notifications performed worst in the last 30 days?&lt;/li&gt;
&lt;li&gt;What's the best performing template this week?&lt;/li&gt;
&lt;li&gt;How do channels compare across segments and tenants?&lt;/li&gt;
&lt;li&gt;How many workflows are active?&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Show me delivery and open rates for the weekly-digest workflow, split by tenant, for the last 30 days."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It returns the answer, can visualize it as a chart, and can post it straight to Slack.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Debug "why didn't they get it?"
&lt;/h3&gt;

&lt;p&gt;We all know this one. A notification doesn't arrive, and the cause could be a channel issue, a preference opt-out, a tenant override or a vendor failure. Finding it means digging through logs and screens.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"User &lt;a href="mailto:john@cypher.com"&gt;john@cypher.com&lt;/a&gt; says he's not getting emails. What's wrong?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent traces the delivery, finds what stopped it, explains the cause and suggests a fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Cool, but is it safe on a live workspace?"
&lt;/h2&gt;

&lt;p&gt;Fair question, and honestly the one we spent the most time on. The agent acts on real organizational data, so safety is built into every step:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Guardrail&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Workspace-scoped access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Every session is limited to one workspace. A conversation in one can't read or change data in another.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service token auth&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Each session uses a workspace-scoped JWT that expires and auto-refreshes, so access is short-lived by default.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Permission before changes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The agent asks for confirmation before any edit. Updates read the resource's current state first, so nothing is silently overwritten.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No destructive deletes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deletes aren't exposed to the agent at all. Only low-risk, reversible operations (like removing a user from a list) are allowed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Validated, not guessed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;It runs on validated SuprSend schemas and product knowledge, so it uses correct parameters instead of guessing.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Who is it for?
&lt;/h2&gt;

&lt;p&gt;Developers, product managers, marketing, customer success and ops teams. Basically anyone who manages notifications but doesn't want to write integration code for every change. Work that used to wait on engineering can now happen in a conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  A bigger shift
&lt;/h2&gt;

&lt;p&gt;This launch is part of a larger direction: SuprSend is becoming &lt;strong&gt;AI-first notification infrastructure&lt;/strong&gt;, where you operate the platform by describing what you need instead of learning every knob and switch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it and tell us what you think
&lt;/h2&gt;

&lt;p&gt;Start with a first prompt in the dashboard: &lt;strong&gt;&lt;a href="https://auth.suprsend.com/?client_id=client_01HQTQ7EHH9GZTE1RGXEPN5RX2&amp;amp;redirect_uri=https%3A%2F%2Fapp.suprsend.com%2Fen%2Fcallback&amp;amp;authorization_session_id=01M4Z7PMJ3RCPXM2QMZVXSYYFF" rel="noopener noreferrer"&gt;Try SuprSend Agent&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We'd love to hear from you in the comments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What's the first thing you'd ask the agent to build?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What guardrails would you want on an AI agent that touches production config?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What's the most painful notification bug you've ever had to debug?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We read every reply. 👇&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>A complete guide on Notification Service for Modern Applications in 2026</title>
      <dc:creator>Nikita Navral</dc:creator>
      <pubDate>Wed, 14 Oct 2026 10:53:02 +0000</pubDate>
      <link>https://dev.to/suprsend/a-complete-guide-on-notification-service-for-modern-applications-in-2026-128c</link>
      <guid>https://dev.to/suprsend/a-complete-guide-on-notification-service-for-modern-applications-in-2026-128c</guid>
      <description>&lt;p&gt;Most notification services start as a single function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;email_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works for a while. Then product asks for push, then SMS, then an in-app inbox, then "can users pick what they get?" Before long, this one function has become a distributed system.&lt;/p&gt;

&lt;p&gt;This post breaks down what a production notification service contains, split into the &lt;strong&gt;functional&lt;/strong&gt; parts users and PMs see, and the &lt;strong&gt;non-functional&lt;/strong&gt; parts that keep it alive at 3 a.m. At the end, there's a framework for deciding whether to build or buy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The big picture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your app --&amp;gt; Notification API --&amp;gt; Preference check --&amp;gt; Template render
                                        |
                              Priority queues (critical / high / low)
                                        |
                              Channel workers (email, SMS, push, inbox...)
                                        |
                              Vendor adapters --&amp;gt; Webhooks --&amp;gt; Analytics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each box hides real engineering work. Let's go through them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: Functional components
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Delivery channels and vendor integrations
&lt;/h3&gt;

&lt;p&gt;Users live on different channels: email, SMS, push, in-app inbox, WhatsApp, Slack or Teams, even automated calls. To maximize engagement you'll want several of them.&lt;/p&gt;

&lt;p&gt;Every channel means picking a vendor, learning its API, testing quality of service, monitoring it, and planning a fallback. A common way to keep this manageable is an adapter layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChannelAdapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DeliveryResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SendgridAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ChannelAdapter&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TwilioAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ChannelAdapter&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FcmAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ChannelAdapter&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your core logic talks to one interface, and vendor quirks stay at the edges.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Templating engine
&lt;/h3&gt;

&lt;p&gt;Templates quietly become the biggest ownership burden. They need constant changes for copy, personalization, branding, dynamic variables, conditional logic, multi-lingual support, and correct rendering across devices.&lt;/p&gt;

&lt;p&gt;Each channel also has different strengths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Email:&lt;/strong&gt; long, detailed content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SMS:&lt;/strong&gt; short and attention-grabbing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Push:&lt;/strong&gt; rich media and action buttons&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If every copy change needs a deploy, your engineers become a bottleneck. The fix is a visual editor so PMs and marketers can build and test without a pull request.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. User preferences
&lt;/h3&gt;

&lt;p&gt;Marketing emails get a simple unsubscribe link. Product notifications are trickier, because users want some of them (security alerts) and not others (weekly tips).&lt;/p&gt;

&lt;p&gt;A decent preference system lets users control:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type:&lt;/strong&gt; which categories they get&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Channel:&lt;/strong&gt; where they get them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frequency:&lt;/strong&gt; instant, hourly, daily or weekly digest&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timing:&lt;/strong&gt; quiet hours
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;should_send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prefs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_preferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;prefs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enabled_categories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;in_quiet_hours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_critical&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;reschedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quiet_hours_end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without this, users do the only thing they can: block everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Batching and digests
&lt;/h3&gt;

&lt;p&gt;Say your product has comments. If 20 people reply to a thread, you don't want 20 notifications. You want one: "20 new comments."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Batching:&lt;/strong&gt; define a window, collect events inside it, then send one combined message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Digests:&lt;/strong&gt; send a summary at a cadence the user chooses, such as hourly, daily or weekly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depending on the notification type, you may need both.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Multi-tenancy
&lt;/h3&gt;

&lt;p&gt;If your product serves other businesses and sends notifications to their users, you need tenant-level isolation and customization: their branding, their sending domain, their channels, their preference rules.&lt;/p&gt;

&lt;p&gt;Example: a SaaS tool sending invoices on behalf of customers. The end user doesn't know your brand, so the email must carry your customer's logo, content and sender identity, all running on shared infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Analytics
&lt;/h3&gt;

&lt;p&gt;PMs need delivery, open and click data to improve notifications. But every channel and vendor reports differently, and some metrics don't exist (you can't track opens on SMS).&lt;/p&gt;

&lt;p&gt;That means you need a normalized event model, so cross-channel analysis is possible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"delivered"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"channel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"notification_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"u_456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-14T10:32:00Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Map vendor webhooks into this shape and you can compare channels apples to apples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: Non-functional components
&lt;/h2&gt;

&lt;p&gt;This is the stuff nobody sees until it breaks.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Scalability and load balancing
&lt;/h3&gt;

&lt;p&gt;Notification traffic spikes unexpectedly: a flash sale, a viral feature, an incident. Design for bursts, and balance load across servers and regions.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Fault tolerance and retries
&lt;/h3&gt;

&lt;p&gt;Downtime isn't acceptable for OTPs or compliance messages. You need redundancy, state management, fallback vendors, and rate-limit-aware throttling per channel.&lt;/p&gt;

&lt;p&gt;One subtle point: &lt;strong&gt;not every failure deserves a retry.&lt;/strong&gt; An expired push token is dead, and retrying it just wastes resources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;RETRYABLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timeout&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rate_limited&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vendor_5xx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;PERMANENT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;invalid_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unsubscribed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bounced&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;RETRYABLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;retry_with_backoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;PERMANENT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;mark_channel_inactive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Understanding why something failed matters as much as retrying it.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. High deliverability
&lt;/h3&gt;

&lt;p&gt;Reaching the user involves several things at once: multiple channels, reliable vendors, cleaning inactive contacts, and managing bounces.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Low latency
&lt;/h3&gt;

&lt;p&gt;People expect notifications fast. Cut network round trips, optimize database queries, and shorten delivery paths. It's ongoing work, because performance degrades as you scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Observability
&lt;/h3&gt;

&lt;p&gt;You need detailed logging, error tracking and performance monitoring so you can diagnose failures quickly and answer compliance questions. If you can't answer "what happened to this user's notification?", debugging becomes guesswork.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Message queue prioritization
&lt;/h3&gt;

&lt;p&gt;This one saves a lot of pain. An OTP and a weekly newsletter shouldn't share a queue. If bulk sends clog the pipeline, login codes get delayed and drop-off rises.&lt;/p&gt;

&lt;p&gt;Use separate queues with their own throughput limits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;QUEUES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;critical&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;workers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unlimited&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c1"&gt;# OTP, security
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;workers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1000/s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;      &lt;span class="c1"&gt;# transactional
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;low&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;workers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;200/s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;       &lt;span class="c1"&gt;# promos, digests
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This protects critical messages without over-provisioning for everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  To build or not to build?
&lt;/h2&gt;

&lt;p&gt;Here's a quick framework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build in-house when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your needs are simple.&lt;/strong&gt; Few notification types, low volume, rare changes, a single channel. Direct vendor integration or a lightweight internal service will do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need heavy customization.&lt;/strong&gt; Your use case is unusual enough that third-party platforms can't support it (like a chatbot over SMS).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notifications are your product.&lt;/strong&gt; If you're something like WhatsApp, owning it makes sense.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Consider alternatives when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resources are limited.&lt;/strong&gt; Building and maintaining this takes real engineering capacity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time to market matters.&lt;/strong&gt; A third-party service can get you live far faster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need complex features.&lt;/strong&gt; Workflows, white-labeling, preferences, personalization, batching and cross-channel logic come ready-made on established platforms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want to focus on your core product.&lt;/strong&gt; Getting notifications right takes experimentation, channel expertise, scaling work and observability, which pulls attention from what makes your product special.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the layer we built &lt;a href="https://www.suprsend.com" rel="noopener noreferrer"&gt;SuprSend&lt;/a&gt; to handle, so teams can skip the plumbing. Full disclosure: we work on SuprSend, so weigh that accordingly. The checklist above holds whichever way you go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;A good notification service balances two sides. The functional side gives product teams control over templates, preferences and analytics. The non-functional side gives everyone reliability, speed and visibility. Skipping either one hurts.&lt;/p&gt;

&lt;p&gt;Whichever path you choose, the goal is the same: a smooth, meaningful experience that keeps users connected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Over to you:&lt;/strong&gt; did you build your notification system in-house or use a platform? What's the one component you wish you'd planned for earlier? Tell us in the comments.&lt;/p&gt;

</description>
      <category>api</category>
      <category>architecture</category>
      <category>backend</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Notifications Are Not Just an API Call: 11 Reasons They Quietly Eat Your Sprint</title>
      <dc:creator>Nikita Navral</dc:creator>
      <pubDate>Tue, 13 Oct 2026 13:24:26 +0000</pubDate>
      <link>https://dev.to/suprsend/notifications-are-not-just-an-api-call-11-reasons-they-quietly-eat-your-sprint-4i5m</link>
      <guid>https://dev.to/suprsend/notifications-are-not-just-an-api-call-11-reasons-they-quietly-eat-your-sprint-4i5m</guid>
      <description>&lt;p&gt;Every product team starts the same way: "We just need to send an email when someone signs up." One SDK, one template, done by Friday.&lt;/p&gt;

&lt;p&gt;Six months later you have four channels, three vendors, a pile of cron jobs, and a PM asking whether the payment reminder copy can be changed without a deploy.&lt;/p&gt;

&lt;p&gt;Notifications look trivial and behave like infrastructure. They're how your product talks to users: alerts, reminders, recommendations, launches, even user-to-user messages. Send too little and the experience breaks. Send too much and people unsubscribe. Here are the 11 places where it gets hard.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Trivial tasks that keep landing on engineers
&lt;/h2&gt;

&lt;p&gt;Notification logic is rarely hard. What's hard is iteration. Getting the content, trigger and timing right takes many tries, and PMs want to move fast. But when templates and triggers live in code, every tweak joins the sprint queue. Adding a new channel to an existing notification multiplies the work again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; let non-engineers edit content and logic without a deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Nobody knows what's actually live
&lt;/h2&gt;

&lt;p&gt;Before building a new notification, you need to know what already exists. Answering that means digging through the code or trusting a shared doc that went stale weeks ago. With several PMs involved, legacy notifications keep running because nobody wants to audit them. Cleanup becomes a once-or-twice-a-year project.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Testing is a coordination problem
&lt;/h2&gt;

&lt;p&gt;Testing notifications isn't technically complex, but it eats time. Test sends often go to one shared device or account that not everyone can access. Scheduled notifications usually need a developer to trigger them manually whenever QA or a PM wants to check them. That's a lot of context switching for something that should be self-serve.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Scaling exposes the cracks
&lt;/h2&gt;

&lt;p&gt;As volume grows, delivery reliability becomes your problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No priority separation.&lt;/strong&gt; If a promotional blast clogs the queue, your OTP sits behind it. Scaling everything up to compensate gets expensive. What you want is immediate delivery for transactional messages and staggered delivery for promotional ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Pseudocode: route by priority so promos never block transactional
&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;transactional&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;otp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;security&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;promotional&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;No fallback or retries.&lt;/strong&gt; If the provider API is down, queued messages are lost, and so is important communication. You need retries and a fallback path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No failure alerts.&lt;/strong&gt; Many teams only learn about failed deliveries when the business team or a customer complains. In the worst case, they never find out. Treat delivery failures like server downtime and alert on them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stale deliveries.&lt;/strong&gt; A delayed message that arrives two weeks later, like a "payment debited" notice, is worse than no message. Put an expiry on time-sensitive notifications:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Drop the message if it's too old to be useful
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;mark_expired&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It sounds like a rare edge case, but it leaves a bad impression when it happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Analytics are scattered
&lt;/h2&gt;

&lt;p&gt;Each vendor gives you its own dashboard, which makes it hard to compare delivery and click rates across channels. User-level analytics are even rarer. How many messages did one user get this week? Did several fire at once? What does the overall experience feel like from their side? Without that view, users end up either under-engaged or bombarded.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Multi-channel is a tightrope
&lt;/h2&gt;

&lt;p&gt;Reaching users on every channel raises conversion, but it also raises unsubscribes. It's also costly to send the same message on three channels when the user saw it on the first.&lt;/p&gt;

&lt;p&gt;A smarter approach staggers delivery across channels based on past behavior and message type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Push -&amp;gt; wait 30 min -&amp;gt; not seen? -&amp;gt; Email -&amp;gt; wait 24h -&amp;gt; not seen? -&amp;gt; SMS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That saves money and avoids spamming people.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Sending to dead channels
&lt;/h2&gt;

&lt;p&gt;People change numbers and leave companies. Messages to inactive contacts cost money, and high email bounce rates damage your domain reputation, which can push future emails into spam. Most tools report a bounce only after you've sent. It's far better to filter out inactive channels in advance and keep your contact data clean.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Multi-lingual support
&lt;/h2&gt;

&lt;p&gt;Expanding into non-English markets means storing each user's language preference and maintaining a template per language, per channel. The engineering effort grows quickly, even though the logic stays the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Users want a say
&lt;/h2&gt;

&lt;p&gt;Letting users choose what they get notified about is good engagement strategy. Without granular preferences, many users take the only option available and unsubscribe from everything, which hurts engagement and sales. Quora is a good example: users can opt out of specific types of emails without leaving entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. One-size-fits-all frequency
&lt;/h2&gt;

&lt;p&gt;Engagement notifications usually run on a fixed schedule, because custom frequency per user isn't feasible by hand. The result is endless reminders for users who aren't interested, while engaged users could be getting more.&lt;/p&gt;

&lt;p&gt;A smarter system adapts to behavior: send more when users respond, back off when they go quiet, and try a different way to re-engage. That requires per-user data processing feeding back into the delivery pipeline, which most startups can't justify building. So they settle for light personalization and a frequency cap. It works, but it leaves engagement on the table.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Vendor lock-in and reliability
&lt;/h2&gt;

&lt;p&gt;No vendor guarantees 100% uptime. For time-sensitive messages like OTPs, you need a fallback vendor. But every vendor has its own API and integration rules, so each one costs real engineering time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;vendor&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vendor_a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vendor_b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vendor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;VendorError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;DeliveryFailed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks simple, until you maintain two or three vendors' payload formats, webhooks and error codes. Most teams stay with one vendor per channel until an outage forces the issue, or pick a multi-channel vendor that may not be the cheapest option.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;None of these problems is impossible alone. Together, they turn "just send a notification" into a standing drain on your team's time.&lt;/p&gt;

&lt;p&gt;That's the problem we're building &lt;a href="https://www.suprsend.com" rel="noopener noreferrer"&gt;SuprSend&lt;/a&gt; to solve, so engineering teams can stop reinventing notification infrastructure and focus on their core product.&lt;/p&gt;

&lt;p&gt;Now I'm curious: &lt;strong&gt;how does your team handle notifications today?&lt;/strong&gt; Home-grown service, a single vendor, or something else? What's been the biggest pain point? Let us know in the comments.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
