<?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: Alfonso</title>
    <description>The latest articles on DEV Community by Alfonso (@mechm).</description>
    <link>https://dev.to/mechm</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%2F72910%2Ffdff78c5-7f6d-42d4-8410-a6fc8e902eb1.png</url>
      <title>DEV Community: Alfonso</title>
      <link>https://dev.to/mechm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mechm"/>
    <language>en</language>
    <item>
      <title>Improve Your Debugging Approach for Better Software Applications (&amp; Sounder Sleep 😴)</title>
      <dc:creator>Alfonso</dc:creator>
      <pubDate>Mon, 06 Feb 2023 15:37:18 +0000</pubDate>
      <link>https://dev.to/mechm/improve-your-debugging-approach-for-better-software-applications-sounder-sleep--420g</link>
      <guid>https://dev.to/mechm/improve-your-debugging-approach-for-better-software-applications-sounder-sleep--420g</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10i9ia9ef59y0cuq52mq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10i9ia9ef59y0cuq52mq.jpg" alt="splash image" width="800" height="632"&gt;&lt;/a&gt;&lt;/p&gt;
A page from the Harvard Mark II electromechanical computer's log, featuring a dead moth that was removed from the device with the note "first actual case of [a] bug being found", on September 9, 1947.



&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the process of developing applications, bugs will inevitably be introduced (“I code, therefore I create bugs” ~ Descartes, probably). Bugs can be introduced for a variety of reasons, such as logical errors, misunderstanding of requirements, lack of tests, tight deadlines, or something as simple as having an off day as a fallible human. However, knowing that bugs are inevitable, we can arm ourselves with tools to quickly identify and address software bugs before they are released in the wild. &lt;/p&gt;

&lt;p&gt;Many people assume that they already know everything that there is to know about finding and fixing bugs, or that debugging can be an afterthought since they believe that feature development should take priority. The reality is that it is all too common to approach debugging with a haphazard, ineffective approach. Haphazard approaches can lead to frustration and wasted time, and can actually prevent you from working on new features for your users. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihcyqz6tie8z2fte8qd6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihcyqz6tie8z2fte8qd6.gif" alt="gif of bugs" width="364" height="708"&gt;&lt;/a&gt;&lt;/p&gt;
What a haphazard debugging approach might result in



&lt;p&gt;In this post, we will break down what debugging is as well as one approach that you can employ while you are developing software applications. Additionally, we will add more tools to your debugging toolbox that you may not be aware of. We will also review some interesting bug case studies, noting how the authors employed some debugging techniques in their approaches. This post assumes some familiarity with programming concepts. &lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Debugging?
&lt;/h2&gt;

&lt;p&gt;If we accept that creating software inevitably leads to the creation of bugs, then we must accept that some of the time taken in software development must be allocated to maintaining and debugging existing code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8gtjhmkuo0jffvc28hea.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8gtjhmkuo0jffvc28hea.gif" alt="catching a bug in animal crossing" width="1024" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;
I caught a bug!



&lt;p&gt;Debugging is the process of locating, identifying, and fixing bugs. Even though a test might reveal the presence of a bug, it will not tell us what the exact error is or how the code needs to be fixed. Oftentimes, developers will approach debugging in a sub-optimal way: randomly opening files in the codebase in the hopes of finding out where the issue is coming from; changing lines of codes in a seemingly random manner and restarting servers in the hopes that their changes have fixed the issue; or worse yet, being paralyzed into thinking that the code should not be touched further in fear of causing other unintended consequences.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Is Debugging Important?
&lt;/h2&gt;

&lt;p&gt;With great software comes great responsibility. It is a disservice to our users to ship products with glaring, obtrusive bugs, as these bugs can lead to unexpected results. Depending on the industry that you are operating in, software bugs can lead to financial losses, reputational damage, loss of trust from your users, or personal injury, or they can live in infamy for causing a security vulnerability. &lt;/p&gt;

&lt;p&gt;The following famous bugs demonstrate the importance of squashing bugs before they are shipped out to users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NASA Mars Polar Lander&lt;/strong&gt; - the lander was destroyed because its flight software mistook vibrations caused by the deployment of the stowed legs for evidence that the vehicle had landed, and shut off the engines 40 meters from the Martian surface. The lander had traveled approximately 79.5 million miles up to that point, only to crash and fail at the end. This resulted in financial damages of $175 million and a failed mission. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Therac-25&lt;/strong&gt; - a computer-controlled radiation therapy machine that, due to several software bugs, incorrectly administered massive overdoses of radiation, resulting in the deaths of several patients. A quintessential case study of the potentially fatal dangers of engineers’ overconfidence.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knight Capital Group&lt;/strong&gt; -  Knight Capital’s systems incorrectly executed trade orders due to a repurposed software flag that triggered defective code. This resulted in a loss of $440 million.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;“MissingNo” Pokemon&lt;/strong&gt; - a glitch Pokémon species present in Pokémon Red and Blue, which can be encountered by performing a particular sequence of seemingly unrelated actions. Capturing this Pokémon may corrupt the game's data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjvgea5o8exvz5rbpuje.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjvgea5o8exvz5rbpuje.png" alt="catching MISSIGNO in Pokemon" width="735" height="488"&gt;&lt;/a&gt;&lt;/p&gt;
This bug can be exploited to duplicate items and modify game behavior



&lt;h2&gt;
  
  
  &lt;strong&gt;A&lt;/strong&gt; Debugging Approach
&lt;/h2&gt;

&lt;p&gt;Below, I will break down my personal debugging approach. I lean on principles that I have learned in other engineering disciplines, years of observation while pairing with other developers, and utilizing resources on how to improve troubleshooting skills. &lt;/p&gt;

&lt;p&gt;It is important to note that this post assumes that we have not already prevented and detected certain software bugs with error handling, testing, linting, static type checking, proper code formatting, and additional assistive tooling.&lt;/p&gt;

&lt;p&gt;I like to follow the scientific method approach when I am debugging:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make an observation&lt;/li&gt;
&lt;li&gt;Gather information&lt;/li&gt;
&lt;li&gt;Make a hypothesis&lt;/li&gt;
&lt;li&gt;Test my hypothesis&lt;/li&gt;
&lt;li&gt;Analyze if my test is or is not working&lt;/li&gt;
&lt;li&gt;Repeat until bug is fixed&lt;/li&gt;
&lt;li&gt;???&lt;/li&gt;
&lt;li&gt;Profit&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For step 1, &lt;strong&gt;making an observation&lt;/strong&gt;, I first check to see if the buggy behavior still exists, or if it is actually a bug at all—bug reports might be incorrect or users might be reporting an issue inaccurately. At times, I will pick up a bug ticket but when I go to verify the buggy behavior, the bug has already been fixed by other work. It’s important to verify that the behavior still exists before spending time on an unnecessary fix. If the buggy behavior is still present, I verify that it also occurs in my local development environment. This helps eliminate any subtle issues that could be present only in production due to differences in environment configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7hekka9q3s3k13977876.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7hekka9q3s3k13977876.png" alt="meme pic" width="556" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I find that the bulk of the work should be done in step 2: &lt;strong&gt;gathering information&lt;/strong&gt;. This is where it is useful to know what tools you have at your disposal, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://levelup.gitconnected.com/theres-more-than-just-console-log-aac71c2345f4" rel="noopener noreferrer"&gt;Console.log&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://code.visualstudio.com/Docs/editor/debugging" rel="noopener noreferrer"&gt;VS Code Debugger&lt;/a&gt; (or the Chrome Debugger, or the debugger in the editor of your choice)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/devtools/open/" rel="noopener noreferrer"&gt;Chrome dev tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://beta.reactjs.org/learn/react-developer-tools" rel="noopener noreferrer"&gt;React dev tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/reduxjs/redux-devtools" rel="noopener noreferrer"&gt;Redux dev tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Service logs, terminal logs&lt;/li&gt;
&lt;li&gt;Your peers&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Rubber_duck_debugging" rel="noopener noreferrer"&gt;Rubber ducks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As developers, we are lucky that there are a variety of tools to help us with data collection. Debuggers, logs, and additional dev tools should all be utilized to track down as much information as possible about the bug. They can really help narrow down the scope of the problem. I would implore anybody reading this post to explore these tools in more depth, as updates are constantly being made to improve the developer experience. There is an abundance of resources available regarding any dev tool that you may be using. These tools will help you throughout the entirety of your career, so learning them well is a very good investment of your time! &lt;/p&gt;

&lt;p&gt;For step 3, &lt;strong&gt;making a hypothesis&lt;/strong&gt;, it is important to eliminate any assumptions that you are making about how the program is operating. Coming up with a hypothesis on the root cause of the bug should involve clear thinking. Random guesses here will not be helpful so be sure to take a step back and use all of the information that you gathered in the previous step to make an informed decision. Really tricky bugs, especially those lacking error messages, might need to be tackled with informed trial and error.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4ikla3iksl46h69byzz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4ikla3iksl46h69byzz.png" alt="another meme" width="769" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
Step 3, making a hypothesis, in brief



&lt;p&gt;After coming up with your hypothesis, you can move onto step 4, &lt;strong&gt;testing your hypothesis&lt;/strong&gt;: you can narrow in on the part of the codebase that you suspect contains the bug. You can then add debugger statements before the suspected areas of code to step through how variables and functions are operating,  comment or modify specific sections of the code, or create unit or integration tests that account for the buggy behavior and re-run your test suite. &lt;/p&gt;

&lt;p&gt;Along with step 2 (collecting information), I believe that step 5, &lt;strong&gt;analyzing whether your test is working&lt;/strong&gt;, is next in order of importance. After you have modified sections of the codebase, you should examine your changes. A careful analysis can help you determine if this bug is isolated to this section of the codebase, or if the buggy behavior might be present in similar areas in other parts of the codebase. If the buggy behavior is still present despite your changes, you need to cycle back up to step 1 and repeat this process. &lt;/p&gt;

&lt;p&gt;After performing these steps on a variety of bugs, you will automatically start to cycle through these steps when you encounter any new issues. This can help with bugs that are reported, but it can also help with catching bugs before they are ever committed into the codebase. &lt;/p&gt;

&lt;p&gt;The great thing about this approach is that it can also be used while pair programming. Having multiple sets of eyes on a problem and cycling through these steps can help you find the issue more quickly. I have found that the “time to ask to pair program” threshold is different for everyone, but it is important to leverage your teammates and their knowledge. You can ask yourself who has last worked on this feature, or who knows a lot about this feature and may be able to provide more insight?&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Tips &amp;amp; Tricks
&lt;/h2&gt;

&lt;p&gt;In addition to the approach that I like to use, I also like to keep the following things in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check easy and fast stuff first, even if it seems unlikely. For example, are my servers running, am I checking the correct file, does the TypeScript server need a quick restart? Did I save my changes? &lt;/li&gt;
&lt;li&gt;Depending on the type of bug, do I have the relevant screens up on my monitors? Are Chrome Dev Tools open, are my terminals open, are any back-end logs open, are the relevant dev tools open (e.g., React dev tools, Redux dev tools)?&lt;/li&gt;
&lt;li&gt;Is this a rendering issue (React/CSS) or a logic issue (JS/TS)? Both? &lt;/li&gt;
&lt;li&gt;Is this a front-end issue or a back end-issue? Both? &lt;/li&gt;
&lt;li&gt;How long has this bug been present? I often like to use git bisect to detect which commit introduced the bug.&lt;/li&gt;
&lt;li&gt;Make sure to read/skim the entire error message. However, logs often contain lots of noise.&lt;/li&gt;
&lt;li&gt;Error messages are &lt;em&gt;clues&lt;/em&gt; but not gospel, and they can be misleading. For example, error messaging might be incorrect, it may not have been updated properly by a previous developer, or the error could be coming from further upstream. Error messages can be red herrings, and it is important to treat them as such. &lt;/li&gt;
&lt;li&gt;Have other people experienced this issue? Check Stack Overflow and GitHub issues using key search terms. &lt;/li&gt;
&lt;li&gt;Are there any special notes in any of the relevant documentation that may have been overlooked? &lt;/li&gt;
&lt;li&gt;Use the “fold”/“unfold” features of your editor to minimize the amount of code noise that your brain must process while you are skimming through files. &lt;/li&gt;
&lt;li&gt;Taking a walk—sometimes stepping away and coming back with a fresh set of eyes leads to new insights. In the same vein, those “eureka!” moments could come when you are away from your machine. &lt;/li&gt;
&lt;li&gt;Make use of &lt;a href="https://en.wikipedia.org/wiki/Rubber_duck_debugging" rel="noopener noreferrer"&gt;“rubber duck” debugging&lt;/a&gt; - explain the problem to an inanimate object (or a willing coworker) step-by-step. As you present the information, you might spot the error or places where your thinking could be reevaluated. &lt;/li&gt;
&lt;li&gt;Have you tried asking &lt;a href="https://chat.openai.com/chat" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt;? Although still in its nascent stages, it can serve as a quick primer for certain questions. (Disclaimer: make sure to verify its results are correct and accurate.)&lt;/li&gt;
&lt;li&gt;Did you try turning it off and on again? In the extremely rare event that you are experiencing non-deterministic behavior, it might not be you but the universe: &lt;a href="https://www.youtube.com/watch?v=AaZ_RSt0KP8" rel="noopener noreferrer"&gt;The Universe is Hostile to Computers&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjop6p251nsb7b1hhn70m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjop6p251nsb7b1hhn70m.png" alt="chatting with ChatGPT" width="800" height="912"&gt;&lt;/a&gt;&lt;/p&gt;
Asking ChatGPT about CORS issues



&lt;h2&gt;
  
  
  Crash Bandicoot: A Case Study
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsocsvhcnwygpqsmwovpl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsocsvhcnwygpqsmwovpl.jpg" alt="Crash Bandicoot Game Cover" width="400" height="400"&gt;&lt;/a&gt;&lt;/p&gt;
The culprit



&lt;p&gt;In this &lt;a href="https://www.gamedeveloper.com/programming/my-hardest-bug-ever" rel="noopener noreferrer"&gt;post&lt;/a&gt; regarding a memory card error in Crash Bandicoot 1, the author describes how they narrowed down the problem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“About the only thing you can do when you run out of ideas debugging is &lt;strong&gt;divide and conquer: keep removing more and more of the errant program's code until you're left with something relatively small that still exhibits the problem.&lt;/strong&gt; You keep carving parts away until the only stuff left is where the bug is.”&lt;/p&gt;

&lt;p&gt;“I returned repeatedly to the test program, &lt;strong&gt;trying to detect some pattern to the errors that occurred&lt;/strong&gt; when the timer was set to 1kHz. Eventually, I noticed that the errors happened when someone was playing with the PS1 controller. Since I would rarely do this myself—why would I play with the controller when testing the load/save code?—I hadn't noticed it. But one day one of the artists was waiting for me to finish testing—I'm sure I was cursing at the time—and he was nervously fiddling with the controller. It failed. "Wait, what? Hey, do that again!"”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;In conclusion, if you take anything away from this post, let it be this: debugging is a skill that &lt;strong&gt;can&lt;/strong&gt; be improved. Through the use of a methodical and structured approach, you can improve how quickly you detect and address bugs. Doing so will lead to a more pleasant development experience and more robust applications for your users.  &lt;/p&gt;

&lt;p&gt;Happy coding! &lt;/p&gt;

</description>
      <category>welcome</category>
    </item>
    <item>
      <title>Why Your Emails May Be Bouncing Back &amp; What You Can Do About It</title>
      <dc:creator>Alfonso</dc:creator>
      <pubDate>Thu, 06 May 2021 13:05:06 +0000</pubDate>
      <link>https://dev.to/mechm/why-your-emails-may-be-bouncing-back-what-you-can-do-about-it-4ddg</link>
      <guid>https://dev.to/mechm/why-your-emails-may-be-bouncing-back-what-you-can-do-about-it-4ddg</guid>
      <description>&lt;p&gt;Recently at Giant Machines, I was investigating a handful of emails from one of our client projects that were not being properly delivered to specific Internet Service Providers (ISPs). After doing some research on their email settings and configurations, I discovered that an ISP's spam scanner configuration may have been mistakenly blocking harmless emails for its customers. As a result, I had to troubleshoot and debug these emails locally to identify why they were not being delivered. For the local debugging that I did, I used an npm package called &lt;a href="https://www.npmjs.com/package/spamscanner" rel="noopener noreferrer"&gt;spamscanner&lt;/a&gt;, which I will be covering in more depth down below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dataprot.net/statistics/spam-statistics/" rel="noopener noreferrer"&gt;55% to 85%&lt;/a&gt; of all email traffic is due to spam emails! Due to this, email clients are constantly attempting to block potentially harmful spam emails from reaching their users. However, at times, perfectly valid (and non-spam) emails can be flagged incorrectly.&lt;/p&gt;

&lt;p&gt;For this post, I will cover how ISPs use spam scanners to try to detect malicious emails. I will also show you how to set up a spam scanner locally that you can use to debug and troubleshoot your own emails. This troubleshooting can be helpful in determining why your emails may not be reaching their intended recipients! This post assumes a basic familiarity with JavaScript, using a terminal, and using a code editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Email Basics
&lt;/h2&gt;

&lt;p&gt;The process for sending and receiving email has changed quite a bit since its inception, but its basic principles have remained in place. For the purposes of this post, all that you need to know is that web mail clients (such as Gmail, Outlook, etc.) communicate back and forth with dedicated email servers. For a closer look at how email works under the hood, refer to the following article: "&lt;a href="https://www.freecodecamp.org/news/how-does-email-work/" rel="noopener noreferrer"&gt;How Does Email Work.&lt;/a&gt;"&lt;/p&gt;

&lt;h2&gt;
  
  
  Email Authentication
&lt;/h2&gt;

&lt;p&gt;Due to the abundance of email spam, several protocols have been implemented over the years to try to mitigate spam messages by performing various programmatic checks.&lt;/p&gt;

&lt;p&gt;The three main protocols are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SPF (Sender Policy Framework)&lt;/strong&gt;: Is the sender who they claim to be?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DKIM (DomainKeys Identified Mail)&lt;/strong&gt;: Encrypts email headers with a private key; servers then use a publicly available key to decrypt the headers and verify the message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DMARC (Domain Message Authentication Reporting and Conformance)&lt;/strong&gt;: Built on top of SPF and DKIM; senders can set policies deciding how to handle SPF/DKIM and what to do for failing checks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For additional information on these email authentication protocols, refer to the following article: "&lt;a href="https://sendgrid.com/blog/email-authentication-explained/" rel="noopener noreferrer"&gt;How Email Authentication Works&lt;/a&gt;."&lt;/p&gt;

&lt;h2&gt;
  
  
  Email Spam Scanners
&lt;/h2&gt;

&lt;p&gt;To detect whether incoming emails are malicious, mail servers also use spam scanners, such as Apache's popular &lt;a href="https://spamassassin.apache.org/" rel="noopener noreferrer"&gt;SpamAssasin&lt;/a&gt;. The internal workings of these spam scanners can be somewhat complicated (involving &lt;a href="https://en.wikipedia.org/wiki/Naive_Bayes_classifier" rel="noopener noreferrer"&gt;Naive Bayes Classifiers&lt;/a&gt; on &lt;a href="https://cwiki.apache.org/confluence/display/SPAMASSASSIN/HowScoresAreAssigned" rel="noopener noreferrer"&gt;trained, large datasets&lt;/a&gt;, for the curious), but the primary takeaway is that these classification systems typically assign a numerical point value to an incoming email to determine the validity of the message. The higher the score, the more likely that the email is spam. For reference, the ISP Optimum &lt;a href="http://help.webhosting.optonline.net/documents/rd/gb/gettingstarted/old/Managing_Domain_Names/Update_DNS_and_MX_Records/faq_email.htm#question20" rel="noopener noreferrer"&gt;states&lt;/a&gt; the following regarding their spam filtering:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All incoming emails are evaluated against these spam rules and are assigned a "spam score". This score determines whether the message will be classified as spam. For standard filtering, the threshold is set at 5, meaning &lt;strong&gt;any message with a score of 5 or higher is classified as spam&lt;/strong&gt;. &lt;strong&gt;Messages scoring between 5 and 10 will be delivered, but will include a spam notification in the subject of the email so that you can immediately identify and delete these messages&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Different ISPs have different policy configurations on their chosen spam scanner, but the same idea applies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fwugu3Dm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fwugu3Dm.png" alt="Litmus Email Screenshot"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Services like &lt;a href="https://www.litmus.com/" rel="noopener noreferrer"&gt;Litmus&lt;/a&gt; provide the ability to see how various spam scanners rank your emails.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As you can see in the screenshot above, the email template that I was investigating received very low scores across the various spam scanners. So what gives? Why were these emails bouncing back, despite having a low score? We will be taking a closer look at this specific issue down below.&lt;/p&gt;
&lt;h3&gt;
  
  
  First Steps
&lt;/h3&gt;

&lt;p&gt;Before using a spam scanner to investigate and troubleshoot your email templates, there are some quick wins for lowering your score that can be achieved by following some of the recommendations listed in &lt;a href="https://github.com/CopernicaMarketingSoftware/Documentation/blob/master/Publisher/en/some-tips-to-lower-your-email-spam-score.md" rel="noopener noreferrer"&gt;this article&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  So… about that local spam scanner setup?
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Initial Setup
&lt;/h3&gt;

&lt;p&gt;For installation instructions on the npm package &lt;a href="https://www.npmjs.com/package/spamscanner" rel="noopener noreferrer"&gt;spamscanner&lt;/a&gt;, refer to their &lt;a href="https://www.npmjs.com/package/spamscanner" rel="noopener noreferrer"&gt;docs&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Simple Server and Local Email Template
&lt;/h3&gt;

&lt;p&gt;Email clients allow you to download your email messages (with the file extension ".eml"). With these locally saved messages, we can run spamscanner against them to further inspect their contents.&lt;/p&gt;

&lt;p&gt;Assuming that you have installed spamscanner and have Node.js locally setup, you may use the following bare-bones script for running the scanner against a locally saved email message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in a file called index.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&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;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Make sure to install spamscanner in your package.json&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SpamScanner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;spamscanner&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;scanEmail&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// For a list of all options &amp;amp; their defaults, see:&lt;/span&gt;
  &lt;span class="c1"&gt;// https://www.npmjs.com/package/spamscanner#api&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scanner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SpamScanner&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Swap out the "Your_locally_saved_message_here.eml" file with the actual filename in the directory&lt;/span&gt;
  &lt;span class="c1"&gt;// containing this script&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your_locally_saved_message_here.eml&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;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;scanResult&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;scanner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// For a list of properties available for inspection, see:&lt;/span&gt;
    &lt;span class="c1"&gt;// https://www.npmjs.com/package/spamscanner#scannerscansource&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Scan results, scanResult.mail:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scanResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mail&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;err&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="nf"&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 in scanEmail:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&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="nf"&gt;scanEmail&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// To run this script, run `node index.js` in your terminal where this script resides.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that you can also run the scanner on Strings or Buffers as long as they are a complete SMTP message (i.e., they include headers and the full email contents).&lt;/p&gt;

&lt;p&gt;The results of running this script will come back in the following shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ScanResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;is_spam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;classification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;phishing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;executables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;arbitrary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nl"&gt;links&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&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;For a detailed description of these fields, refer to the &lt;a href="https://github.com/spamscanner/spamscanner#scannerscansource" rel="noopener noreferrer"&gt;docs&lt;/a&gt;. Typically, the result in the &lt;code&gt;is_spam&lt;/code&gt; field &lt;em&gt;should&lt;/em&gt; be enough to give you confidence that your email will not be marked as spam. Note that spamscanner does not assign a numerical value but instead opts to return a boolean.&lt;/p&gt;

&lt;p&gt;However, different ISPs use different spam scanners, and it may be necessary to investigate your email messages further. To do so, make sure that the "debug" flag is set to &lt;code&gt;true&lt;/code&gt;, as per the code sample above. You can then inspect the contents of &lt;code&gt;scanResult.mail&lt;/code&gt;, which is an object containing more detailed debugging information regarding the email contents (shown below).&lt;/p&gt;

&lt;p&gt;This ".mail" object returns the following shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ParsedMail&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Attachment&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;bcc&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;AddressObject&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;AddressObject&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;cc&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;AddressObject&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;AddressObject&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;date&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;from&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;AddressObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;headerLines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HeaderLines&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;inReplyTo&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;normal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;low&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;high&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;references&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;replyTo&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;AddressObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;textAsHtml&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;to&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;AddressObject&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;AddressObject&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;It can be used to get more specific information about the email.&lt;/p&gt;

&lt;p&gt;A sample screenshot of the "headers" field that is a part of the ".mail" object is shown below.&lt;/p&gt;

&lt;p&gt;In the emails that I was investigating, the spam scanner classifier was marking the email messages as "not spam" but Optimum was appending the following &lt;code&gt;X-Optimum-spam: yes&lt;/code&gt; header to the messages as they were incoming:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FWJxF61P.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FWJxF61P.png" alt="Terminal Screenshot"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Some of the headers present in the email message file. Note the Optimum spam header.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This was causing these messages to not only be marked as spam but they were &lt;strong&gt;also being blocked/bounced entirely!&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  When all else fails, try manual debugging.
&lt;/h2&gt;

&lt;p&gt;If your messages are still being blocked despite a low spam scanner score (or &lt;code&gt;is_spam&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt; if using spamscanner), you may have to take a more manual approach. To do so, I gradually removed parts of the email and re-sent the trimmed-down emails to the ISP that was blocking us. I was eventually able to trace the problem down to this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"mailto:example@example.com"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Contact customer support&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Specifically the &lt;strong&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/Creating_hyperlinks#e-mail_links" rel="noopener noreferrer"&gt;mailto:&lt;/a&gt;&lt;/strong&gt; present in the template caused Optimum's email configuration to flag the email as spam and reject the message outright, despite mailto tags not causing our messages to be flagged as spam by other ISPs.&lt;/p&gt;

&lt;p&gt;Additionally, other emails were bouncing back due to the following (modified) copy:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If this email change request wasn't authorized by you, please click the link below to cancel. If you have any questions, you can contact support via &lt;a href="mailto:example@example.com"&gt;example@example.com&lt;/a&gt; or by calling +1 555-555-5555.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Specifically, the &lt;code&gt;+1&lt;/code&gt; present in the template caused Optimum's spam scanner configuration to flag the email as spam and reject the message outright despite being valid and despite not being flagged by other ISPs or SpamAssasin.&lt;/p&gt;

&lt;p&gt;Due to Optimum's unique SpamAssassin configuration, we were seeing issues for our customers who had an Optimum domain email and attempted to receive emails with "mailto:" or "+1" present. It is not clear why Optimum chooses to block these emails when other ISPs do not, but it could be the case that their configuration is particularly sensitive and errs on the side of caution in attempting to mitigate potential security risks.&lt;/p&gt;

&lt;p&gt;The issues that may be affecting your emails may differ but the techniques used here can help you narrow down why your emails may be bouncing back!&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Email servers accept, forward, and deliver messages.&lt;/li&gt;
&lt;li&gt;If an email has not been properly authenticated, the email server must return a failure "bounce" message.&lt;/li&gt;
&lt;li&gt;Spam scanners typically assign a point ranking to emails to classify them as spam or not spam. Hot dog/not hot dog anyone? 🌭&lt;/li&gt;
&lt;li&gt;You can use the npm package &lt;a href="https://github.com/spamscanner/spamscanner" rel="noopener noreferrer"&gt;spamscanner&lt;/a&gt; locally on your email templates to check whether they are being classified as spam.&lt;/li&gt;
&lt;li&gt;When all else fails, you may have to try a more manual debugging approach to debug ISP-specific edge cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://spamassassin.apache.org/" rel="noopener noreferrer"&gt;https://spamassassin.apache.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sendgrid.com/blog/10-tips-to-keep-email-out-of-the-spam-folder/" rel="noopener noreferrer"&gt;https://sendgrid.com/blog/10-tips-to-keep-email-out-of-the-spam-folder/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sendgrid.com/docs/glossary/spam/" rel="noopener noreferrer"&gt;https://sendgrid.com/docs/glossary/spam/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have any questions? Comment down below and happy coding!&lt;/p&gt;

</description>
      <category>emails</category>
      <category>spamscanner</category>
      <category>javascript</category>
      <category>debugging</category>
    </item>
  </channel>
</rss>
