<?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: Bolivar Stephen</title>
    <description>The latest articles on DEV Community by Bolivar Stephen (@sboli).</description>
    <link>https://dev.to/sboli</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%2F509671%2F7e95f0d9-b7fd-4e79-8978-d609aae74a53.jpeg</url>
      <title>DEV Community: Bolivar Stephen</title>
      <link>https://dev.to/sboli</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sboli"/>
    <language>en</language>
    <item>
      <title>Every metric is green-ok but your users can't log-in</title>
      <dc:creator>Bolivar Stephen</dc:creator>
      <pubDate>Fri, 06 Feb 2026 22:18:46 +0000</pubDate>
      <link>https://dev.to/sboli/every-metric-is-green-ok-but-your-users-cant-log-in-4dmc</link>
      <guid>https://dev.to/sboli/every-metric-is-green-ok-but-your-users-cant-log-in-4dmc</guid>
      <description>&lt;p&gt;Your monitoring says one thing. Reality says another. And you're stuck in the middle trying to figure out what's actually broken.&lt;/p&gt;

&lt;p&gt;This is the most dangerous blind spot in modern infrastructure: the gap between what your metrics show and what your users experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Green Dashboards Lie
&lt;/h2&gt;

&lt;p&gt;Here's what we tell ourselves: if CPU is low, memory is available, and HTTP is 200 OK, the system must be working.&lt;/p&gt;

&lt;p&gt;This assumption is wrong.&lt;/p&gt;

&lt;p&gt;Infrastructure metrics measure &lt;strong&gt;potential&lt;/strong&gt;, not &lt;strong&gt;reality&lt;/strong&gt;. They tell you your system &lt;em&gt;could&lt;/em&gt; work. They don't tell you it &lt;em&gt;is&lt;/em&gt; working.&lt;/p&gt;

&lt;p&gt;It's like saying your car is absolutely fine because the tank is full of gas. However you have 2 flat tires and no steering wheel.&lt;/p&gt;

&lt;p&gt;Infrastructure metrics are necessary. But they're not the big picture.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Build a Complete Monitoring Strategy
&lt;/h2&gt;

&lt;p&gt;Combine infrastructure metrics with workflow validation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure layer&lt;/strong&gt; (traditional monitoring):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU, memory, disk, network utilization&lt;/li&gt;
&lt;li&gt;Process health checks&lt;/li&gt;
&lt;li&gt;Resource saturation metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Network layer&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TCP port connectivity&lt;/li&gt;
&lt;li&gt;DNS resolution&lt;/li&gt;
&lt;li&gt;TLS handshake success&lt;/li&gt;
&lt;li&gt;Certificate expiry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Application layer&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP response codes&lt;/li&gt;
&lt;li&gt;API endpoint availability&lt;/li&gt;
&lt;li&gt;Response time percentiles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Business logic layer&lt;/strong&gt; (workflow monitoring):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User registration completes end-to-end&lt;/li&gt;
&lt;li&gt;Login → session → data fetch works&lt;/li&gt;
&lt;li&gt;Checkout → payment → confirmation succeeds&lt;/li&gt;
&lt;li&gt;Password reset emails actually send&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each layer catches different failure modes. Infrastructure metrics catch capacity issues. Network checks catch connectivity problems. Application metrics catch crashes. Workflow checks catch the subtle breaks where everything &lt;em&gt;looks&lt;/em&gt; healthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start With Your Critical Path
&lt;/h2&gt;

&lt;p&gt;You don't need to monitor every possible user journey. Start with the one workflow that would cause panic if it broke. For example &lt;em&gt;Registration&lt;/em&gt; and &lt;em&gt;Main value proposition&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Then build a basic check that verify this workflow.&lt;br&gt;
Can a user &lt;strong&gt;actually&lt;/strong&gt; create an account ?&lt;br&gt;
Can a user &lt;strong&gt;actually&lt;/strong&gt; click on that button and it does do what it's supposed to ?&lt;/p&gt;

&lt;p&gt;Shift from "are our servers healthy?" to "can users accomplish what they came here to do?"&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Your infrastructure metrics will tell you when capacity runs low. When processes crash. When disk fills up.&lt;/p&gt;

&lt;p&gt;They won't tell you when authentication tokens expire. When APIs return errors wrapped in 200 responses. When background jobs stop processing.&lt;/p&gt;

&lt;p&gt;If you want to know whether your system actually works, test it the way users experience it. Try to do what they do. Verify it works end-to-end.&lt;/p&gt;

&lt;p&gt;There's a difference between monitoring infrastructure and monitoring user experience, and that's why I built &lt;a href="https://monitrics.com" rel="noopener noreferrer"&gt;Monitrics&lt;/a&gt;&lt;/p&gt;

</description>
      <category>monitoring</category>
      <category>analytics</category>
      <category>devops</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Send automatic release notes email from Gitlab</title>
      <dc:creator>Bolivar Stephen</dc:creator>
      <pubDate>Wed, 09 Jun 2021 18:09:37 +0000</pubDate>
      <link>https://dev.to/sboli/send-automatic-release-notes-email-from-gitlab-3ge0</link>
      <guid>https://dev.to/sboli/send-automatic-release-notes-email-from-gitlab-3ge0</guid>
      <description>&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;As a tech lead I'm required to do some communication with the company. Until today, I was sending a recap email about new features and bugfixes on every release. But, I tend to "forget" because it's tedious and boring.&lt;/p&gt;

&lt;p&gt;Let automate this !&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you will need:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sendgrid. Free tier is sufficient&lt;/li&gt;
&lt;li&gt;Gitlab&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  n8n to the rescue
&lt;/h2&gt;

&lt;p&gt;Extendable workflow automation. That's the tagline. Simple, explicit. I like it. I need a no-code platform that can do things for me, but which also offers me the possibility to run code. As an experienced JS/TS developer n8n seems perfect. Let's dive in.&lt;/p&gt;

&lt;h4&gt;
  
  
  Installation
&lt;/h4&gt;

&lt;p&gt;They provide a &lt;a href="https://docs.n8n.io/getting-started/quickstart.html#give-it-a-spin-using-npx"&gt;Docker image&lt;/a&gt;, which is always handy. I'm going to set this up in my cluster, to my liking. Here is a compose file to get you started with docker and traefik 1.7.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.7'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;n8n&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;n8nio/n8n:0.123.1&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;GENERIC_TIMEZONE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;America/Port_of_Spain"&lt;/span&gt;
      &lt;span class="na"&gt;TZ&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;America/Port_of_Spain"&lt;/span&gt;
      &lt;span class="na"&gt;N8N_BASIC_AUTH_ACTIVE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;N8N_BASIC_AUTH_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;CHANGE ME&amp;gt;&lt;/span&gt;
      &lt;span class="na"&gt;N8N_BASIC_AUTH_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;CHANGE ME&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;n8n-data:/home/node/.n8n&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;traefik-public&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512M&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.enable=true'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.backend=n8n'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.frontend.rule=Host:host.domain.tld'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.docker.network=traefik-public'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.port=5678'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.tags=traefik-public'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.redirectorservice.frontend.entryPoints=http'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.redirectorservice.frontend.redirect.entryPoint=https'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.webservice.frontend.entryPoints=https'&lt;/span&gt;
&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;traefik-public&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;n8n-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use a &lt;a href="https://n8n.io/cloud"&gt;cloud hosted&lt;/a&gt; version of n8n if setting this up on your own is not something you're interested in.&lt;/p&gt;

&lt;p&gt;Now that everything is installed. It's time to dive in what we actually want to do. Which is send and email to the team whenever a new release is published on Gitlab.&lt;/p&gt;

&lt;p&gt;I prepared the workflow. Import &lt;a href="https://gist.github.com/sboli/b537c558eb31df4f245c3a0dfba703f2"&gt;this json workflow&lt;/a&gt; in n8n. And let's make it work on your setup.&lt;/p&gt;

&lt;p&gt;After import, you should find a workflow like this.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aNL1i8ir--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gi5hz9ummicrxa3bh289.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aNL1i8ir--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gi5hz9ummicrxa3bh289.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
 (Ignore the start button. We're not going to use it). &lt;/p&gt;

&lt;h4&gt;
  
  
  1. Gitlab webhook
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Edit the Webhook node and change the &lt;code&gt;path&lt;/code&gt; parameter to a random value, we'll reference this value as $path later.&lt;/li&gt;
&lt;li&gt;Go to your project Webhook settings on Gitlab and create a new webhook at the url &lt;a href="https://yourdomain.tld/webhook/%24path"&gt;https://yourdomain.tld/webhook/$path&lt;/a&gt;
Perfect. Next.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. Gitlab Markdown to html
&lt;/h4&gt;

&lt;p&gt;The webhook release note is in Markdown. Not very mail friendly if you ask me. Let's convert it to HTML.&lt;br&gt;
Conveniently, Gitlab &lt;a href="https://docs.gitlab.com/ee/api/markdown.html"&gt;offers an api&lt;/a&gt; for that.&lt;br&gt;
That's the &lt;code&gt;convertMdToHtml&lt;/code&gt; node. Let's setup our gitlab api key in n8n.&lt;/p&gt;

&lt;p&gt;Create a new &lt;em&gt;Header auth&lt;/em&gt; credential that you must name &lt;strong&gt;gitlab-api-key&lt;/strong&gt;.&lt;br&gt;
The header name is "Authorization" and the value is &lt;code&gt;Bearer &amp;lt;your_api_key&amp;gt;&lt;/code&gt;. If you don't know how to create a gitlab api key, head over &lt;a href="https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html"&gt;to the wonderful docs&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Finally, send the mail.
&lt;/h4&gt;

&lt;p&gt;Create a new &lt;a href="https://sendgrid.com/docs/ui/account-and-settings/api-keys/"&gt;api key&lt;/a&gt; in Sengrid. Copy it to your clipboard.&lt;/p&gt;

&lt;p&gt;Now create a new Sendgrid credential in n8n. You must name it &lt;strong&gt;sendgrid-api-key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Start your workflow. You're done !&lt;/p&gt;

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