<?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: Knock</title>
    <description>The latest articles on DEV Community by Knock (@knock_labs).</description>
    <link>https://dev.to/knock_labs</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1175362%2F90a3bcee-6cf9-4a92-9af7-f62f28eae43d.png</url>
      <title>DEV Community: Knock</title>
      <link>https://dev.to/knock_labs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/knock_labs"/>
    <language>en</language>
    <item>
      <title>Why you need observability in notification systems</title>
      <dc:creator>Knock</dc:creator>
      <pubDate>Mon, 02 Oct 2023 20:49:12 +0000</pubDate>
      <link>https://dev.to/knocklabs/why-you-need-observability-in-notification-systems-2pl8</link>
      <guid>https://dev.to/knocklabs/why-you-need-observability-in-notification-systems-2pl8</guid>
      <description>&lt;p&gt;Your notifications are a critical bridge between your product and your customers. A broken notification system hurts your customer experience and leads to unsubscribes. When a critical notification fails to send, the result is a missed growth opportunity or customer churn.&lt;/p&gt;

&lt;p&gt;Observability matters in notifications. Companies don’t think twice about adding observability to their core product. They know outages have a direct impact on their business. But when it comes to notifications, teams often let their service run in silence with nothing past basic logging.&lt;/p&gt;

&lt;p&gt;This is a mistake. Active observability in notifications ensures your customers don’t get spammed by your product and don’t miss the important events that help them find value and help your product grow. &lt;/p&gt;

&lt;p&gt;In this post we’ll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An overview of observability: logs, traces, and metrics&lt;/li&gt;
&lt;li&gt;How observability informs product direction&lt;/li&gt;
&lt;li&gt;Observability in notification systems&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Observability helps engineering and customer success understand “the now”&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To understand observability, you have to know a little about where the idea comes from—control theory. Control theory is about modeling dynamic systems to achieve a desired output. The system is observable if you can measure the state of that system, and use those measurements to stabilize the system.&lt;/p&gt;

&lt;p&gt;This is what you are doing with observability in the software engineering sense—you are measuring the state of your system (your notification service), so that you can correct any destabilization (errors, bugs, or outages).&lt;/p&gt;

&lt;p&gt;Observability establishes a feedback loop, enabling your engineering team to address and rectify issues. It allows your developers to quickly identify what is going wrong and why. Observability tools assist developers in identifying anomalies, inconsistencies, and errors in the system. They are outputting the three pillars of observability, each providing a different angle of insight into system behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logs&lt;/strong&gt;: Logs are discrete events or records generated by a service. They provide context, detail, and the sequence of activities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traces&lt;/strong&gt;: Tracing provides insights into the flow of a request across various services. It gives a view of the end-to-end journey of a request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metrics&lt;/strong&gt;: Metrics are aggregate values that provide a high-level view of system health.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s say your notification service has stopped sending messages. We can use each of these to investigate the problem from different angles to diagnose the root cause.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logs for detail
&lt;/h3&gt;

&lt;p&gt;The logs provide detailed event-based information. An engineer can check the error logs for any error messages or exceptions thrown by the notification service. Errors might indicate problems like failed database connections, third-party API failures (e.g., if using a service like Twilio or Firebase), or internal logic issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Traces for paths
&lt;/h3&gt;

&lt;p&gt;Traces are useful if the notification service interacts with multiple components or services. You may have a wrapper notification service that calls services for email, SMS, or in-app messaging, as well as message templating. Traces can show the path a notification request takes to help identify if a specific service or component in the flow is causing the delay or failure. Traces are also good for finding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Latency Issues&lt;/strong&gt;: If the traces indicate that notifications are being processed but with significant delays, it can point to performance issues. For instance, an external API taking too long to respond can be a bottleneck.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependencies&lt;/strong&gt;: Traces can highlight dependencies, such as databases or third-party services that might be problematic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Metrics for trends
&lt;/h3&gt;

&lt;p&gt;Metrics provide a high-level view and are good for identifying trends and performance anomalies. If you are instrumenting different components of your notification service, you might see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A spike in error rates can correlate with the time the issue started.&lt;/li&gt;
&lt;li&gt;An increase in queue length indicating that notifications are being added faster than they're being processed.&lt;/li&gt;
&lt;li&gt;Increases in CPU, memory, or network utilization if there are logic errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if users aren’t receiving notifications:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Logs might show that notifications are being created, but no send confirmation exists.&lt;/li&gt;
&lt;li&gt;Traces show that the notification service is making a request to your SMS service API, but there's no response.&lt;/li&gt;
&lt;li&gt;Metrics show that the response time for the SMS API has spiked, and there's a high error rate.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then your team will know that the SMS service is either down or experiencing issues, and that is causing the notification service to fail.&lt;/p&gt;

&lt;p&gt;Engineering can then do two things. Firstly, escalate the issue with the SMS service to fix the fault. But, as importantly, escalate the issue with their customer service team. With observability, the CS team knows what the problem is and can give this information to affected customers. Observability allows you to add two further enhancements to this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can give your customer service team access to system insights from observability tools, so they can proactively address potential customer challenges.&lt;/li&gt;
&lt;li&gt;You can add observability data to &lt;a href="https://status.knock.app/"&gt;status pages&lt;/a&gt; so customers can find this information themselves and be continually informed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bringing them together for system performance
&lt;/h3&gt;

&lt;p&gt;Performance optimization is how the feedback loop from observability of notification services extends out beyond just immediate performance to the long-term improvement of the service.&lt;/p&gt;

&lt;p&gt;Here, metrics give teams an insight into any existing or potential bottlenecks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Latency metrics&lt;/strong&gt;—specifically, the time it takes to process and send notifications—teams can spot inefficiencies. Whether it's a slow response of an external API or delays in internal logic, metrics help developers optimally tune the system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource utilization metrics&lt;/strong&gt;, be it those related to CPU, memory, or network consumption, can decide scalability. These metrics can show how the service operates under current peak loads and thus what is going to need to be optimized or upgraded for future growth.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, these metrics are the leading indicator of costs. If you have notifications needing multiple retries before successful delivery, this doesn't just hamper user experience but also escalates operational costs. Metrics can illuminate such inefficiencies, triggering deeper dives into root causes and potential remedies.&lt;/p&gt;

&lt;p&gt;The same goes for external services. It's essential to have a metrics-driven perspective on the cost and performance of these providers so the team can make well-informed decisions, be it the continuation of existing services or the shifting to alternative solutions.&lt;/p&gt;

&lt;p&gt;There's also a subtler side to observability. To understand when things are amiss, you first need to understand what 'normal' looks like. Engineering teams need to have a clear picture of what smooth operations look like for their notification service. This means understanding average request times, typical server loads, normal error rates, and other metrics. This baseline observability ensures that anomalies don't go unnoticed.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Observability helps your product team understand the future&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Observability isn't just about monitoring and troubleshooting. It also offers insights that can drive service and product enhancement. When it comes to a notification service, metrics play a pivotal role in understanding user behavior and potential areas for improvement.&lt;/p&gt;

&lt;p&gt;Observability in notifications is crucial in understanding how users interact with a product. Metrics, in this context, act as the eyes and ears of a product team, giving them valuable insights into user behaviors, preferences, and engagement levels.&lt;/p&gt;

&lt;p&gt;When introducing a new notification feature, it's essential to track its performance closely. Metrics can show if the latest feature has been welcomed by the users or if there's a disconnect. For instance, a recent roll-out of a notification feature that garners little engagement may indicate it doesn't resonate with users or perhaps lacks clarity, prompting the product team to revisit its design or utility.&lt;/p&gt;

&lt;p&gt;Moreover, metrics can dive deeper into users' &lt;a href="https://docs.knock.app/concepts/preferences"&gt;notification preferences&lt;/a&gt;. Are they more inclined towards SMS, email, or push notifications? How frequently do they want to be notified? Such insights can empower teams to tailor their notification strategies, aligning them closer to what users genuinely want.&lt;/p&gt;

&lt;p&gt;But where do these metrics come from? And how do we ensure we're capturing the right data?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Email Notifications&lt;/strong&gt;: Pixels embedded within emails can report if the email was opened. Moreover, tracked links can reveal which parts of the email content users found engaging enough to click on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-app Notifications&lt;/strong&gt;: Event tracking tools can capture how users interact with in-app notifications—whether they click on them, dismiss them, or adjust settings related to them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SMS and Push Notifications&lt;/strong&gt;: Delivery success rates can be essential metrics here. A high rate of undelivered messages might hint at issues with the service provider or the content itself.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Another crucial aspect is the content and relevance of notifications. Metrics like &lt;a href="https://docs.knock.app/send-notifications/tracking"&gt;open rates or click-through rates for notifications&lt;/a&gt; can shed light on this. If users frequently open a notification but seldom click through, it may suggest the content is catchy but not compelling enough to drive action. Conversely, high click-through rates could indicate content that's both engaging and relevant.&lt;/p&gt;

&lt;p&gt;Finally, while metrics offer a quantitative view, qualitative feedback is just as critical. Pairing metrics with user surveys or feedback sessions can provide richer insights, combining the 'what' with the 'why'. In essence, observability in notifications goes beyond just numbers; it's about understanding user behavior and iterating to enhance the overall user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Adding observability to your notifications&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you don’t have observability working with your current notification service, you need to add it. It’s the difference between a successful service and not, and the difference between keeping your customers and not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://knock.app/"&gt;Knock&lt;/a&gt; has observability built in through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://knock.app/features/observability"&gt;End-to-end debugging capabilities&lt;/a&gt; from API request issued to workflow run logs.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.knock.app/developer-tools/outbound-webhooks"&gt;Webhooks&lt;/a&gt; for notification status changes.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://docs.knock.app/integrations/extensions/datadog"&gt;Datadog&lt;/a&gt; extension to stream key metrics into your Datadog dashboard in realtime.&lt;/li&gt;
&lt;li&gt;Streaming normalized, cross-channel notification engagement data to your &lt;a href="https://docs.knock.app/integrations/extensions/data-sync"&gt;data warehouse&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best place to start is to &lt;a href="https://dashboard.knock.app/signup?utm_source=blog&amp;amp;utm_medium=referral&amp;amp;utm_campaign=guide-to-notif-systems"&gt;sign up for an account&lt;/a&gt; and &lt;a href="https://docs.knock.app/?utm_source=blog&amp;amp;utm_medium=referral&amp;amp;utm_campaign=guide-to-notif-systems"&gt;read our docs&lt;/a&gt;. If you’d like to learn more, you can &lt;a href="https://knock.app/contact-sales?utm_source=blog&amp;amp;utm_medium=referral&amp;amp;utm_campaign=guide-to-notif-systems"&gt;book a personalized demo&lt;/a&gt; today.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>tooling</category>
      <category>webdev</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Going From Single-Channel to Multi-Channel Notifications</title>
      <dc:creator>Knock</dc:creator>
      <pubDate>Mon, 02 Oct 2023 20:39:51 +0000</pubDate>
      <link>https://dev.to/knocklabs/going-from-single-channel-to-multi-channel-notifications-1a43</link>
      <guid>https://dev.to/knocklabs/going-from-single-channel-to-multi-channel-notifications-1a43</guid>
      <description>&lt;p&gt;When you first start building an application, email is a great way to notify users about a new message or task. It’s universal and it’s easy to implement. Email makes sense when you only need one notification channel. But that doesn’t last for long.&lt;/p&gt;

&lt;p&gt;A user asks “Hey, do you have a Slack bot? I’d love to get updates on there.” Or a PM wants to add a nice bell icon in the top-right to increase engagement. Or you build a mobile app and want to add push notifications.&lt;/p&gt;

&lt;p&gt;What started as a simple single-channel integration turns into a complicated, multi-channel notification system.&lt;/p&gt;

&lt;h2&gt;
  
  
  The slippery slope of multi-channel notifications
&lt;/h2&gt;

&lt;p&gt;Here’s how you might set up a single-channel email notification for a new app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sgMail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@sendgrid/mail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sendEmailNotification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recipient@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sender@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;New Task Added&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`A new task "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" has been added to your task list.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;strong&amp;gt;A new task "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" has been added to your task list.&amp;lt;/strong&amp;gt;`&lt;/span&gt;&lt;span class="p"&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;await&lt;/span&gt; &lt;span class="nx"&gt;sgMail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Email notification sent successfully!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error sending email: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Logic to add task&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taskAddedSuccessfully&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskAddedSuccessfully&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Task "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" added successfully!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sendEmailNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error adding task.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Add a task&lt;/span&gt;
&lt;span class="nx"&gt;addTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Finish email integration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a common inline approach, especially for a new app. You are concentrating on the logic of the app—the email notification is an afterthought. All you do is just add a new function in your code to handle it.&lt;/p&gt;

&lt;p&gt;This will work &lt;em&gt;while&lt;/em&gt; you’ve only got email notifications. But that isn’t going to happen.&lt;/p&gt;

&lt;p&gt;Let’s say you want to add SMS. What are you going to do? Well, you can add a &lt;code&gt;sendSMSNotification&lt;/code&gt; function like your email one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;twilio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;twilio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sgMail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@sendgrid/mail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accountSid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TWILIO_ACCOUNT_SID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TWILIO_AUTH_TOKEN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;twilio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountSid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sendEmailNotification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sendSMSNotification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`A new task "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" has been added to your task list.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+1234567890&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+0987654321&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`SMS sent with ID: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error sending SMS: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Logic to add task&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taskAddedSuccessfully&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskAddedSuccessfully&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Task "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" added successfully!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sendEmailNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sendSMSNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error adding task.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Add a task&lt;/span&gt;
&lt;span class="nx"&gt;addTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Finish both notification integrations&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s not great. We can already start to see some problems creeping in. Let’s start with the basic code issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tight Coupling&lt;/strong&gt;: The code tightly couples the task addition logic with the email and SMS notification logic. This can make it difficult to add other types of notifications without significant modifications to the existing functions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single Responsibility Principle Violation&lt;/strong&gt;: The &lt;code&gt;addTask&lt;/code&gt; function has multiple responsibilities: adding a task, sending an email and sending an SMS. As you add more notification types, this function will become bloated and violate the Single Responsibility Principle (SRP) of software design.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Propagation&lt;/strong&gt;: If sending a notification fails (e.g., an issue with the SendGrid service), it might affect the primary task of adding the task itself or the SMS sending, leading to intertwined error handling challenges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: Adding more notification types would mean adding more and more code to the &lt;code&gt;addTask&lt;/code&gt; function, making it harder to maintain over time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s just think about the coupling. This will work for whenever someone adds a task. But what if someone updates a task? Or completes a task? Or leaves a comment? Or any of the other thousand things task management apps do?&lt;/p&gt;

&lt;p&gt;We need to decouple this code.  The best option is moving to an event-based paradigm. This involves emitting events when actions like adding a task take place and having listeners respond to these events. &lt;/p&gt;

&lt;p&gt;Here we’ll use Node.js's built-in &lt;code&gt;events&lt;/code&gt; module and refactor our above code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;twilio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;twilio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;events&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Twilio Setup&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accountSid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TWILIO_ACCOUNT_SID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TWILIO_AUTH_TOKEN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;twilio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountSid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Event Setup&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;TaskEventEmitter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taskEmitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;TaskEventEmitter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Notification Service&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notificationService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;sendSMSNotification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`A new task "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" has been added to your task list.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+1234567890&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// Replace with recipient's phone number&lt;/span&gt;
                &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+0987654321&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;   &lt;span class="c1"&gt;// Replace with your Twilio number&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`SMS sent with ID: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error sending SMS: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nx"&gt;sendEmailNotification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recipient@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sender@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;New Task Added&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`A new task "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" has been added to your task list.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;strong&amp;gt;A new task "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" has been added to your task list.&amp;lt;/strong&amp;gt;`&lt;/span&gt;&lt;span class="p"&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;await&lt;/span&gt; &lt;span class="nx"&gt;sgMail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Email notification sent successfully!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error sending email: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Event Listeners&lt;/span&gt;
&lt;span class="nx"&gt;taskEmitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;taskAdded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Task "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" added successfully!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;notificationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendEmailNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;notificationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendSMSNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Add a Task Function&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Here, logic to add the task to a database or system would be placed.&lt;/span&gt;
    &lt;span class="c1"&gt;// We'll assume it always succeeds for simplicity.&lt;/span&gt;
    &lt;span class="nx"&gt;taskEmitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;taskAdded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;taskName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Example usage&lt;/span&gt;
&lt;span class="nx"&gt;addTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Finish event-based integration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a breakdown of what's changed:&lt;/p&gt;

&lt;p&gt;The built-in &lt;code&gt;EventEmitter&lt;/code&gt; allows us to emit and listen to custom events in our application. We've then grouped the &lt;code&gt;sendSMSNotification&lt;/code&gt; and &lt;code&gt;sendEMailNotification&lt;/code&gt; functions under a &lt;code&gt;notificationService&lt;/code&gt; object. This provides better organization and can more easily be expanded with other notification methods in the future.&lt;/p&gt;

&lt;p&gt;The main task-adding function now simply emits a 'taskAdded' event after adding a task. When the 'taskAdded' event is emitted, our listener responds by logging the addition of the task and sending email and SMS notifications.&lt;/p&gt;

&lt;p&gt;This setup allows for a clearer separation of concerns and makes it easier to add other types of notifications or additional actions in response to task-related events. For instance, you could easily add another listener for 'taskAdded' that sends a Slack message or logs the event to an analytics platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unraveling multi-channel complexity
&lt;/h2&gt;

&lt;p&gt;But, we still have problems. Two are pretty obvious.&lt;/p&gt;

&lt;p&gt;Firstly, we have content repetition across the notifications. The structure and content of each notification (e.g., subject, message body) are hardcoded for emails and for SMS. For each new type of notification we’ll have to hardcode these in as well. This can be an easy fix. For instance, for SMS and Push this might just be a string with interpolation. But it sure would be swell if we could pull in consistent content, messaging, and design across all channels, maybe via some kind of &lt;a href="https://docs.knock.app/designing-workflows/template-editor/overview"&gt;templating&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Secondly, we’re still stuck with our erroring out problems. If the email sending fails, so does the SMS sending. More channels mean more points of failure. You have to write a ton of logic to catch all the sad paths, and make sure that not only do you handle errors gracefully, but that errors in a channel don’t lead to misinformation or confusion.&lt;/p&gt;

&lt;p&gt;But even as we are sorting out the logic of this code, we’ll start to uncover all the other problems with going from single-channel to multi-channel notifications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Notification complexity
&lt;/h3&gt;

&lt;p&gt;The above email + SMS notification event duplex starts to solve notification complexity for you. But it doesn’t stop there.&lt;/p&gt;

&lt;p&gt;In the code above, the user will get an email and an SMS, basically immediately. Add Slack. Add push. You’re now bombarding your users with notifications in every messaging app on every device. This is a surefire way to make sure they disable notifications.&lt;/p&gt;

&lt;p&gt;So you need the logic to handle sending to the right channel at the right time. This has to take into account three factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What are the user’s preferences for channel notifications? Do they want email &lt;em&gt;and&lt;/em&gt; SMS &lt;em&gt;and&lt;/em&gt; push?&lt;/li&gt;
&lt;li&gt;Do you have the required data to send to this channel? Has the user added their phone number so you can send an SMS? Have they enabled push notifications and do you have the registered tokens?&lt;/li&gt;
&lt;li&gt;What device is the user currently logged into? If they are in the app, can you send that notification first? Then fallback to push or email?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Within all that, you need to consider timing. You don’t want every notification to send simultaneously, so you need to decide what goes first, in which order, and when. So you might send a push notification first, then if it isn’t  seen within 10 minutes send an email as a fallback.&lt;/p&gt;

&lt;p&gt;Now you have to keep track of notification open state and then run a scheduled task via a cron job to send the email notification. So now you also have a cron service you need to set up and run &lt;em&gt;only&lt;/em&gt; for your notifications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service complexity
&lt;/h3&gt;

&lt;p&gt;There’s a great video of an &lt;a href="https://www.youtube.com/watch?v=-VuXIgp9S7o&amp;amp;ab_channel=JohnBritton"&gt;early Twilio demo&lt;/a&gt; from about ten years ago where, in a few lines of code, the presenter gets a new phone number and sends out a notification to everyone in the conference audience.&lt;/p&gt;

&lt;p&gt;Yeah, it doesn’t work like that anymore. If you want a number to send SMS notifications there are a number of hoops to jump through to show you aren’t about to spam the world about your pump-and-dump cryptocurrency.&lt;/p&gt;

&lt;p&gt;Each of these services has nuances that you’ll only discover once you need to implement them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SMS—you have to buy a number, create a campaign, and state your purpose for using the number as part of the &lt;a href="https://support.twilio.com/hc/en-us/articles/1260800720410-What-is-A2P-10DLC-#h_01EX7FTY86BBGGQENQGC7K0MGE"&gt;A2P 10DLC system&lt;/a&gt;. You’ll get a response in a few days about whether you are allowed to send SMS messaging.&lt;/li&gt;
&lt;li&gt;Email—easier than SMS, but you still have to consider your reputation score and can get banned if the email provider considers that you are sending too many emails or they are getting marked as spam.&lt;/li&gt;
&lt;li&gt;Slack—you have to set up and manage Slack bots that will actually send your notifications.&lt;/li&gt;
&lt;li&gt;Push—In the case of Apple Push Notification Service and Firebase Cloud Messaging, you’ll have to maintain a stateful HTTP connection to be able to send push notifications on Apple and Android devices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is before we get into the quirks of each of the SDKs for these channels, their credentials, their rate limits, and dealing with their updates and deprecations. &lt;/p&gt;

&lt;h3&gt;
  
  
  Organizational complexity
&lt;/h3&gt;

&lt;p&gt;Finally we have the burden to your team. &lt;/p&gt;

&lt;p&gt;Originally your engineering team were building neat productivity hacks into your task management app. AI was on the roadmap! Now they’re managing servers for cron jobs and wondering why the Slack API is so complicated.&lt;/p&gt;

&lt;p&gt;The PM that wanted that in-app notification bell can’t get a holistic view of user behavior and is getting lost tracking user engagement and response rates that are fragmented across channels.&lt;/p&gt;

&lt;p&gt;The data team now has to add telemetry to half a dozen of these notification channels to get observability into delivery and error rates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building an ideal notification service
&lt;/h2&gt;

&lt;p&gt;Notifications start simple, and then get complex all at once. So, if you are building out a notification service that takes this complexity into account, what does it look like?&lt;/p&gt;

&lt;p&gt;First, the service has to be built around the idea that each event is the trigger for a &lt;a href="https://docs.knock.app/concepts/workflows"&gt;workflow&lt;/a&gt; for notifications. There might be a single notification or multiple, each with different requirements. The service needs to take the event and expand it into the type of notification that needs to be generated.&lt;/p&gt;

&lt;p&gt;Within that, we need to call on a centralized &lt;a href="https://docs.knock.app/designing-workflows/template-editor/overview"&gt;messaging template&lt;/a&gt;. Consistency is key. Whether you're sending a text, an email, or a Slack message, the content's tone, style, and branding should remain constant. A centralized templating system ensures that every notification, regardless of its type or channel, aligns with the brand's voice and adheres to pre-defined formats.&lt;/p&gt;

&lt;p&gt;We also want the service to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deliver notifications reliably. This means incorporating mechanisms like retries for delivery failures and rate limit mitigation to prevent hitting API limits. This ensures that each notification has the best chance of reaching its intended audience without overwhelming delivery channels.&lt;/li&gt;
&lt;li&gt;Give users the liberty to choose their &lt;a href="https://docs.knock.app/concepts/preferences"&gt;preferred notification channels&lt;/a&gt;, be it email, SMS, or any other. The system should apply these preferences universally across notification types, ensuring that users only receive messages how and when they want them.&lt;/li&gt;
&lt;li&gt;Build comprehensive log lines and metrics that allow for real-time monitoring, facilitating prompt troubleshooting. This ensures high uptime, performance, and user satisfaction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, we want to understand user engagement with the service. An ideal system would not just send notifications but also track &lt;a href="https://docs.knock.app/send-notifications/tracking"&gt;engagement data&lt;/a&gt; like clicks and opens. This data can then be integrated into a data warehouse, allowing for analysis alongside product analytics. By examining these metrics, you can continually refine your notification strategy to increase customer value and retention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Single integration, multiple channels
&lt;/h2&gt;

&lt;p&gt;This is why we built &lt;a href="https://knock.app"&gt;Knock&lt;/a&gt;. An ideal notification service isn't just about informing users—it's about enhancing their experience, respecting their preferences, and deriving insights to perpetually improve. &lt;/p&gt;

&lt;p&gt;With Knock, you can integrate once with our API and then build out &lt;a href="https://docs.knock.app/concepts/workflows"&gt;workflows&lt;/a&gt; to other notifications easily within our dashboard. Just add your &lt;a href="https://docs.knock.app/concepts/channels"&gt;channels&lt;/a&gt; and we’ll take care of the errors and logic and preferences and complexity. We build notifications so you can focus on your core product.&lt;/p&gt;

&lt;p&gt;If you're dealing with the complexity of a multi-channel notification system and want a great set of APIs to keep things simple, you should &lt;a href="https://knock.app/"&gt;try Knock&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The best place to start is to &lt;a href="https://dashboard.knock.app/signup?utm_source=blog&amp;amp;utm_medium=referral&amp;amp;utm_campaign=guide-to-notif-systems"&gt;sign up for an account&lt;/a&gt; and &lt;a href="https://docs.knock.app/?utm_source=blog&amp;amp;utm_medium=referral&amp;amp;utm_campaign=guide-to-notif-systems"&gt;read our docs&lt;/a&gt;. If you’d like to learn more, you can &lt;a href="https://knock.app/contact-sales?utm_source=blog&amp;amp;utm_medium=referral&amp;amp;utm_campaign=guide-to-notif-systems"&gt;book a personalized demo&lt;/a&gt; today.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
