<?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>Forem: Hexmos</title>
    <description>The latest articles on Forem by Hexmos (@hexmos).</description>
    <link>https://forem.com/hexmos</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%2Forganization%2Fprofile_image%2F8372%2F7c5608e9-6f35-4925-8d50-f3f7dae28e2d.png</url>
      <title>Forem: Hexmos</title>
      <link>https://forem.com/hexmos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/hexmos"/>
    <language>en</language>
    <item>
      <title>LiveAPI Devlogs Part 3: Transforming User Onboarding with 3 Industry-Inspired Methods</title>
      <dc:creator>Rijul Rajesh</dc:creator>
      <pubDate>Sun, 22 Dec 2024 14:14:43 +0000</pubDate>
      <link>https://forem.com/hexmos/liveapi-devlogs-part-3-transforming-user-onboarding-with-3-industry-inspired-methods-g4f</link>
      <guid>https://forem.com/hexmos/liveapi-devlogs-part-3-transforming-user-onboarding-with-3-industry-inspired-methods-g4f</guid>
      <description>&lt;p&gt;Here, we will explore the new updates and improvements in &lt;a href="https://hexmos.com/liveapi" rel="noopener noreferrer"&gt;LiveAPI&lt;/a&gt; from the past few weeks.&lt;/p&gt;

&lt;p&gt;Developers rarely have time to document their code properly. Projects are rushed to meet deadlines, which makes documenting the codebase challenging.&lt;/p&gt;

&lt;p&gt;While Swagger helps, it's not a complete solution; it has considerable setup and maintenance costs. Additionally, an exposed Swagger endpoint can introduce security threats. &lt;a href="https://hexmos.com/liveapi" rel="noopener noreferrer"&gt;LiveAPI&lt;/a&gt; addresses all these issues. &lt;/p&gt;

&lt;p&gt;LiveAPI provides a &lt;strong&gt;Super-Convenient&lt;/strong&gt; solution by providing the following.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using AI to automate documentation.&lt;/li&gt;
&lt;li&gt;Staying up-to-date with revisions and interacting with APIs through widgets.&lt;/li&gt;
&lt;li&gt;Ensuring end-to-end code security.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even though all these features have been developed and used, some new users may find difficulty to use everything without proper guidance.&lt;/p&gt;

&lt;p&gt;So we were faced with a new problem: &lt;strong&gt;How to onboard users easily without friction?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this article, we will demonstrate how we addressed these issues using &lt;strong&gt;three methods&lt;/strong&gt; inspired by other products.&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%2F6pj5qj825yl6kh3865xa.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%2F6pj5qj825yl6kh3865xa.png" alt="alt text" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Difficulty We Faced with Onboarding New Users
&lt;/h3&gt;

&lt;p&gt;When speaking of the difficulty we have been facing, there were 3 issues.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Difficulty in Getting Users to Sign up and Use the Product
&lt;/h4&gt;

&lt;p&gt;Merely using signup can be a barrier for new users to get familiarized with the product, as they will seek simpler ways to use the product.&lt;/p&gt;

&lt;p&gt;So we had to think of a solution that would get people to use the product in the easiest way possible, without barriers.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Difficulty of Keeping the Users Engaged
&lt;/h4&gt;

&lt;p&gt;Even if the user signs up, if there is no proper guidance for beginners, they won't know what to do and may eventually drop off.&lt;/p&gt;

&lt;p&gt;So we need to make sure that there are some beginner-level activities for the user to do, to understand the product more and engage.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Difficulty of Explaining the Features
&lt;/h4&gt;

&lt;p&gt;The user signed up and has some guidance on what things to do, but some users won't properly understand what is written and will find it difficult to use the product.&lt;/p&gt;

&lt;p&gt;In such cases we need a proper tour around the UI, highlighting the areas to be interacted with and how to properly use it.&lt;/p&gt;

&lt;p&gt;To address these problems, let's explore how existing products in the market handle them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Case Study 1: How Picwordify Gets Users to Signup
&lt;/h3&gt;

&lt;p&gt;Continue &lt;a href="https://journal.hexmos.com/liveapi-devlogs03/" rel="noopener noreferrer"&gt;reading the article here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>programming</category>
      <category>learning</category>
    </item>
    <item>
      <title>From Lama2 to LiveAPI: Building Super-Convenient API Documentation (Part II)</title>
      <dc:creator>Athreya aka Maneshwar</dc:creator>
      <pubDate>Sat, 14 Dec 2024 19:40:48 +0000</pubDate>
      <link>https://forem.com/hexmos/from-lama2-to-liveapi-building-super-convenient-api-documentation-part-ii-1p2a</link>
      <guid>https://forem.com/hexmos/from-lama2-to-liveapi-building-super-convenient-api-documentation-part-ii-1p2a</guid>
      <description>&lt;p&gt;&lt;em&gt;Hello, I'm Maneshwar. I'm building git-lrc, an AI code reviewer that runs on every commit. It is free, unlimited, and source-available on Github. &lt;a href="https://github.com/HexmosTech/git-lrc" rel="noopener noreferrer"&gt;Star Us&lt;/a&gt; to help devs discover the project. Do give it a try and share your feedback for improving the product.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hello, I'm Maneshwar. I'm working on &lt;a href="https://hexmos.com/freedevtools/" rel="noopener noreferrer"&gt;FreeDevTools online&lt;/a&gt; currently building **one place for all dev tools, cheat codes, and TLDRs&lt;/em&gt;* — a free, open-source hub where developers can quickly find and use tools without any hassle of searching all over the internet.&lt;/p&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/lovestaco/how-a-hobby-api-collection-and-execution-tool-is-evolving-into-a-product-299n"&gt;previous post&lt;/a&gt;, I shared how a small team of students working part-time built Lama2—a tool that simplified API collection and execution. &lt;/p&gt;

&lt;p&gt;It quickly became an essential part of our workflow, but as our API repositories grew, Lama2's manual process started showing its limits.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Ch
&lt;/h3&gt;

&lt;p&gt;allenges of Scaling Lama2&lt;br&gt;&lt;br&gt;
When we started, our team consisted of five students juggling part-time work and studies.  &lt;/p&gt;

&lt;p&gt;We worked 3-4 hours daily, often pushing the boundaries of our limited capabilities. Lama2 was just one of three projects we were building at the time.  &lt;/p&gt;

&lt;p&gt;Despite our constraints, &lt;strong&gt;Lama2 received a good reception on Hacker News&lt;/strong&gt;. We even gained some early advocates for the product. For a CLI tool and niche language, it was a solid response.  &lt;/p&gt;

&lt;p&gt;However, shipping features still took us longer than we hoped. By the time we were ready to compete, the market for API clients was already crowded.  &lt;/p&gt;

&lt;p&gt;Established teams working full-time on similar products gained traction through their hard work and outreach. While Lama2 solved real problems, it didn’t generate the widespread buzz we had envisioned.  &lt;/p&gt;

&lt;p&gt;We realized that for Lama2 to make a real impact, it needed more than just execution tools.  &lt;/p&gt;
&lt;h3&gt;
  
  
  The Challenges of Scaling &lt;a href="https://hexmos.com/lama2/index.html" rel="noopener noreferrer"&gt;Lama2&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;When we started, our team consisted of five students juggling part-time work and studies.  &lt;/p&gt;

&lt;p&gt;We worked 3-4 hours daily, often pushing the boundaries of our limited capabilities. Lama2 was just one of three projects we were building at the time.  &lt;/p&gt;

&lt;p&gt;Despite our constraints, &lt;strong&gt;Lama2 received a good reception on &lt;a href="https://news.ycombinator.com/item?id=34206333" rel="noopener noreferrer"&gt;Hacker News&lt;/a&gt;&lt;/strong&gt;. We even gained some early advocates for the product. For a CLI tool and niche language, it was a solid response.  &lt;/p&gt;

&lt;p&gt;However, shipping features still took us longer than we hoped. By the time we were ready to compete, the market for API clients was already crowded.  &lt;/p&gt;

&lt;p&gt;Established teams working full-time on similar products gained traction through their hard work and outreach. While Lama2 solved real problems, it didn’t generate the widespread buzz we had envisioned.  &lt;/p&gt;

&lt;p&gt;We realized that for Lama2 to make a real impact, it needed more than just execution tools.  &lt;/p&gt;
&lt;h2&gt;
  
  
  The Problem with Manual API Documentation
&lt;/h2&gt;

&lt;p&gt;Even with Lama2, maintaining large API collections was daunting. Initially, collecting APIs in a single repository for all services felt manageable. But as we scaled to four backends and hundreds of APIs, the process became overwhelming.&lt;/p&gt;

&lt;p&gt;We knew firsthand how frustrating it was to manually document and sync API changes. And we weren’t alone—every developer faces this challenge when dealing with large API collections.&lt;/p&gt;
&lt;h2&gt;
  
  
  A Vision for Automation
&lt;/h2&gt;

&lt;p&gt;We knew we needed to automate the workflow, making API documentation effortless and execution seamless. Our goal was to eliminate manual steps and create a tool that could:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Automatically document APIs as code was merged, without need of setting up any kind of meta tags etc.&lt;/li&gt;
&lt;li&gt;Keep documentation updated with every change&lt;/li&gt;
&lt;li&gt;Allow anyone in the organization to execute APIs with ease&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our goal was simple: &lt;strong&gt;"&lt;a href="https://hexmos.com/liveapi/#pricing" rel="noopener noreferrer"&gt;Super-Convenient API Documentation&lt;/a&gt;."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine a system where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Input&lt;/strong&gt;: A repository link&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output&lt;/strong&gt;: Fully documented APIs that stay updated with every commit.&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%2Fdfvhduzcnfwz8fhq4z42.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%2Fdfvhduzcnfwz8fhq4z42.png" alt="Swagger Alternatives" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Building &lt;a href="https://hexmos.com/liveapi/" rel="noopener noreferrer"&gt;LiveAPI&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;To bring this vision to life, we started developing &lt;strong&gt;LiveAPI&lt;/strong&gt;, a platform designed with the following key features:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;a href="https://hexmos.com/liveapi/#features:~:text=Github%20and%20Gitlab%20integration" rel="noopener noreferrer"&gt;One-Click Repository Connection&lt;/a&gt;:&lt;/strong&gt; Developers could connect their GitHub, GitLab, or Bitbucket repository effortlessly.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;a href="https://hexmos.com/liveapi/#features:~:text=%3C%3C-,Code%20to%20Docs,-We%20use%20your" rel="noopener noreferrer"&gt;Automated Documentation Generation&lt;/a&gt;:&lt;/strong&gt; Documentation would be generated automatically for every commit, with auto-syncing to keep it up-to-date.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://hexmos.com/liveapi/#features:~:text=Execute%20APIs%20and%20Code%20Generation" rel="noopener noreferrer"&gt;Automated Code Snippets&lt;/a&gt;:&lt;/strong&gt; Generate code snippets for any language, enabling frontend developers to move faster. &lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Developer-Friendly Experience:&lt;/strong&gt; Minimal setup, maximum convenience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LiveAPI Runner with Privacy First:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;We never store your repository’s code.&lt;/li&gt;
&lt;li&gt;Using our logic, we extract only routes and API validators.&lt;/li&gt;
&lt;li&gt;This entire process runs on your private server, ensuring your data never leaves your infrastructure.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Spreading the Word
&lt;/h3&gt;

&lt;p&gt;After months of work, LiveAPI is ready. We built a tool that could take the pain out of managing and documenting APIs, enabling teams to focus on building features rather than wrangling documentation. &lt;/p&gt;

&lt;p&gt;A collection of UI/UX-focused tools crafted to simplify workflows, save time, and reduce friction in searching tools/materials. &lt;/p&gt;

&lt;p&gt;Any feedback or contributors are welcome! &lt;/p&gt;

&lt;p&gt;It’s online, open-source, and ready for anyone to use.&lt;/p&gt;

&lt;p&gt;👉 Check it out:&lt;br&gt;&lt;br&gt;
⭐ Star it on GitHub:  &lt;/p&gt;

&lt;p&gt;Let’s make it even better together.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/HexmosTech/git-lrc" rel="noopener noreferrer"&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%2Fyzvpkxm9mga1pweneahx.png" alt="git-lrc" width="800" height="109"&gt;&lt;/a&gt; &lt;br&gt;
 *AI agents write code fast. They also silently remove logic, change behavior, and introduce bugs -- without telling you. You often find out in production. &lt;/p&gt;

&lt;p&gt;git-lrc fixes this. It hooks into git commit and reviews every diff before it lands. 60-second setup. Completely free.* &lt;/p&gt;

&lt;p&gt;Any feedback or contributors are welcome! It's online, source-available, and ready for anyone to use. &lt;/p&gt;

&lt;p&gt;⭐ Star it on GitHub: &lt;br&gt;
 &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/HexmosTech" rel="noopener noreferrer"&gt;
        HexmosTech
      &lt;/a&gt; / &lt;a href="https://github.com/HexmosTech/git-lrc" rel="noopener noreferrer"&gt;
        git-lrc
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Free, Unlimited AI Code Reviews That Run on Commit
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
&lt;p&gt;| &lt;a href="https://github.com/HexmosTech/git-lrc/readme/README.da.md" rel="noopener noreferrer"&gt;🇩🇰 Dansk&lt;/a&gt; | &lt;a href="https://github.com/HexmosTech/git-lrc/readme/README.es.md" rel="noopener noreferrer"&gt;🇪🇸 Español&lt;/a&gt; | &lt;a href="https://github.com/HexmosTech/git-lrc/readme/README.fa.md" rel="noopener noreferrer"&gt;🇮🇷 Farsi&lt;/a&gt; | &lt;a href="https://github.com/HexmosTech/git-lrc/readme/README.fi.md" rel="noopener noreferrer"&gt;🇫🇮 Suomi&lt;/a&gt; | &lt;a href="https://github.com/HexmosTech/git-lrc/readme/README.ja.md" rel="noopener noreferrer"&gt;🇯🇵 日本語&lt;/a&gt; | &lt;a href="https://github.com/HexmosTech/git-lrc/readme/README.nn.md" rel="noopener noreferrer"&gt;🇳🇴 Norsk&lt;/a&gt; | &lt;a href="https://github.com/HexmosTech/git-lrc/readme/README.pt.md" rel="noopener noreferrer"&gt;🇵🇹 Português&lt;/a&gt; | &lt;a href="https://github.com/HexmosTech/git-lrc/readme/README.ru.md" rel="noopener noreferrer"&gt;🇷🇺 Русский&lt;/a&gt; | &lt;a href="https://github.com/HexmosTech/git-lrc/readme/README.sq.md" rel="noopener noreferrer"&gt;🇦🇱 Shqip&lt;/a&gt; | &lt;a href="https://github.com/HexmosTech/git-lrc/readme/README.zh.md" rel="noopener noreferrer"&gt;🇨🇳 中文&lt;/a&gt; |&lt;/p&gt;
&lt;br&gt;
&lt;br&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/948c8f2d5cf41b48985cd364d48c3a2dc9bfbfd42eab3e0a9a1b3e61f5f17ce3/68747470733a2f2f6865786d6f732e636f6d2f66726565646576746f6f6c732f7075626c69632f6c725f6c6f676f2e737667"&gt;&lt;img width="60" alt="git-lrc logo" src="https://camo.githubusercontent.com/948c8f2d5cf41b48985cd364d48c3a2dc9bfbfd42eab3e0a9a1b3e61f5f17ce3/68747470733a2f2f6865786d6f732e636f6d2f66726565646576746f6f6c732f7075626c69632f6c725f6c6f676f2e737667"&gt;&lt;/a&gt;
&lt;br&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;git-lrc&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Free, Unlimited AI Code Reviews That Run on Commit&lt;/h2&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
&lt;p&gt;&lt;a href="https://www.producthunt.com/products/git-lrc?embed=true&amp;amp;utm_source=badge-top-post-badge&amp;amp;utm_medium=badge&amp;amp;utm_campaign=badge-git-lrc" rel="nofollow noopener noreferrer"&gt;&lt;img alt="git-lrc - Free, unlimited AI code reviews that run on commit | Product Hunt" width="200" src="https://camo.githubusercontent.com/87bf2d4283c1e0aa99e254bd17fefb1c67c0c0d39300043a243a4aa633b6cecc/68747470733a2f2f6170692e70726f6475637468756e742e636f6d2f776964676574732f656d6265642d696d6167652f76312f746f702d706f73742d62616467652e7376673f706f73745f69643d31303739323632267468656d653d6c6967687426706572696f643d6461696c7926743d31373731373439313730383638"&gt;&lt;/a&gt;
 &lt;/p&gt;
&lt;br&gt;
&lt;a href="https://goreportcard.com/report/github.com/HexmosTech/git-lrc" rel="nofollow noopener noreferrer"&gt;&lt;img alt="Go Report Card" src="https://camo.githubusercontent.com/e74c0651c3ee9165a2ed01cb0f6842c494029960df30eb9c24cf622d3d21bf46/68747470733a2f2f676f7265706f7274636172642e636f6d2f62616467652f6769746875622e636f6d2f4865786d6f73546563682f6769742d6c7263"&gt;&lt;/a&gt; &lt;a href="https://github.com/HexmosTech/git-lrc/actions/workflows/gitleaks.yml" rel="noopener noreferrer"&gt;&lt;img alt="gitleaks.yml" title="gitleaks.yml: Secret scanning workflow" src="https://github.com/HexmosTech/git-lrc/actions/workflows/gitleaks.yml/badge.svg"&gt;&lt;/a&gt; &lt;a href="https://github.com/HexmosTech/git-lrc/actions/workflows/osv-scanner.yml" rel="noopener noreferrer"&gt;&lt;img alt="osv-scanner.yml" title="osv-scanner.yml: Dependency vulnerability scan" src="https://github.com/HexmosTech/git-lrc/actions/workflows/osv-scanner.yml/badge.svg"&gt;&lt;/a&gt; &lt;a href="https://github.com/HexmosTech/git-lrc/actions/workflows/govulncheck.yml" rel="noopener noreferrer"&gt;&lt;img alt="govulncheck.yml" title="govulncheck.yml: Go vulnerability check" src="https://github.com/HexmosTech/git-lrc/actions/workflows/govulncheck.yml/badge.svg"&gt;&lt;/a&gt; &lt;a href="https://github.com/HexmosTech/git-lrc/actions/workflows/semgrep.yml" rel="noopener noreferrer"&gt;&lt;img alt="semgrep.yml" title="semgrep.yml: Static analysis security scan" src="https://github.com/HexmosTech/git-lrc/actions/workflows/semgrep.yml/badge.svg"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer" href="https://github.com/HexmosTech/git-lrc/./gfx/dependabot-enabled.svg"&gt;&lt;img alt="dependabot-enabled" title="dependabot-enabled: Automated dependency updates are enabled" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FHexmosTech%2Fgit-lrc%2F.%2Fgfx%2Fdependabot-enabled.svg"&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;

&lt;p&gt;AI agents write code fast. They also &lt;em&gt;silently remove logic&lt;/em&gt;, change behavior, and introduce bugs -- without telling you. You often find out in production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;git-lrc&lt;/code&gt; fixes this.&lt;/strong&gt; It hooks into &lt;code&gt;git commit&lt;/code&gt; and reviews every diff &lt;em&gt;before&lt;/em&gt; it lands. 60-second setup. Completely free.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;See It In Action&lt;/h2&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;See git-lrc catch serious security issues such as leaked credentials, expensive cloud
operations, and sensitive material in log statements&lt;/p&gt;
&lt;/blockquote&gt;

  
    
    

    &lt;span class="m-1"&gt;git-lrc-intro-60s.mp4&lt;/span&gt;
    
  

  

  


&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;🤖 &lt;strong&gt;AI agents silently break things.&lt;/strong&gt; Code removed. Logic changed. Edge cases gone. You won't notice until production.&lt;/li&gt;
&lt;li&gt;🔍 &lt;strong&gt;Catch it before it ships.&lt;/strong&gt; AI-powered inline comments show you &lt;em&gt;exactly&lt;/em&gt; what changed and what looks wrong.&lt;/li&gt;
&lt;li&gt;🔁 &lt;strong&gt;Build a&lt;/strong&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/HexmosTech/git-lrc" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How a Hobby API Collection and Execution Tool is Evolving into a Product</title>
      <dc:creator>Athreya aka Maneshwar</dc:creator>
      <pubDate>Thu, 12 Dec 2024 18:38:38 +0000</pubDate>
      <link>https://forem.com/hexmos/how-a-hobby-api-collection-and-execution-tool-is-evolving-into-a-product-299n</link>
      <guid>https://forem.com/hexmos/how-a-hobby-api-collection-and-execution-tool-is-evolving-into-a-product-299n</guid>
      <description>&lt;p&gt;In any startup, managing APIs across multiple services is a common challenge. &lt;/p&gt;

&lt;p&gt;We faced three main issues:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Documenting APIs&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Publishing the documentation&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Updating it whenever APIs change&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each of these had its own set of questions: &lt;em&gt;how to do it, where to do it, what tools to use, and who would take ownership.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To tackle this, our team decided to consolidate all APIs into a single repository called &lt;strong&gt;APIHub&lt;/strong&gt;. Each service’s APIs were stored in a simple and consistent &lt;a href="https://hexmos.com/lama2/explanation/syntax.html" rel="noopener noreferrer"&gt;format&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET | POST | PUT | DELETE | PATCH  
${baseurl}/endpoint  
{  
  "body": "if present"  
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We named the files according to their function. Below is an example of a &lt;code&gt;.l2&lt;/code&gt; file for a "Leave Apply" API, along with a sidebar showing other APIs in the repository:  &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%2F1smezz8e6wbuv0qbz57a.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%2F1smezz8e6wbuv0qbz57a.png" alt="VSCode APIHub Leave API" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Improving Documentation Practices
&lt;/h3&gt;

&lt;p&gt;We made it mandatory to include the corresponding &lt;code&gt;.l2&lt;/code&gt; file in every pull/merge request. If it wasn’t there, the request wouldn’t be approved. This simple rule increased API documentation consistency across the team.&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%2Fga7zejk7jbii4bukaky1.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%2Fga7zejk7jbii4bukaky1.png" alt="Merge requests" width="536" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  From Documentation to Execution
&lt;/h3&gt;

&lt;p&gt;We soon realized that manually testing APIs by copying URLs and payloads to tools like Postman was time-consuming. So, we built a CLI tool called &lt;strong&gt;Lama2&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
&lt;a href="https://hexmos.com/lama2/index.html?ref=dev.to"&gt;Lama2&lt;/a&gt; is a plain-text API manager optimized for Git-based collaboration.  &lt;/p&gt;

&lt;p&gt;With Lama2, you could pass a &lt;code&gt;.l2&lt;/code&gt; file as input, and the CLI would execute the API and show the response in the terminal:  &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%2Fuk0ih1nqt3dbl1as9vzs.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%2Fuk0ih1nqt3dbl1as9vzs.gif" alt="Lama2 cli" width="758" height="471"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;This saved us from constant copy-pasting, but switching directories to find &lt;code&gt;.l2&lt;/code&gt; files was still tedious:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lovestaco@i3nux:~/apihub/feedback/fb_v3/leave$ l2 apply_leave.l2  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Taking it to VSCode
&lt;/h3&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%2Fpj2z9mcexhsx46k7gr2p.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%2Fpj2z9mcexhsx46k7gr2p.png" alt="Options" width="630" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To streamline things further, we developed a &lt;a href="https://marketplace.visualstudio.com/items?itemName=hexmos.Lama2" rel="noopener noreferrer"&gt;VSCode extension&lt;/a&gt;. It came with features that made our workflow even smoother:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Execute &lt;code&gt;.l2&lt;/code&gt; files directly in the editor
&lt;/li&gt;
&lt;li&gt;Copy the file’s Git URL for easy sharing
&lt;/li&gt;
&lt;li&gt;Prettify JSON payloads
&lt;/li&gt;
&lt;li&gt;Generate code snippets for any language from &lt;code&gt;.l2&lt;/code&gt; syntax
&lt;/li&gt;
&lt;li&gt;Create templates for new APIs in seconds
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://journal.hexmos.com/lama2-lsp-journey/" rel="noopener noreferrer"&gt;Auto-completion of variables using LSP&lt;/a&gt;&lt;/li&gt;
&lt;/ol&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%2F1eqnfymj1v83fage61dt.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%2F1eqnfymj1v83fage61dt.gif" alt=" " width="1489" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This extension quickly became a favorite among the team, and we decided to release it on GitHub so others could benefit too.  &lt;/p&gt;

&lt;h3&gt;
  
  
  The Next Problem: Scaling Documentation
&lt;/h3&gt;

&lt;p&gt;As our APIs grew, we asked ourselves:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Why manually document APIs for each service?&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Isn’t it time-consuming to update documentation for every change?&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that’s where the next chapter of our journey begins...&lt;br&gt;
Follow me to learn what happens next in my next post.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>From Vulnerabilities to Vault: How We Stopped Hardcoding Secrets and Started Using Hashicorp Vault</title>
      <dc:creator>Athreya aka Maneshwar</dc:creator>
      <pubDate>Sun, 17 Nov 2024 14:26:53 +0000</pubDate>
      <link>https://forem.com/hexmos/from-vulnerabilities-to-vault-how-we-stopped-hardcoding-secrets-and-started-using-hashicorp-vault-ajn</link>
      <guid>https://forem.com/hexmos/from-vulnerabilities-to-vault-how-we-stopped-hardcoding-secrets-and-started-using-hashicorp-vault-ajn</guid>
      <description>&lt;p&gt;We recently &lt;a href="https://journal.hexmos.com/install-nomad-in-production/" rel="noopener noreferrer"&gt;migrated our infrastructure from Kubernetes to HashiCorp Nomad&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Soon after, we &lt;a href="https://journal.hexmos.com/consul-service-discovery/" rel="noopener noreferrer"&gt;encountered service discovery issues&lt;/a&gt; and integrated Consul to address them.&lt;/p&gt;

&lt;p&gt;At this point, we were feeling a bit more relaxed, knowing that we could dedicate less time to infrastructure and focus more on product development, as we don’t have a separate resource dedicated to DevOps.&lt;/p&gt;

&lt;p&gt;However, we encountered another challenge—a Nomad port was open on the Node, and a friendly person helped us identify this vulnerability.&lt;/p&gt;

&lt;p&gt;They were able to access it directly using the server's IP address, without needing a domain.&lt;/p&gt;

&lt;p&gt;As a result, we had to quickly rotate all our payment keys, AWS credentials, and other secrets.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: Hardcoded Secrets and Security Risks
&lt;/h3&gt;

&lt;p&gt;In some areas, our team had hardcoded keys directly into the codebase.&lt;/p&gt;

&lt;p&gt;This meant there wasn’t a single place to manage these secrets, and updating them across our systems was a manual, error-prone process.&lt;/p&gt;

&lt;p&gt;It was clear we needed a solution that would allow us to change a secret in one place, and have it propagate automatically throughout the system.&lt;/p&gt;

&lt;p&gt;Initially, we considered using AWS Secrets Manager alongside our &lt;a href="https://docs.gitlab.com/ee/ci/" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt; CI/CD pipeline to inject environment variables during container deployments.&lt;/p&gt;

&lt;p&gt;However, this approach required passing a large number of variables as arguments to &lt;code&gt;nomad job run&lt;/code&gt; from the project's &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;, which cluttered our configuration and reduced system readability.&lt;/p&gt;

&lt;p&gt;That’s when we thought, &lt;em&gt;Why not go full &lt;a href="https://developer.hashicorp.com/" rel="noopener noreferrer"&gt;HashiStack&lt;/a&gt;?&lt;/em&gt; We decided to explore &lt;a href="https://developer.hashicorp.com/vault" rel="noopener noreferrer"&gt;Vault&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exploring Secret Management Options
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://aws.amazon.com/secrets-manager/" rel="noopener noreferrer"&gt;AWS Secrets Manager&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;AWS Secrets Manager&lt;/em&gt; offers capabilities for managing and rotating secrets such as database credentials, API keys, and other sensitive information.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Access Management&lt;/strong&gt;: AWS Secrets Manager relies on AWS IAM (Identity and Access Management) for managing permissions. For granular control, additional tools like &lt;em&gt;Chamber&lt;/em&gt; maybe required. Chamber leverages AWS IAM roles and policies to restrict access by namespace, simplifying the process of partitioning secrets across different environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Cloud Limitations&lt;/strong&gt;: While effective within AWS (our existing infra), use in multi-cloud environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost of service:&lt;/strong&gt; $0.40 per secret per month.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://developer.hashicorp.com/vault" rel="noopener noreferrer"&gt;HashiCorp Vault&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;Hashicorp Vault&lt;/em&gt; provides a robust, identity-based security solution designed to authenticate and authorize access to secrets automatically, supporting a wide range of infrastructures.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Access Control&lt;/strong&gt;: Vault offers RBAC (Role-Based Access Control) for user logins, enabling administrators to manage human access to secrets. For machine access, Vault provides policy-driven access with leases, allowing for precise control over which applications can access secrets and for how long. This includes options like automatic key rotation and expiration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Secrets Generation&lt;/strong&gt;: Vault can dynamically generate AWS IAM credentials, providing temporary access that’s tightly controlled by policies and easily revoked if needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Environment Compatibility&lt;/strong&gt;: Vault supports cross-region, cross-cloud, and cross-datacenter replication, making it a seamless choice for organizations with multi-cloud setups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost of service:&lt;/strong&gt; Free.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why We Chose HashiCorp Vault
&lt;/h3&gt;

&lt;p&gt;Vault offered the ideal solution: it allowed &lt;a href="https://developer.hashicorp.com/nomad" rel="noopener noreferrer"&gt;Nomad&lt;/a&gt; to request sensitive information, such as payment secrets or AWS keys, directly from Vault.&lt;/p&gt;

&lt;p&gt;Vault would verify if Nomad was authorized to access those secrets and promptly deliver the data.&lt;/p&gt;

&lt;p&gt;This way, we could centralize key management, automate secret updates, and improve security by avoiding hardcoded keys.&lt;/p&gt;

&lt;p&gt;We developed a proof of concept, where we demonstrated Vault's ability to manage key rotation and securely store secrets in one place, accessible across all our projects.&lt;/p&gt;

&lt;p&gt;This not only simplified secret management but also ensured consistency and security for our entire infrastructure stack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://journal.hexmos.com/how-to-use-hashicorp-vault/" rel="noopener noreferrer"&gt;Continue reading full article...&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>webdev</category>
      <category>security</category>
    </item>
    <item>
      <title>Why We Chose NGINX + HashiStack Over Kubernetes for Our Service Discovery Needs</title>
      <dc:creator>Athreya aka Maneshwar</dc:creator>
      <pubDate>Sun, 06 Oct 2024 17:20:20 +0000</pubDate>
      <link>https://forem.com/hexmos/why-we-chose-nginx-hashistack-over-kubernetes-for-our-service-discovery-needs-3ebd</link>
      <guid>https://forem.com/hexmos/why-we-chose-nginx-hashistack-over-kubernetes-for-our-service-discovery-needs-3ebd</guid>
      <description>&lt;p&gt;We recently switched from &lt;a href="https://journal.hexmos.com/install-nomad-in-production/" rel="noopener noreferrer"&gt;Kubernetes to Nomad&lt;/a&gt; to manage our infrastructure. At first, with two nodes and multiple services,we had a hard time getting the request routing to work &lt;strong&gt;reliably&lt;/strong&gt;.&lt;br&gt;
In this post, I’ll walk through how we built an &lt;strong&gt;efficient and low-cost service discovery solution&lt;/strong&gt; for our infrastructure—and why it could benefit others facing similar routing issues.&lt;/p&gt;

&lt;p&gt;Spoiler: You can achieve smooth results &lt;strong&gt;without&lt;/strong&gt; needing &lt;strong&gt;NGINX Plus&lt;/strong&gt;, thanks to NGINX’s robust features and the power of &lt;strong&gt;open-source modules&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Routing Problem: A Snapshot of Our Setup
&lt;/h2&gt;

&lt;p&gt;At the core of our infrastructure lies a &lt;strong&gt;typical setup&lt;/strong&gt;: a browser making requests to our server, an &lt;strong&gt;NGINX reverse proxy&lt;/strong&gt; forwarding those requests, and &lt;strong&gt;Nomad&lt;/strong&gt; managing the services on multiple nodes.&lt;br&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%2Fhj8uequf4i4f49n8yt96.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%2Fhj8uequf4i4f49n8yt96.png" alt="Group 565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Initially, we had &lt;strong&gt;hardcoded the node IPs&lt;/strong&gt; into our &lt;a href="https://nginx.org/en/" rel="noopener noreferrer"&gt;Nginx&lt;/a&gt; configuration,&lt;br&gt;
which caused a major problem when services were &lt;strong&gt;redeployed to different nodes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Every redeployment required &lt;strong&gt;manual NGINX configuration&lt;/strong&gt; updates.&lt;br&gt;
This quickly became &lt;strong&gt;unsustainable&lt;/strong&gt; as the number of services grew.&lt;/p&gt;

&lt;p&gt;That’s when we decided to &lt;strong&gt;integrate service discovery&lt;/strong&gt; into our stack.&lt;/p&gt;
&lt;h2&gt;
  
  
  Service Discovery: Why It Matters
&lt;/h2&gt;

&lt;p&gt;Service discovery is the process of automatically detecting services on a network.&lt;br&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%2Fuoj5ynpmde0291n8tcbs.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%2Fuoj5ynpmde0291n8tcbs.png" alt="Group 571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a dynamic, multi-node setup like ours, services constantly &lt;strong&gt;shift between nodes&lt;/strong&gt; and deployments. Without proper service discovery, it’s impossible to route requests to the right service on the right node.&lt;/p&gt;

&lt;p&gt;Without this, a request arriving at our server might hit the &lt;strong&gt;wrong service&lt;/strong&gt;, or worse, &lt;strong&gt;fail completely&lt;/strong&gt; because NGINX had no idea where the service was running.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nomad alone doesn’t solve this issue&lt;/strong&gt;. That’s where &lt;a href="https://www.consul.io/" rel="noopener noreferrer"&gt;Consul&lt;/a&gt; comes in.&lt;/p&gt;

&lt;p&gt;Consul tracks the &lt;strong&gt;location and port&lt;/strong&gt; of each service deployed via Nomad, and &lt;strong&gt;NGINX uses this data&lt;/strong&gt; to ensure requests reach their intended destinations. This is key if you want &lt;strong&gt;scalable&lt;/strong&gt;, &lt;strong&gt;robust routing&lt;/strong&gt; without hardcoding IP addresses or relying on static configurations.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Should You Care?
&lt;/h2&gt;

&lt;p&gt;This solution isn’t just a neat technical solution, but &lt;strong&gt;a necessary move&lt;/strong&gt; if you want to avoid downtime by making request &lt;strong&gt;go to healthy services&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Whether you’re a startup or running a large-scale app, the right &lt;strong&gt;service discovery mechanism&lt;/strong&gt; helps reduce complexity, improve reliability, and &lt;strong&gt;keep your infrastructure flexible&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of it like moving from a fragile system to something far more robust without the need for heavyweight orchestration like Kubernetes.&lt;br&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%2Ffsakk9ev12zb8chhwjei.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%2Ffsakk9ev12zb8chhwjei.png" alt="0_72Hjj4cz4_GTMDTO"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  NGINX and Service Discovery: Exploring the Options
&lt;/h2&gt;

&lt;p&gt;While NGINX offers &lt;strong&gt;built-in service discovery features&lt;/strong&gt;, they are part of &lt;strong&gt;NGINX Plus&lt;/strong&gt;, the enterprise version.&lt;/p&gt;

&lt;p&gt;We opted to explore &lt;strong&gt;open-source alternatives&lt;/strong&gt; and found a pre-built &lt;strong&gt;open-source NGINX module&lt;/strong&gt; that allows NGINX to retrieve &lt;strong&gt;service location data from Consul&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here’s why we made this choice:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cost Considerations&lt;/strong&gt;: NGINX Plus requires &lt;strong&gt;paid subscriptions&lt;/strong&gt;, which means ongoing management of licenses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature Set&lt;/strong&gt;: NGINX Plus comes with a broad set of features, but many of them were unnecessary for our specific use case.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full Control&lt;/strong&gt;: By using a &lt;strong&gt;purely FOSS solution&lt;/strong&gt;, we maintain &lt;strong&gt;100% control&lt;/strong&gt; over our infrastructure, without relying on external enterprise solutions.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Why This Solution Worked
&lt;/h3&gt;

&lt;p&gt;We’re now serving &lt;strong&gt;all internal and customer-facing&lt;/strong&gt; &lt;a href="https://hexmos.com/#products" rel="noopener noreferrer"&gt;Hexmos apps&lt;/a&gt; using this &lt;strong&gt;custom NGINX and HashiStack setup&lt;/strong&gt;. The &lt;strong&gt;custom NGINX&lt;/strong&gt; is necessary because of our &lt;strong&gt;legacy configurations&lt;/strong&gt; as we &lt;a href="https://journal.hexmos.com/install-nomad-in-production/" rel="noopener noreferrer"&gt;transitioned&lt;/a&gt; from Kubernetes. This makes our case particularly interesting for others facing similar transitions.&lt;/p&gt;

&lt;p&gt;A lot of smaller teams are using NGINX with PM2 to manage their processes. While that works, it doesn’t scale easily if you’re trying to handle multiple nodes or containers.&lt;/p&gt;

&lt;p&gt;For teams using &lt;a href="https://github.com/nginx/nginx" rel="noopener noreferrer"&gt;NGINX&lt;/a&gt;+&lt;a href="https://github.com/Unitech/pm2" rel="noopener noreferrer"&gt;PM2&lt;/a&gt;, moving to NGINX + HashiStack is a more robust and flexible solution—a great fit for startups looking for scalability without the complexity of Kubernetes.&lt;/p&gt;

&lt;p&gt;In fact, many startups are likely using PM2, and very few truly need Kubernetes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Moving to NGINX+HashiStack
&lt;/h2&gt;

&lt;p&gt;Larger organizations like &lt;a href="https://github.com/zerodha/nomad-cluster-setup" rel="noopener noreferrer"&gt;Zerodha&lt;/a&gt; and &lt;a href="https://blog.cloudflare.com/how-we-use-hashicorp-nomad/" rel="noopener noreferrer"&gt;Cloudflare&lt;/a&gt; are using &lt;strong&gt;Nomad&lt;/strong&gt; to manage their infrastructure. Both companies have substantial setups but avoid Kubernetes, showing that &lt;strong&gt;Nomad + Consul&lt;/strong&gt; can scale effectively without the overhead of Kubernetes.&lt;/p&gt;

&lt;p&gt;For startups, &lt;strong&gt;HashiStack&lt;/strong&gt; is like &lt;strong&gt;PM2 on steroids&lt;/strong&gt;—providing &lt;strong&gt;multi-node and Docker control&lt;/strong&gt;. It allows you to &lt;strong&gt;easily manage different&lt;/strong&gt; &lt;a href="https://developer.hashicorp.com/nomad/docs/drivers" rel="noopener noreferrer"&gt;workloads&lt;/a&gt;—whether &lt;strong&gt;binaries&lt;/strong&gt; or &lt;strong&gt;Docker container&lt;/strong&gt;—across multiple nodes, while being lightweight enough for smaller operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kubernetes&lt;/strong&gt; is often &lt;strong&gt;overkill&lt;/strong&gt; unless you’re running at a very large scale. &lt;strong&gt;HashiStack&lt;/strong&gt; with custom NGINX offers a much simpler, cost-effective, and scalable alternative.&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%2Ffx30wwlqytf2fa9smsoh.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%2Ffx30wwlqytf2fa9smsoh.png" alt="Group 579"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our transition from Kubernetes to Nomad was &lt;a href="https://journal.hexmos.com/install-nomad-in-production/#kubernetes-vs-nomad-a-clash-of-titans" rel="noopener noreferrer"&gt;eye-opening&lt;/a&gt;. Here’s why this solution could be a good fit for teams considering an upgrade:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Simplicity&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HashiStack + NGINX&lt;/strong&gt;: Lightweight and easy to manage with just two binaries—&lt;strong&gt;Nomad&lt;/strong&gt; (orchestration) and &lt;strong&gt;Consul&lt;/strong&gt; (service discovery). The custom NGINX module integrates seamlessly without complex setups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes&lt;/strong&gt;: A full-featured but complex platform, requiring numerous services and configurations. Often needs a dedicated team for ongoing management.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Flexibility and Scale&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HashiStack + NGINX&lt;/strong&gt;: Supports a range of workloads (containers, binaries, VMs) and scales smoothly across nodes and regions. Ideal for startups or teams seeking flexible deployment management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes&lt;/strong&gt;: Excels in container-heavy environments but can be overkill for smaller setups. Its complexity makes scaling harder to manage.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cost Efficiency and Operational Effort&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Both &lt;strong&gt;HashiStack + NGINX and Kubernetes&lt;/strong&gt; are open-source, offering flexibility without upfront licensing fees.
However, their cost efficiency varies when factoring in operational complexity and labor hours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HashiStack + NGINX&lt;/strong&gt;: Free and open-source, avoiding enterprise license costs (like NGINX Plus). Easier to set up and maintain, making it a cost-effective solution for smaller teams with limited DevOps resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes&lt;/strong&gt;: Also open-source may require additional tools (e.g., Ingress controllers), increasing operational complexity. Its steep learning curve and management demands can lead to higher labor costs.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Options for Service Discovery Integration with NGINX
&lt;/h2&gt;

&lt;p&gt;When comparing &lt;strong&gt;Nomad's template stanza&lt;/strong&gt; with &lt;strong&gt;Consul Template&lt;/strong&gt;, the choice largely depends on your use case, but both have their strengths and challenges. Let’s break down the pros and cons of each approach:&lt;/p&gt;
&lt;h3&gt;
  
  
  1. &lt;strong&gt;Nomad Template Stanza&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Usage&lt;/strong&gt;: The template stanza in Nomad is often used for injecting dynamic content (like load balancer configs) directly into tasks. It relies heavily on the integration with Consul to fetch service details and generate configurations dynamically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tight integration&lt;/strong&gt;: Works seamlessly with Nomad jobs and Consul service discovery. It automatically reconfigures services when Nomad or Consul detects changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No extra processes&lt;/strong&gt;: Since it's native to Nomad, you don’t need to run a separate daemon for templates to update.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signal-based reload&lt;/strong&gt;: Can signal the containerized service (e.g., NGINX) to reload configurations on updates (&lt;code&gt;SIGHUP&lt;/code&gt; signal).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;All-in-one Job Spec&lt;/strong&gt;: Everything is packed into the same Nomad job file (code, template logic, service configuration), which could simplify management for some.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complexity&lt;/strong&gt;: The inline template can get quite complex and difficult to maintain, especially as your configuration grows. Writing Nomad templates with complex &lt;code&gt;range&lt;/code&gt; statements to handle service discovery (like the upstream block for NGINX) can become cumbersome.
&lt;em&gt;Example:&lt;/em&gt; &lt;code&gt;{{ range service "echo-server" }} ... {{ else }}server 127.0.0.1:65535;{{ end }}&lt;/code&gt; could be tricky for large applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited portability&lt;/strong&gt;: The template configuration is tied to Nomad’s job files, which can make it harder to migrate or adapt to environments where Nomad is not in use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Steeper learning curve&lt;/strong&gt;: The embedded logic in the template stanza can feel overwhelming. For newcomers, this can make understanding and debugging more difficult.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2. &lt;strong&gt;Consul Template Daemon&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Usage&lt;/strong&gt;: Consul Template is a standalone daemon that fetches data from Consul, Nomad, or Vault and renders it into templates, offering more flexibility for updating service configurations. It can be used independently or alongside Nomad.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Separation of concerns&lt;/strong&gt;: The configuration and template management are decoupled from Nomad, so you can manage templates independently. This is useful when you have multiple services and configurations that need to be updated based on Consul data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Powerful templating features&lt;/strong&gt;: Consul Template can handle more complex scenarios and logic than the Nomad template stanza due to its broader templating syntax.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run custom commands&lt;/strong&gt;: It can run any arbitrary command after rendering a template (like restarting a service), offering more flexibility in how you manage updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-system&lt;/strong&gt;: Consul Template can be used for other systems as well (e.g., Vault or just plain Consul), making it more versatile and portable.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Extra daemon&lt;/strong&gt;: You need to run an additional process (&lt;code&gt;consul-template&lt;/code&gt;) which adds operational overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual setup and management&lt;/strong&gt;: It requires setting up configuration and managing the lifecycle of the daemon. You’ll also need to configure reload logic manually, which could be overkill for smaller systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reloading complexity&lt;/strong&gt;: You have to configure signals or restart logic to handle service restarts correctly, and incorrect configurations could lead to service downtime or stale configurations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  3. &lt;strong&gt;DNS Service Discovery with NGINX Plus&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We wish we could tell you more about NGINX Plus, but it's a paid tool and we haven't had a chance to try it out. From what I've heard, it's a really smooth experience. It automatically keeps track of where your services are and sends traffic to the right places. If you're looking for a hassle-free solution and don't mind spending a bit extra, NGINX Plus might be a great fit.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. &lt;strong&gt;NGINX’s ngx_http_consul_backend_module&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/hashicorp/ngx_http_consul_backend_module" rel="noopener noreferrer"&gt;ngx_http_consul_backend_module&lt;/a&gt; is a NGINX add-on that I've found incredibly useful for establishing a &lt;strong&gt;direct connection between NGINX and Consul&lt;/strong&gt;. This module uses the &lt;strong&gt;Consul Go backend&lt;/strong&gt; to efficiently discover and route to healthy services.&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%2F7neffi2st0roh23ekjn9.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%2F7neffi2st0roh23ekjn9.png" alt="Group 564"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No need for NGINX reloads&lt;/strong&gt;: Since NGINX queries the &lt;a href="https://github.com/hashicorp/consul/tree/main/api" rel="noopener noreferrer"&gt;Consul Go API client&lt;/a&gt; for healthy services on each request, there’s no need to reload NGINX whenever a &lt;strong&gt;service moves between nodes&lt;/strong&gt; or when new instances are added.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified service discovery&lt;/strong&gt;: Module directly &lt;strong&gt;route each request through Consul&lt;/strong&gt;, ensuring that traffic is always directed to &lt;strong&gt;healthy services&lt;/strong&gt;. NGINX fetches the healthy services without needing custom health checks, external scripts, or manual intervention.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved reliability&lt;/strong&gt;: Since the Consul backend only provides information about healthy hosts, there is &lt;strong&gt;no risk&lt;/strong&gt; of requests being sent to &lt;strong&gt;dead or unhealthy services&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficient connection pooling&lt;/strong&gt;: By using the official Consul Go API client, the module benefits from efficient connection management, contributing to faster and more reliable service discovery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Familiar configuration interface&lt;/strong&gt;: The setup with Consul and NGINX is relatively straightforward, and familiar configuration directives (like &lt;code&gt;proxy_pass&lt;/code&gt; and &lt;code&gt;$backend&lt;/code&gt;) make it easy to integrate into existing NGINX configurations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Need to rebuild NGINX from source&lt;/strong&gt;: The biggest downside is that you need to &lt;strong&gt;rebuild NGINX from source&lt;/strong&gt; with this module. This adds an extra step to your deployment process and makes updates or migrations slightly more cumbersome. If you’re using packaged NGINX versions from repositories, this could be a hassle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance overhead&lt;/strong&gt;: Rebuilding from source means you’ll need to maintain your own version of NGINX, handle upgrades, and ensure compatibility with other NGINX modules you may want to use.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Workflow
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;A request arrives at NGINX that fits a specific &lt;strong&gt;location block&lt;/strong&gt; which includes a Consul directive.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    location / &lt;span class="o"&gt;{&lt;/span&gt;
        consul &lt;span class="nv"&gt;$backend&lt;/span&gt; echo-server-lovestaco-com&lt;span class="p"&gt;;&lt;/span&gt;
        add_header X-Debug-Backend &lt;span class="nv"&gt;$backend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        proxy_pass http://&lt;span class="nv"&gt;$backend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;NGINX then calls the &lt;strong&gt;ngx_http_consul_backend&lt;/strong&gt; function, providing it with two pieces of information.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;The first piece of information is a variable where the result will be stored (for example, &lt;code&gt;$backend&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The second piece of information is the name of the Consul service to which the request should be routed (like &lt;code&gt;echo-server-lovestaco-com&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The &lt;a href="https://github.com/hashicorp/ngx_http_consul_backend_module/blob/62c7a501aeeab71bb72dd620ad6f00e848be0b1e/src/ngx_http_consul_backend_module.c#L52" rel="noopener noreferrer"&gt;ngx_http_consul_backend&lt;/a&gt; function uses &lt;a href="https://man7.org/linux/man-pages/man3/dlopen.3.html" rel="noopener noreferrer"&gt;dlopen&lt;/a&gt; to load the &lt;a href="https://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html" rel="noopener noreferrer"&gt;shared C library&lt;/a&gt; (the &lt;a href="https://superuser.com/questions/71404/what-is-an-so-file" rel="noopener noreferrer"&gt;.so&lt;/a&gt; file) and calls the Go function defined within that library.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This Go function interacts with Consul using the official API client library. It gathers a list of available IP addresses and selects one to return.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The chosen IP address is sent back to the &lt;strong&gt;ngx_http_consul_backend&lt;/strong&gt; function, and assigned to &lt;code&gt;$backend&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The next step involves using NGINX's built-in &lt;code&gt;proxy_pass&lt;/code&gt; directive to forward the traffic to the selected host.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Below image shows the flow of a request using consul&lt;br&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%2Fxl4v10hcjej7r65bs1ed.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%2Fxl4v10hcjej7r65bs1ed.png" alt="Consul 1-2024-10-02-145903"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Step-by-Step Guide on How We Made It Work by Rebuilding NGINX from Source
&lt;/h3&gt;
&lt;h4&gt;
  
  
  1. Install the Essential Build Tools
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt-get &lt;span class="nt"&gt;-yqq&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;build-essential curl git libpcre3 libpcre3-dev libssl-dev zlib1g-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  2. Download and Extract NGINX from Source
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /tmp
curl &lt;span class="nt"&gt;-sLo&lt;/span&gt; nginx.tgz https://nginx.org/download/nginx-1.24.0.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Extract the downloaded tarball to access the NGINX source code
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xzvf&lt;/span&gt; nginx.tgz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  4. Download and Extract the NGINX Development Kit (NDK)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Download the ngx_devel_kit module, which is required for building the backend.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sLo&lt;/span&gt; ngx_devel_kit-0.3.0.tgz https://github.com/simpl/ngx_devel_kit/archive/v0.3.0.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xzvf&lt;/span&gt; ngx_devel_kit-0.3.0.tgz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  6. Clone the &lt;code&gt;ngx_http_consul_backend_module&lt;/code&gt; Repository
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/hashicorp/ngx_http_consul_backend_module.git /go/src/github.com/hashicorp/ngx_http_consul_backend_module
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  7. Change Ownership of the NGINX Extensions Directory
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;whoami&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;whoami&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; /go/src/github.com/hashicorp/ngx_http_consul_backend_module
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;whoami&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;whoami&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; /usr/local/nginx/ext/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  8. Tidy Go Modules
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go mod tidy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  9. Compile the Go Code as a Shared C Library That NGINX Will Dynamically Load
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Set the CGO flags to include the &lt;code&gt;ngx_devel_kit&lt;/code&gt; directory
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CGO_CFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-I /tmp/ngx_devel_kit-0.3.0/src"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
go build &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-buildmode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;c-shared &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/local/nginx/ext/ngx_http_consul_backend_module.so &lt;span class="se"&gt;\&lt;/span&gt;
  ./ngx_http_consul_backend_module.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;This will compile the object file with symbols to &lt;code&gt;/usr/local/nginx/ext/nginx_http_consul_backend_module.so&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  10. Configure NGINX with Required Paths and Modules
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;To add a module during the NGINX build process, use the following configuration command
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /tmp/nginx-1.24.0

&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-g -O0"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
./configure &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--with-debug&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/nginx &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--sbin-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/sbin/nginx &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--conf-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/nginx/nginx.conf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--pid-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/run/nginx.pid &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--error-log-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/log/nginx/error.log &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--http-log-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/log/nginx/access.log &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--add-module&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/ngx_devel_kit-0.3.0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--add-module&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/go/src/github.com/hashicorp/ngx_http_consul_backend_module
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Common Configuration Options&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--prefix=/etc/nginx&lt;/code&gt;: Installation directory for Nginx binaries and configuration files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--sbin-path=/usr/sbin/nginx&lt;/code&gt;: Path to the Nginx binary executable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--conf-path=/etc/nginx/nginx.conf&lt;/code&gt;: Path to the main Nginx configuration file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--pid-path=/var/run/nginx.pid&lt;/code&gt;: Path to the Nginx process ID file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--error-log-path=/var/log/nginx/error.log&lt;/code&gt;: Path to the Nginx error log file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--http-log-path=/var/log/nginx/access.log&lt;/code&gt;: Path to the Nginx access log file.&lt;/li&gt;
&lt;li&gt;(Add other desired modules with &lt;code&gt;--with-modulename_module&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Make sure to include the &lt;code&gt;--add-module&lt;/code&gt; option for each static module you want to build with NGINX.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  11. Build and Install NGINX
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  12. Verify NGINX Installation and Configuration
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/usr/sbin/nginx &lt;span class="nt"&gt;-V&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Hardcoded Backend vs. Consul-driven Backend&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let's compare two scenarios&lt;/p&gt;
&lt;h4&gt;
  
  
  1. Hardcoded Backend
&lt;/h4&gt;

&lt;p&gt;This is sort of a traditional approach where you manually specify the IP address and port of the backend server in your NGINX configuration. Here's an example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;server &lt;span class="o"&gt;{&lt;/span&gt;
  listen        80&lt;span class="p"&gt;;&lt;/span&gt;
  server_name   one.example.com www.one.example.com&lt;span class="p"&gt;;&lt;/span&gt;

  location / &lt;span class="o"&gt;{&lt;/span&gt;
    proxy_pass     http://127.0.0.1:8080/&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c"&gt;# Hardcoded IP and port&lt;/span&gt;

    proxy_set_header  Host      &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    proxy_set_header  X-Real-IP  &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach has limitations&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Static Configuration:&lt;/strong&gt; If the backend server IP or port changes, you need to manually update the NGINX configuration and reload NGINX.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability Issues:&lt;/strong&gt; Manually managing configurations becomes cumbersome as your infrastructure grows.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. Consul-driven Backend with &lt;code&gt;ngx_http_consul_backend_module&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;ngx_http_consul_backend_module&lt;/code&gt; simplifies backend management by leveraging Consul's service discovery capabilities. Here's how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consul Service Listing:&lt;/strong&gt; First, list the available services registered in Consul using the &lt;code&gt;consul catalog services -tags&lt;/code&gt; command. This will display service names and tags for easier identification.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ubuntu@master:~&lt;span class="nv"&gt;$ &lt;/span&gt;consul catalog services &lt;span class="nt"&gt;-tags&lt;/span&gt;
consul           consul
one-example-com   one-example-com,primary
dns              primary
echo-server-1
nomad            http,rpc,serf
nomad-client     http
python-http-server  http,python-http-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NGINX Configuration:&lt;/strong&gt; Update your NGINX configuration to utilize the &lt;code&gt;consul&lt;/code&gt; directive within the location block. This directive retrieves the healthy backend server information for the specified service name and stores it in a variable (e.g., &lt;code&gt;$backend&lt;/code&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;server &lt;span class="o"&gt;{&lt;/span&gt;
  listen        80&lt;span class="p"&gt;;&lt;/span&gt;
  server_name   one.example.com www.one.example.com&lt;span class="p"&gt;;&lt;/span&gt;

  location / &lt;span class="o"&gt;{&lt;/span&gt;
    consul        &lt;span class="nv"&gt;$backend&lt;/span&gt; one-example-com&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c"&gt;# Retrieve backend from Consul&lt;/span&gt;
    proxy_pass     http://&lt;span class="nv"&gt;$backend&lt;/span&gt;/&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="c"&gt;# Use retrieved backend address&lt;/span&gt;

    proxy_set_header  Host      &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    proxy_set_header  X-Real-IP  &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Benefits of Consul-driven Backend&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Configuration:&lt;/strong&gt; NGINX automatically discovers healthy backend servers registered in Consul, eliminating the need for manual configuration updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; As your infrastructure grows with more backend servers, NGINX seamlessly adjusts to route traffic to healthy instances.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Additional Notes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remember to install and configure &lt;code&gt;ngx_http_consul_backend_module&lt;/code&gt; for this approach to work.&lt;/li&gt;
&lt;li&gt;Refer to the module's documentation for advanced configuration options.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By employing &lt;code&gt;ngx_http_consul_backend_module&lt;/code&gt;, you can achieve a dynamic and scalable backend management system for your NGINX server, simplifying configuration and enhancing overall application reliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: A Lightweight, Flexible Solution
&lt;/h2&gt;

&lt;p&gt;Switching from Kubernetes to Nomad allowed me to streamline our deployments, but it also required better service discovery to ensure smooth routing between services.&lt;/p&gt;

&lt;p&gt;By using Consul and an open-source NGINX module, we avoided the complexity and cost of NGINX Plus while still getting an efficient, scalable solution.&lt;/p&gt;

&lt;p&gt;For anyone currently running NGINX with PM2 or those looking for a simpler alternative to Kubernetes, NGINX with the HashiStack (Nomad + Consul) is a flexible, powerful, and cost-effective solution.&lt;br&gt;
It’s lightweight, robust, and much easier to manage at scale.&lt;/p&gt;

&lt;p&gt;If you're exploring service discovery for a similar setup, give it a try—it might be the neat solution you need.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Stay ahead of the curve! Subscribe for a weekly dose of insights on&lt;br&gt;
development, IT, operations, design, leadership and more.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/company/hexmos" rel="noopener noreferrer"&gt;&lt;br&gt;
    &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkarma-src-x02msdf8-23.s3.ap-south-1.amazonaws.com%2Fproduct-menu-logo%2Flinkedin.gif"&gt;&lt;br&gt;
  &lt;/a&gt;&lt;br&gt;
  &lt;a href="https://twitter.com/HexmosTech" rel="noopener noreferrer"&gt;&lt;br&gt;
    &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkarma-src-x02msdf8-23.s3.ap-south-1.amazonaws.com%2Fproduct-menu-logo%2Ftwitter.gif"&gt;&lt;br&gt;
  &lt;/a&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>nginx</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>Why your API Doc Structure Matters: How these 5 Principles make it easy for your readers</title>
      <dc:creator>Rijul Rajesh</dc:creator>
      <pubDate>Sun, 15 Sep 2024 14:30:00 +0000</pubDate>
      <link>https://forem.com/hexmos/why-your-api-doc-structure-matters-how-these-5-principles-make-it-easy-for-your-readers-1gdp</link>
      <guid>https://forem.com/hexmos/why-your-api-doc-structure-matters-how-these-5-principles-make-it-easy-for-your-readers-1gdp</guid>
      <description>&lt;h2&gt;
  
  
  Common Issues with API Docs: And How I Managed to Solve Them
&lt;/h2&gt;

&lt;p&gt;Good API Documentation is crucial for developers to understand and utilize the APIs in the best way possible, And it can contribute to the success of the project.&lt;/p&gt;

&lt;p&gt;A Project without good documentation is like a tool without proper instructions. No matter how good the tool is, if people don't understand, it's useless. So we need a proper idea of what common mistakes people commit when making API documentation for their project.&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%2Fk23k75y3oa8ojs5jnn43.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%2Fk23k75y3oa8ojs5jnn43.png" alt="alt text" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Discovering the right API that I need
&lt;/h3&gt;

&lt;p&gt;When a developer enters your documentation, they will usually have an &lt;strong&gt;specific goal in mind&lt;/strong&gt;. They will be searching for an API which they need from your documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Suppose your API docs includes an endpoint for sending emails&lt;br&gt;
If the developer searches with "email" then the relevant endpoint should come up right away.&lt;/p&gt;

&lt;p&gt;If the API Documentation has &lt;strong&gt;poor search functionality and unorganized documentation structure&lt;/strong&gt;, the developer can get exausted trying to find the API they need.&lt;/p&gt;

&lt;p&gt;The solution towards this problem is to implement a proper search functionality as well as a documentation structure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.divio.com/documentation-system/" rel="noopener noreferrer"&gt;Divio&lt;/a&gt; has a good structure that helps to easily grasp the contents.&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%2Fsmfqttt3cyl12p6qovss.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%2Fsmfqttt3cyl12p6qovss.png" alt="alt text" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
This system divides documentation into 4 main types&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Tutorials (Learning Oriented)&lt;/strong&gt;: Designed to teach, help a developer to acheive something from start to finish&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. How-To Guides(Problem oriented)&lt;/strong&gt;: Helps to solve a particular problem step by step&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Explanations (Understanding oriented)&lt;/strong&gt;: Provides in-depth explanations about how things work&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Reference(Information oriented)&lt;/strong&gt;: Serve as a technical resource if you need specific details&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Figuring out how to use it
&lt;/h3&gt;

&lt;p&gt;When API documentations are made, We can often forget that this documentation is meant to be &lt;strong&gt;read by a developer who has no prior experience working with the system&lt;/strong&gt;. So the they can often be left wondering what the endpoint does and what is the bigger picture in which this API belongs.&lt;/p&gt;

&lt;p&gt;To solve such issues, we need a detailed description, which should be beginner friendly, and should demonstrate real-world usecases&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
Suppose the following API is shown in the documentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://dogapi.dog/api/v2/breeds/{id}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user might not immediately know what the API does or what value to insert for &lt;code&gt;{id}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To resolve this, the documentation needs a clear description and a real-world example, like:&lt;/p&gt;

&lt;p&gt;The API (&lt;a href="https://dogapi.dog/api/v2/breeds" rel="noopener noreferrer"&gt;https://dogapi.dog/api/v2/breeds&lt;/a&gt;) provides detailed information about various dog breeds. This API allows users to retrieve specific data related to dog breeds, including attributes like name, physical characteristics, life expectancy, weight, temperament, and other related details.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://dogapi.dog/api/v2/breeds/68f47c5a-5115-47cd-9849-e45d3c378f12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Procrastination in creating the code and getting it done
&lt;/h3&gt;

&lt;p&gt;Even if the documentation is well-defined, &lt;strong&gt;the developer may still feel some friction in integrating the API&lt;/strong&gt; into his code. As a solution to this problem, we can have code snippets in various languages that can be generated from the API.&lt;/p&gt;

&lt;p&gt;The developer can directly copy and paste these snippets into his code and get things running quick.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For example&lt;/strong&gt;&lt;br&gt;
A python developer will prefer a ready made request like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://dogapi.dog/api/v2/breeds/68f47c5a-5115-47cd-9849-e45d3c378f12&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rather than a plain url&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://dogapi.dog/api/v2/breeds/68f47c5a-5115-47cd-9849-e45d3c378f12&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Handling Errors and Authentication
&lt;/h3&gt;

&lt;p&gt;Imagine a new developer who is using your API &lt;strong&gt;encounters an error or faces an issue related to authentication&lt;/strong&gt;.The first place they would look is your Documentation, to see what are the possible errors and the solutions which can be applied.&lt;/p&gt;

&lt;p&gt;But if the documentation doesn't have such information, then the &lt;strong&gt;whole debugging process can become painful&lt;/strong&gt;. This can even lead to the user ultimately dropping off from your API and searching for easier alternative solutions.&lt;/p&gt;

&lt;p&gt;We can solve this problem by listing the common errors, showing detailed troubleshooting solutions and link towards how the authentication tokens are to be obtained.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine a developer trying to access a protected API endpoint, but faces errors.&lt;/p&gt;

&lt;p&gt;The API docs should demo how to use the authentication token like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="s2"&gt;"https://httpbin.org/bearer"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_ACCESS_TOKEN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And show the possible errors like so.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Error Code&lt;/th&gt;
&lt;th&gt;Error Message&lt;/th&gt;
&lt;th&gt;Troubleshooting Tip&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;401&lt;/td&gt;
&lt;td&gt;Missing Authorization Header&lt;/td&gt;
&lt;td&gt;Ensure you include the &lt;code&gt;Authorization&lt;/code&gt; header with the Bearer Token.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;403&lt;/td&gt;
&lt;td&gt;Invalid or Expired Token&lt;/td&gt;
&lt;td&gt;Verify that the token is valid and has not expired.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;Internal Server Error&lt;/td&gt;
&lt;td&gt;Try again later, or contact support if the issue persists.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How Mainstream API Docs Structure Information for the Readers: A Case Study
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mintlify
&lt;/h3&gt;

&lt;p&gt;For our Mintlify example, let's visit the &lt;a href="https://infisical.com/docs/api-reference/endpoints/identities/create" rel="noopener noreferrer"&gt;infisical&lt;/a&gt; documentation.&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%2Fqxfe6h1yi527ymk1hqcb.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%2Fqxfe6h1yi527ymk1hqcb.png" width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Suppose, I want to look for an &lt;strong&gt;API for Creating Client secret&lt;/strong&gt;. There are 2 ways in which I can access it. One is to go through the Auth-related categories and find them. Or I can use the search bar and search for "Client secret". It's pretty straightforward to get the APIs we need.&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%2Fuyttjr28dvhxln7cnfqv.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%2Fuyttjr28dvhxln7cnfqv.png" width="800" height="512"&gt;&lt;/a&gt;&lt;br&gt;
Since we got the API we need, We need to understand it next. Details are provided related to &lt;strong&gt;Authorizations, Path parameters, Body, and Response&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now we have understood the API. The next thing to do is get the implementation done. We have the code snippets in various languages (cURL, Python, Javascript etc) as well as the responses to see what it looks like.&lt;/p&gt;

&lt;h3&gt;
  
  
  Readme
&lt;/h3&gt;

&lt;p&gt;Let's explore the Readme documentation.&lt;br&gt;
We will be visiting &lt;a href="https://docs.withpersona.com/reference/list-all-api-keys" rel="noopener noreferrer"&gt;persona&lt;/a&gt; documentation for this.&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%2F75e14qv6m4g5pntwz4b6.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%2F75e14qv6m4g5pntwz4b6.png" alt="image" width="800" height="486"&gt;&lt;/a&gt;&lt;br&gt;
The UI consists of a sidebar and a topbar. We can see the Topbar having &lt;strong&gt;OpenAPI Spec&lt;/strong&gt;, which is a standard format for defining RESTful APIs, allowing for clear documentation and interaction with the API. This also means the documentation is synced with the OpenAPI Definition, ensuring the details in the documentation are always upto-date.&lt;/p&gt;

&lt;p&gt;There is an &lt;strong&gt;API Changelog&lt;/strong&gt; as well, so each changes are tracked.&lt;br&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%2Fgmdv9vc60xhwjmni08yz.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%2Fgmdv9vc60xhwjmni08yz.png" alt="image" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Suppose we want to use the &lt;strong&gt;Create Authorization API&lt;/strong&gt;. I can either go to the OAuth section or the search bar to get the API.&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%2Fnd1nojnlolqh2wjdfpko.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%2Fnd1nojnlolqh2wjdfpko.png" alt="image" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To understand how the API works, I can utilize the "Try it" button to run the API myself and observe what the output looks like.&lt;/p&gt;

&lt;p&gt;For deeper understanding, we have sections like &lt;strong&gt;Form-Data, Headers, and Responses&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We can also observe an Alert, which informs the user about the prerequisites.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fern
&lt;/h3&gt;

&lt;p&gt;At last, we can check the Fern Documentation.&lt;br&gt;
We will be visiting &lt;a href="https://docs.vellum.ai/api-reference/overview/getting-started" rel="noopener noreferrer"&gt;Vellum&lt;/a&gt; documentation.&lt;/p&gt;

&lt;p&gt;We will try searching for &lt;strong&gt;Execute Prompt API&lt;/strong&gt;. This API can be similarly accessed via the search bar or the sidebar.&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%2Fcr4doraecsnhqrzrvq1o.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%2Fcr4doraecsnhqrzrvq1o.png" alt="image" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For understanding the API, we have a play button to try the APIs ourselves as well as some description about the Request and Response.&lt;/p&gt;

&lt;p&gt;Based on our learnings, we now know the features that these API Documentation platforms use. Let's go through it one by one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Key features that improve Developer Engagement
&lt;/h2&gt;

&lt;p&gt;Read the &lt;a href="https://journal.hexmos.com/doc-structure-principles/" rel="noopener noreferrer"&gt;remaining article&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>openapi</category>
      <category>documentation</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Faster, Easier Deployments: How We Simplified Our Infrastructure with Nomad in 15 Hours (Goodbye, Kubernetes!)</title>
      <dc:creator>Athreya aka Maneshwar</dc:creator>
      <pubDate>Sun, 11 Aug 2024 17:36:23 +0000</pubDate>
      <link>https://forem.com/hexmos/faster-easier-deployments-how-we-simplified-our-infrastructure-with-nomad-in-15-hours-goodbye-kubernetes-38oi</link>
      <guid>https://forem.com/hexmos/faster-easier-deployments-how-we-simplified-our-infrastructure-with-nomad-in-15-hours-goodbye-kubernetes-38oi</guid>
      <description>&lt;p&gt;We're a lean team of 10 managing a relatively simple infrastructure.&lt;/p&gt;

&lt;p&gt;Our setup includes a master Kube v1.24.4, two v1.25.1 nodes, and a self-hosted GitLab for version control.&lt;/p&gt;

&lt;p&gt;Our CI/CD pipeline relies on &lt;a href="https://docs.gitlab.com/runner/" rel="noopener noreferrer"&gt;GitLab runners&lt;/a&gt; to build images and deploy Docker containers using Kubernetes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kubernetes: The Goliath We Couldn't Tame
&lt;/h2&gt;

&lt;p&gt;For a while, our infrastructure was running smoothly with eight services deployed across our Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;However, a few months ago, we hit a snag.&lt;/p&gt;

&lt;p&gt;Nodes became unreachable, deployments stalled, and pods started crashing.&lt;/p&gt;

&lt;p&gt;This instability was diverting our energy into fixing things up instead of producing.&lt;/p&gt;

&lt;p&gt;As a temporary fix, we decided to host four of our deployments on a separate node using Docker.&lt;/p&gt;

&lt;p&gt;This provided some stability, but the underlying issue persisted – the nodes connection continued to go offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drowning in YAML: Our Kubernetes Nightmare
&lt;/h2&gt;

&lt;p&gt;Maintaining Kubernetes for our small team felt manageable at first.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://helm.sh/" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; templates streamlined our deployments, but downtime issues persisted.&lt;/p&gt;

&lt;p&gt;Maintaining Kubernetes for a small team like ours was becoming increasingly burdensome.&lt;/p&gt;

&lt;p&gt;The constant troubleshooting and firefighting were taking a toll on our productivity.&lt;/p&gt;

&lt;p&gt;It felt like we were spending more time managing the infrastructure than building our product.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/hashicorp/nomad" rel="noopener noreferrer"&gt;Nomad&lt;/a&gt;: The Oasis in Our Infrastructure Desert
&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%2Fol3evo49f7kiegxvn83f.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%2Fol3evo49f7kiegxvn83f.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
(Source: &lt;a href="https://www.hashicorp.com/solutions/modern-application-delivery" rel="noopener noreferrer"&gt;HashiCorp&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;After careful consideration, we decided to explore Nomad.&lt;/p&gt;

&lt;p&gt;Large organizations like &lt;a href="https://blog.cloudflare.com/how-we-use-hashicorp-nomad/" rel="noopener noreferrer"&gt;Cloudflare&lt;/a&gt; and &lt;a href="https://tech.trivago.com/post/2019-01-25-nomadourexperiencesandbestpractices" rel="noopener noreferrer"&gt;Trivago&lt;/a&gt; have been running Nomad for some time now.&lt;/p&gt;

&lt;p&gt;Their endorsement of Nomad's &lt;a href="https://developer.hashicorp.com/nomad/docs/nomad-vs-kubernetes#simplicity" rel="noopener noreferrer"&gt;simplicity&lt;/a&gt; and reduced operational overhead increased our interest.&lt;/p&gt;

&lt;p&gt;We were eager to explore how this platform could benefit our smaller team.&lt;/p&gt;

&lt;p&gt;What we wanted was something like a supercharged &lt;a href="https://github.com/Unitech/pm2" rel="noopener noreferrer"&gt;pm2&lt;/a&gt;, but multi-node with good support for both docker and binaries.&lt;/p&gt;

&lt;p&gt;Nomad seemed like the perfect fit, so we decided to give it a shot.&lt;/p&gt;

&lt;p&gt;Our next steps involve diving into Nomad's capabilities and potentially conducting a proof of concept to assess its suitability for our environment, before that let's understand the key difference between Kubernetes and Nomad.&lt;/p&gt;
&lt;h2&gt;
  
  
  Kubernetes vs. Nomad: A Clash of Titans
&lt;/h2&gt;

&lt;p&gt;Kubernetes is like a big, complex city. It’s got everything, but it can be hard to navigate and manage. It's is a collection of many, many services &amp;amp; tools working together.&lt;/p&gt;

&lt;p&gt;Nomad is more like a cozy town. It’s simpler, easier to get around, and still gets the job done. It's is a single binary, which you have to run &amp;amp; you're done&lt;/p&gt;

&lt;p&gt;Here's the breakdown:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Simplicity: Nomad wins. It's like comparing a bike to a car.

&lt;ul&gt;
&lt;li&gt;Nomad is simple to learn and use, making it accessible even for teams with limited experience in managing complex container orchestration systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Flexibility: Nomad can handle different types of workloads, while Kubernetes is mostly focused on containers.

&lt;ul&gt;
&lt;li&gt;It has the ability to schedule, deploy, and manage various workloads (virtualized, containerized, and standalone applications) types, including both Windows- and Linux-based containers.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Consistency: Nomad is the same everywhere, while Kubernetes can vary depending on how you set it up.

&lt;ul&gt;
&lt;li&gt;Setting up Kubernetes in production can be complex and resource-intensive. Kubernetes, while versatile, can introduce complexities due to its various deployment options (like minikube, kubeadm, and k3s). This can lead to differences in setup and management.&lt;/li&gt;
&lt;li&gt;Nomad stands out with its consistent deployment process. As a single binary, it works seamlessly across different environments, from development to production. This unified approach simplifies management and reduces potential issues.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Scaling: Both can handle big jobs, but Nomad seems to do it smoother.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;According to Kubernetes &lt;a href="https://kubernetes.io/docs/setup/best-practices/cluster-large/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, it supports clusters with up to 5,000 nodes and 300,000 total containers. However, as the environment grows, managing the system at scale can become increasingly challenging.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Nomad is built for scale. It effortlessly handles tens of thousands of nodes and can be spread across multiple regions without added complexity. Rigorous testing, including the 2 million &lt;a href="https://www.hashicorp.com/c2m" rel="noopener noreferrer"&gt;container challenge&lt;/a&gt;, has proven Nomad's ability to manage massive workloads efficiently.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, if you want something easy to manage and versatile, Nomad might be your jam.&lt;/p&gt;

&lt;p&gt;But if you need a full-featured platform and don't mind the complexity, Kubernetes could be the way to go.&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%2Fgd6slamx0zfduni1v512.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%2Fgd6slamx0zfduni1v512.jpg" alt="k8s-vs-nomad"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Image source: This image is extracted from a presentation included in the Internet Archive article "&lt;a href="https://www.hashicorp.com/resources/migrating-kubernetes-nomad-consul-doubling-pipeline-speed-gitlab-internet-archive" rel="noopener noreferrer"&gt;Migrating from Kubernetes to Nomad and Consul&lt;/a&gt;" which details the migration process.&lt;/p&gt;

&lt;p&gt;Nomad has a pretty solid UI for real-time insights.&lt;br&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%2Fpa332ktp0y9smn51dx9o.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%2Fpa332ktp0y9smn51dx9o.png" alt="image 348"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  From YAML to HCL: A Developer-Friendly Shift
&lt;/h2&gt;

&lt;p&gt;Thankfully, getting started with Nomad was a breeze.&lt;/p&gt;

&lt;p&gt;In Kubernetes, we define deployments via &lt;a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/" rel="noopener noreferrer"&gt;YAML&lt;/a&gt; files. Whereas Nomad leverages &lt;a href="https://github.com/hashicorp/hcl" rel="noopener noreferrer"&gt;HCL&lt;/a&gt; (HashiCorp Configuration Language).&lt;/p&gt;

&lt;p&gt;This language, built using &lt;a href="https://go.dev/" rel="noopener noreferrer"&gt;Go&lt;/a&gt;, proved to be surprisingly user-friendly for developers like ourselves.&lt;/p&gt;

&lt;p&gt;We were able to quickly grasp the syntax and start building deployments with minimal learning curve.&lt;/p&gt;
&lt;h2&gt;
  
  
  Nomad 101: Your Quick Start Guide
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Installing Nomad for Production
&lt;/h3&gt;

&lt;p&gt;I've set up Nomad with a single server and multiple clients.&lt;br&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%2Fy38916j7w5i0tt4s5gkq.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%2Fy38916j7w5i0tt4s5gkq.png" alt="Group 538"&gt;&lt;/a&gt;&lt;br&gt;
If you wish to deploy services on the server machine as well, you can register it as a client.&lt;br&gt;
This way, one node can act as a coordinator/leader while also deploying services, making efficient use of compute resources.&lt;/p&gt;

&lt;p&gt;The official HashiCorp &lt;a href="https://developer.hashicorp.com/nomad/tutorials/enterprise/production-deployment-guide-vm-with-consul#production-deployment-guide-vm-with-consul" rel="noopener noreferrer"&gt;installation&lt;/a&gt; instructions for Linux are clear and easy to follow.&lt;/p&gt;

&lt;p&gt;We found them to be much simpler than the sometimes cumbersome setup process experienced with Kubernetes years earlier.&lt;/p&gt;

&lt;p&gt;Note: To run your services that depend on Docker using Nomad, Docker must be installed on the clients.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Exposing Nomad Ports
&lt;/h3&gt;

&lt;p&gt;To ensure your Nomad servers and clients can communicate (join), we need to open specific ports on your network.&lt;/p&gt;
&lt;h4&gt;
  
  
  Create Security Group
&lt;/h4&gt;

&lt;p&gt;A security group acts as a firewall, controlling incoming and outgoing traffic for your instances.&lt;/p&gt;

&lt;p&gt;In our AWS setup, we'll create a security group to allow access to the following ports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Port &lt;code&gt;4648&lt;/code&gt;: Used for &lt;em&gt;&lt;a href="https://www.serf.io/" rel="noopener noreferrer"&gt;Serf communication&lt;/a&gt; between Nomad servers&lt;/em&gt;. Both a TCP and UDP listener will be exposed to this address.
This protocol is responsible for cluster membership and failure detection.&lt;/li&gt;
&lt;li&gt;Port &lt;code&gt;4647&lt;/code&gt;: Dedicated to &lt;em&gt;Nomad &lt;a href="https://developer.hashicorp.com/nomad/docs/configuration#rpc-1" rel="noopener noreferrer"&gt;RPC communication&lt;/a&gt; between nodes&lt;/em&gt;. The address used to advertise to Nomad clients for connecting to Nomad servers for RPC. This allows Nomad clients to connect to Nomad servers from behind a NAT gateway. This address muct be reachable by all Nomad client nodes. When set, the Nomad servers will use the advertise.serf address for RPC connections amongst themselves.
This is the primary channel for task scheduling and status updates.&lt;/li&gt;
&lt;li&gt;Port &lt;code&gt;4646&lt;/code&gt;: Enables &lt;em&gt;&lt;a href="https://developer.hashicorp.com/nomad/tutorials/transport-security/security-concepts#:~:text=for%20its%20traffic.-,HTTP,-%2D%20Used%20to%20communicate" rel="noopener noreferrer"&gt;HTTP&lt;/a&gt; API access for interacting between CLI Nomad agents&lt;/em&gt;. The address the HTTP server is bound to. The address to advertise for the HTTP interface.
&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%2F3g06ls9pkpkeapqpdjbm.png" alt="Group 534"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Attach Security Group to Server and Client Nodes
&lt;/h4&gt;

&lt;p&gt;We need to associate the created security group with both your Nomad server and client instances.&lt;/p&gt;

&lt;p&gt;By doing this, you allow incoming traffic on the specified ports, enabling communication within the Nomad cluster.&lt;br&gt;
EC2 -&amp;gt; Instance -&amp;gt; Actions -&amp;gt; Security -&amp;gt; Change Security Groups&lt;br&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%2Fbs7e9or74spr2dme2w4t.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%2Fbs7e9or74spr2dme2w4t.png" alt="Group 537"&gt;&lt;/a&gt;&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%2Fvrvffr6b0yf096a7ydcp.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%2Fvrvffr6b0yf096a7ydcp.png" alt="Group 535"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Joining Forces: Connecting Clients to Your Nomad Cluster
&lt;/h3&gt;

&lt;p&gt;Assuming Nomad is already installed on your client machines, follow these steps to join them to the Nomad server:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Edit &lt;code&gt;client.hcl&lt;/code&gt;&lt;/strong&gt;
Open the client.hcl file located at &lt;code&gt;/etc/nomad.d/client.hcl&lt;/code&gt; on each client machine.
Add the following configuration, replacing &lt;code&gt;152.21.12.71&lt;/code&gt; with the actual IP address of your Nomad server:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;client &lt;span class="o"&gt;{&lt;/span&gt;
  enabled &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true
  &lt;/span&gt;server_join &lt;span class="o"&gt;{&lt;/span&gt;
    retry_join &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"152.21.12.71:4647"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Restart Nomad&lt;/strong&gt;
To apply the configuration changes, restart the Nomad service on each client machine:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart nomad
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify Client Status&lt;/strong&gt;
Check the Nomad logs on each client to ensure they have joined the cluster successfully:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; nomad &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;span class="c"&gt;# or&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status nomad
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Look for messages indicating that the client has connected to the server.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node4 nomad[1441379]: &lt;span class="o"&gt;[&lt;/span&gt;INFO]  agent.joiner: starting retry &lt;span class="nb"&gt;join&lt;/span&gt;: &lt;span class="nv"&gt;servers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;152.21.12.71:4647
node4 nomad[1441379]: &lt;span class="o"&gt;[&lt;/span&gt;INFO]  agent.joiner: retry &lt;span class="nb"&gt;join &lt;/span&gt;completed: &lt;span class="nv"&gt;initial_servers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;agent_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;client
node4 nomad[1441379]: &lt;span class="o"&gt;[&lt;/span&gt;INFO]  client: node registration &lt;span class="nb"&gt;complete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Your Nomad clients will now join the cluster and start receiving job allocations from the server. 5. &lt;strong&gt;Listing the Nodes status&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nomad node status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fypz29gk41ap3vfof6t06.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%2Fypz29gk41ap3vfof6t06.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can view the clients and servers with their status from the dashboard, which is pretty great.&lt;br&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%2Fx5y0qodiwzoogj6vtnf1.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%2Fx5y0qodiwzoogj6vtnf1.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Deploying with Ease: Your First Nomad Job
&lt;/h3&gt;

&lt;p&gt;Before deploying a job, let's create a HCL configuration file to define the &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification" rel="noopener noreferrer"&gt;job's specifications&lt;/a&gt;, including where it runs, how many instances to launch, and resource requirements.&lt;/p&gt;
&lt;h4&gt;
  
  
  Generating an Example Job File
&lt;/h4&gt;

&lt;p&gt;Nomad offers a handy command to generate a basic HCL file as a starting point.&lt;br&gt;
&lt;code&gt;nomad init&lt;/code&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Example Job File (fw-parse/fw-parse.hcl)
&lt;/h4&gt;

&lt;p&gt;This example showcases a job that runs a Node.js backend service within a Docker container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://developer.hashicorp.com/nomad/docs/configuration#datacenter" rel="noopener noreferrer"&gt;Datacenters&lt;/a&gt;&lt;/strong&gt;:&lt;br&gt;
A datacenter in Nomad represents a region or availability zone where your jobs will run. It's essential to specify the datacenter for your job.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;job &lt;span class="s2"&gt;"fw-parse"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  datacenters &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dc1"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="c"&gt;# ... rest of the configuration&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, dc1 is the name of the datacenter. You can have multiple datacenters in your Nomad cluster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/group" rel="noopener noreferrer"&gt;Groups&lt;/a&gt;&lt;/strong&gt;:&lt;br&gt;
A group is a logical container for one or more tasks. You can use groups to organize your job and define constraints.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;job &lt;span class="s2"&gt;"fw-parse"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  datacenters &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dc1"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

  group &lt;span class="s2"&gt;"servers"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# Specifies the number of instances of this group that should be running.&lt;/span&gt;
    &lt;span class="c"&gt;# Use this to scale or parallelize your job.&lt;/span&gt;
    &lt;span class="c"&gt;# This can be omitted and it will default to 1.&lt;/span&gt;
    count &lt;span class="o"&gt;=&lt;/span&gt; 1

    network &lt;span class="o"&gt;{&lt;/span&gt;
      port &lt;span class="s2"&gt;"http"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        static &lt;span class="o"&gt;=&lt;/span&gt; 1337  &lt;span class="c"&gt;# Use the internal port your Docker container listens on&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;# ... task configuration&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="c"&gt;# ... rest of the configuration&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/task" rel="noopener noreferrer"&gt;Task&lt;/a&gt; Configuration: Docker Driver&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For our Node.js backend, we'll use the &lt;a href="https://developer.hashicorp.com/nomad/docs/drivers/docker" rel="noopener noreferrer"&gt;Docker driver&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;job &lt;span class="s2"&gt;"fw-parse"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  datacenters &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dc1"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

  group &lt;span class="s2"&gt;"servers"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    task &lt;span class="s2"&gt;"fw-parse"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      driver &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"docker"&lt;/span&gt;
      config &lt;span class="o"&gt;{&lt;/span&gt;
        image &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"gitlab.site:5050/fw-parse/main:latest"&lt;/span&gt;
        &lt;span class="c"&gt;# ... other config options&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The image attribute specifies the Docker image to use.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fw-parse/fw-parse.hcl&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;job &lt;span class="s2"&gt;"fw-parse"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;# Specifies the datacenter where this job should be run&lt;/span&gt;
  datacenters &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dc1"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

  group &lt;span class="s2"&gt;"servers"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# Specifies the number of instances of this group that should be running.&lt;/span&gt;
    &lt;span class="c"&gt;# Use this to scale or parallelize your job.&lt;/span&gt;
    &lt;span class="c"&gt;# This can be omitted and it will default to 1.&lt;/span&gt;
    count &lt;span class="o"&gt;=&lt;/span&gt; 1

    network &lt;span class="o"&gt;{&lt;/span&gt;
      port &lt;span class="s2"&gt;"http"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        static &lt;span class="o"&gt;=&lt;/span&gt; 1337  &lt;span class="c"&gt;# Use the internal port your Docker container listens on&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;# This particular task starts a simple web server within a Docker container&lt;/span&gt;
    task &lt;span class="s2"&gt;"fw-parse"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      driver &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"docker"&lt;/span&gt;

      config &lt;span class="o"&gt;{&lt;/span&gt;
        image &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"gitlab.hexmos.site:5050/backend/fw-parse/main:latest"&lt;/span&gt;
        auth &lt;span class="o"&gt;{&lt;/span&gt;
          username &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"gitlab@hexmos.site"&lt;/span&gt;
          password &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"password"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        ports &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;

      &lt;span class="c"&gt;# Specify the maximum resources required to run the task&lt;/span&gt;
      resources &lt;span class="o"&gt;{&lt;/span&gt;
        cpu     &lt;span class="o"&gt;=&lt;/span&gt; 500
        memory  &lt;span class="o"&gt;=&lt;/span&gt; 256
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;More about &lt;a href="https://developer.hashicorp.com/nomad/docs/drivers/docker#allocating-ports" rel="noopener noreferrer"&gt;ports&lt;/a&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your service is exposing multiple ports, you can configure Nomad to handle them by specifying each port in the network block and mapping them to the tasks. This is particularly useful when your application requires several different ports for various services, such as HTTP, HTTPS, and an another one.&lt;/p&gt;

&lt;p&gt;For example, if your service is exposing ports &lt;code&gt;1337&lt;/code&gt;, &lt;code&gt;1339&lt;/code&gt;, and &lt;code&gt;3000&lt;/code&gt;, you would define them in your Nomad job file like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;job &lt;span class="s2"&gt;"fw-parse"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  datacenters &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dc1"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

  group &lt;span class="s2"&gt;"servers"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    network &lt;span class="o"&gt;{&lt;/span&gt;
      port &lt;span class="s2"&gt;"http"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        static &lt;span class="o"&gt;=&lt;/span&gt; 1337  &lt;span class="c"&gt;# Use the internal port your Docker container listens on&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
      port &lt;span class="s2"&gt;"https"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        static &lt;span class="o"&gt;=&lt;/span&gt; 1339  &lt;span class="c"&gt;# Another port used by your container&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
      port &lt;span class="s2"&gt;"admin"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        static &lt;span class="o"&gt;=&lt;/span&gt; 3000  &lt;span class="c"&gt;# Port for an admin interface or additional service&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    task &lt;span class="s2"&gt;"fw-parse"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      driver &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"docker"&lt;/span&gt;

      config &lt;span class="o"&gt;{&lt;/span&gt;
        image &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"gitlab.hexmos.site:5050/backend/fw-parse/main:latest"&lt;/span&gt;
        auth &lt;span class="o"&gt;{&lt;/span&gt;
          username &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"gitlab@hexmos.site"&lt;/span&gt;
          password &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"password"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        ports &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;, &lt;span class="s2"&gt;"https"&lt;/span&gt;, &lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;  &lt;span class="c"&gt;# List all the ports that the container will use&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Running and Stopping Deployments
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Running a Deployment&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Nomad offers two ways to kickstart your deployment:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Command Line: Use the nomad job run  command.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nomad job run fw-parse/fwparse.hcl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Nomad UI: For a more visual experience, leverage Nomad's intuitive user interface.&lt;/li&gt;
&lt;/ol&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%2F1np0eio2t8n67r6ujz83.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%2F1np0eio2t8n67r6ujz83.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Stopping a Deployment
&lt;/h4&gt;

&lt;p&gt;When you need to halt a running deployment, simply execute the command nomad job stop  where  represents the name assigned to your deployment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nomad stop example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fb9zsotjsmfk5mrqokj2x.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%2Fb9zsotjsmfk5mrqokj2x.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Extras
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Format hcl files: &lt;a href="https://github.com/fatih/hclfmt" rel="noopener noreferrer"&gt;hclfmt&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Config generator: &lt;a href="https://nomcfg.mrkaran.dev/" rel="noopener noreferrer"&gt;nomcfg&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Guides for getting started: &lt;a href="https://developer.hashicorp.com/nomad/tutorials" rel="noopener noreferrer"&gt;Nomad tutorial&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Our Roadmap: Expanding Nomad's Potential
&lt;/h2&gt;

&lt;p&gt;We've recently embarked on our Nomad journey, setting up a foundational environment with a single server and three clients.&lt;/p&gt;

&lt;p&gt;After a week of running a basic Docker-based Node.js backend, we're pleased with the initial results.&lt;/p&gt;

&lt;p&gt;Our vision for Nomad extends far beyond this initial proof of concept.&lt;/p&gt;

&lt;p&gt;We're eager to integrate &lt;a href="https://www.hashicorp.com/resources/nomad-ci-cd-developer-workflows-and-integrations?ajs_aid=6b0556e2-4a03-429d-b44a-a06895e1a67f&amp;amp;product_intent=nomad" rel="noopener noreferrer"&gt;GitLab CI/CD&lt;/a&gt; for streamlined deployments and explore the use of &lt;a href="https://www.hashicorp.com/products/vault" rel="noopener noreferrer"&gt;HashiCorp Vault&lt;/a&gt; for robust secret management.&lt;/p&gt;

&lt;p&gt;Given our team's shared ownership model, Nomad's developer-centric approach aligns perfectly with our goals of rapid iteration and efficient deployment.&lt;/p&gt;

&lt;p&gt;We anticipate Nomad to enhance our operational efficiency and are excited to share our experiences and learnings as we progress.&lt;/p&gt;

&lt;p&gt;We're open to feedback and suggestions from the Nomad community as we continue to refine our setup.&lt;/p&gt;

&lt;p&gt;If you have any recommendations based on your experiences, we'd love to hear them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to dive deeper into the Kubernetes world? Check out our other blog posts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://journal.hexmos.com/kube-network/" rel="noopener noreferrer"&gt;Demystifying Kubernetes Port Forwarding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://journal.hexmos.com/kubernetes-ports-overview-covering-expose-nodeport-targetport/" rel="noopener noreferrer"&gt;6 Kubernetes Ports: A Definitive Look - Expose, NodePort, TargetPort, &amp;amp; More&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://journal.hexmos.com/spotting-kube-failures/" rel="noopener noreferrer"&gt;Spotting Silent Pod Failures in Kubernetes with Grafana&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://journal.hexmos.com/install-nomad-in-production/" rel="noopener noreferrer"&gt;Subscribe&lt;/a&gt; for a weekly dose of insights on development, IT, operations, design, leadership and more.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>devops</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>I spent the last 6 months building LiveAPI Proxy: Here are 10 HARD-EARNED Engineering Lessons you can use now</title>
      <dc:creator>Rijul Rajesh</dc:creator>
      <pubDate>Sun, 09 Jun 2024 16:15:13 +0000</pubDate>
      <link>https://forem.com/hexmos/i-spent-the-last-6-months-building-liveapi-proxy-here-are-10-hard-earned-engineering-lessons-you-can-use-now-1kc6</link>
      <guid>https://forem.com/hexmos/i-spent-the-last-6-months-building-liveapi-proxy-here-are-10-hard-earned-engineering-lessons-you-can-use-now-1kc6</guid>
      <description>&lt;h2&gt;
  
  
  How LiveAPI Taught me some important Lessons in engineering
&lt;/h2&gt;

&lt;p&gt;I have been working on a product named &lt;strong&gt;LiveAPI&lt;/strong&gt;. Let me just give an idea of what this product does.&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%2Fb04gon80u6pocm5d9nxx.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%2Fb04gon80u6pocm5d9nxx.png" width="800" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above API doc is a static one, users cant execute and change things by themselves.&lt;/p&gt;

&lt;p&gt;Static API docs like these often lose customer attention before the developers even try the APIs. &lt;br&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%2F23m29ri59x0wcl3jwgrs.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%2F23m29ri59x0wcl3jwgrs.png" width="785" height="733"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above API Doc uses LiveAPI, here developers can execute these APIs instantly right within their browser, so that developer attention can be captured within the first 30 seconds of their visit.&lt;/p&gt;

&lt;p&gt;LiveAPI uses a WASM Binary and a language core for executing the APIs. These things are already built up and we started testing this on some httpbin URLs, everything seemed fine&lt;/p&gt;

&lt;p&gt;When we tried doing a GET request to &lt;a href="http://www.google.com" rel="noopener noreferrer"&gt;www.google.com&lt;/a&gt;, it failed.&lt;br&gt;
We investigated further and found out that there was a &lt;strong&gt;CORS&lt;/strong&gt; error going on.&lt;/p&gt;

&lt;p&gt;CORS error prevents us from making requests from one site to another site.&lt;br&gt;
But this is a vital thing, because we are always requesting from one site(API docs) to another site(the target API url).&lt;/p&gt;

&lt;p&gt;So we thought for a while on this issue, and an idea popped up. &lt;strong&gt;How about we use proxyservers&lt;/strong&gt;? This is a potential solution to this problem and will get us back up and running. Let's see how proxy servers can be a useful approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning about Proxies: Engineering a Solution for CORS-Free browser requests
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is a proxy server?
&lt;/h3&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%2Flic0s3072k3vxx60ta7w.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%2Flic0s3072k3vxx60ta7w.png" alt="alt text" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Consider this example.&lt;br&gt;
Here you can see two people, Alice and Bob. In the middle there is a proxy.&lt;/p&gt;

&lt;p&gt;Alice asked the proxy to forward a message to him, Bob also does the same.&lt;br&gt;
The proxy acts as the middleman here passing information between these two people.&lt;/p&gt;

&lt;p&gt;This is how proxy servers work.&lt;br&gt;
&lt;strong&gt;A proxyserver acts as a middleman between a client and a server&lt;/strong&gt;, We have 3 things: Client Requests, Proxy Server and Responses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Client Request:&lt;/strong&gt; When you send a request to a website. Instead of the website receiving it first, the proxy server receives it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proxy Server:&lt;/strong&gt; The proxy server then forwards your request to the actual website. It’s like a middleman that handles the communication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt; The website responds to the proxy server, which then forwards the response back to you.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Proxies aid with solving the CORS problem
&lt;/h3&gt;

&lt;p&gt;The proxy server makes the request to the target API on behalf of our LiveAPI tool. Since the browser sees the request coming from the proxy server rather than from our site, it bypasses the CORS restrictions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Figuring out how to build a proxy server: The approach I took
&lt;/h3&gt;

&lt;p&gt;Since we got an idea of what the solution looks like, We were thinking about what technologies should we use.&lt;/p&gt;

&lt;p&gt;For our case, we already had an &lt;strong&gt;apache2 server&lt;/strong&gt; up and running, and since Apache is a widely used server with a lot of module support, we felt it was the better choice for building our proxy server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting the Solution into Action: Building an Apache2 Proxy and Getting LiveAPI working
&lt;/h2&gt;

&lt;p&gt;Continue reading &lt;a href="https://journal.hexmos.com/liveapi-engineering-lessons/" rel="noopener noreferrer"&gt;the article&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>proxy</category>
      <category>api</category>
      <category>apache2</category>
    </item>
    <item>
      <title>Bot Invasion To Automated Defense: My Journey With ML Deployment</title>
      <dc:creator>Athreya aka Maneshwar</dc:creator>
      <pubDate>Sun, 12 May 2024 15:25:15 +0000</pubDate>
      <link>https://forem.com/hexmos/bot-invasion-to-automated-defense-my-journey-with-ml-deployment-11f2</link>
      <guid>https://forem.com/hexmos/bot-invasion-to-automated-defense-my-journey-with-ml-deployment-11f2</guid>
      <description>&lt;p&gt;Remember the &lt;a href="https://dev.to/hexmos/bots-invaded-my-newsletter-heres-how-i-fought-back-with-ml-1f65"&gt;bot invasion&lt;/a&gt; of my newsletter? I fought back with ML, but building a fancy bot zapper wasn't in the budget. Instead, I deployed a cost-effective model to keep those pesky bots out. Here is how I did it!&lt;/p&gt;

&lt;p&gt;In my previous article, "&lt;a href="https://journal.hexmos.com/bots-invaded-my-newsletter-heres-how-i-fought-back-with-ml/?src=hn" rel="noopener noreferrer"&gt;Bots Invaded My Newsletter. Here's How I Fought Back with ML&lt;/a&gt;" I shared my experience building a bot-signup detector using machine learning to tackle a surge of unwanted bot signups on my newsletter.&lt;/p&gt;

&lt;p&gt;While it wasn't the most sophisticated solution, it was a valuable learning experience.&lt;/p&gt;

&lt;p&gt;Few people on &lt;a href="https://www.reddit.com/r/learnmachinelearning/comments/1bscd2c/bots_invaded_my_newsletter_heres_how_i_fought" rel="noopener noreferrer"&gt;Reddit&lt;/a&gt; were curious about deploying their own models.&lt;/p&gt;

&lt;p&gt;Since I'm still on this journey of learning ML deployment, I wanted to share what I've learned so far about finding an easy and cost-effective approach or even finding more feasible approach from you guys.&lt;/p&gt;

&lt;p&gt;Even though I'm just starting out, hopefully this can be helpful for others who are in the same boat!&lt;/p&gt;

&lt;h2&gt;
  
  
  Backstory Of Identifying The Enemy
&lt;/h2&gt;

&lt;p&gt;I had a &lt;a href="https://journal.hexmos.com/bots-invaded-my-newsletter-heres-how-i-fought-back-with-ml/#the-bot-invasion" rel="noopener noreferrer"&gt;&lt;strong&gt;bot invasion&lt;/strong&gt;&lt;/a&gt; to my &lt;a href="https://hexmos.com/365reasons?ref=journal.hexmos.com" rel="noopener noreferrer"&gt;newsletter&lt;/a&gt;, I knew how the bots would look(name, email) like.&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%2Fdium04aa7r04grn0l1vv.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%2Fdium04aa7r04grn0l1vv.png" alt="Bots" width="800" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Their data were stored in DB collected from signups, I used that for the &lt;a href="https://journal.hexmos.com/bots-invaded-my-newsletter-heres-how-i-fought-back-with-ml/#how-did-i-create-the-dataset" rel="noopener noreferrer"&gt;dataset&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For my weapon of choice, I picked a &lt;a href="https://journal.hexmos.com/bots-invaded-my-newsletter-heres-how-i-fought-back-with-ml/#the-model-bert-plus-a-bit-more" rel="noopener noreferrer"&gt;BERT&lt;/a&gt; transformer.&lt;/p&gt;

&lt;p&gt;I &lt;a href="https://journal.hexmos.com/bots-invaded-my-newsletter-heres-how-i-fought-back-with-ml/#what-are-the-numbers-for-training-and-testing" rel="noopener noreferrer"&gt;trained&lt;/a&gt; it with a bunch of emails (144 to be exact) to learn the difference between human and bot names and emails; and it was &lt;a href="https://journal.hexmos.com/bots-invaded-my-newsletter-heres-how-i-fought-back-with-ml/#is-it-working" rel="noopener noreferrer"&gt;working&lt;/a&gt; most of the time.&lt;/p&gt;

&lt;p&gt;So it was all ready, just needed to deploy it live and use it in the signup process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing My Weapon To Fire Against The Enemy
&lt;/h2&gt;

&lt;p&gt;Now that I had my trusty bot detector trained, it was time to figure out how to load it into the battlefield (deployment).&lt;/p&gt;

&lt;p&gt;Here's what I learned about deploying a machine learning model in a simple and cost-effective way.&lt;/p&gt;

&lt;p&gt;Integrating the bot detector with my newsletter signup process was an exciting adventure.&lt;/p&gt;

&lt;p&gt;It felt like discovering a whole new system, just like writing the final line of code that unlocks a new functionality!&lt;/p&gt;

&lt;p&gt;Previously I had a &lt;a href="https://en.wikipedia.org/wiki/Transformer_(deep_learning_architecture)?ref=journal.hexmos.com" rel="noopener noreferrer"&gt;transformer&lt;/a&gt; which would take the name and email as the input and provide a boolean value indicating if the input signup is bot or not.&lt;/p&gt;

&lt;p&gt;For deployment we didn't wanna spin up a new VM and a server to keep listining to the calls or at the same time didn't want our existing services to have this as a piece of them. So went in for AWS &lt;a href="https://docs.aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;Lambda&lt;/a&gt; server less deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can't Use Lambda Straight Away
&lt;/h3&gt;

&lt;p&gt;When I was trying to deploy the transformer model I understood, I cannot use Lambda normally. Because there will be installations like &lt;a href="https://pypi.org/project/transformers/" rel="noopener noreferrer"&gt;transformers&lt;/a&gt;, &lt;a href="https://scikit-learn.org/stable/?ref=journal.hexmos.com" rel="noopener noreferrer"&gt;scikit-learn&lt;/a&gt;, and many more.&lt;/p&gt;

&lt;p&gt;So the alternate solution was to use &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/images-create.html" rel="noopener noreferrer"&gt;Lambda using Docker&lt;/a&gt; container images.&lt;/p&gt;

&lt;p&gt;This was a good exploration, basically it's a Docker image which you create by installing all the pre deps whatever is necessary for you and host it as a Lambda function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker Container Images (But Too Big!)
&lt;/h3&gt;

&lt;p&gt;I loaded up my previously built transformer of 419 MB &lt;code&gt;.bin&lt;/code&gt; file and installed transformers, scikit-learn and may other packages, by the time I built the image it was 9.2 GB!&lt;/p&gt;

&lt;p&gt;Clearly that was a horrible solution for such a basic problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logistic Regression - Smaller and Faster
&lt;/h3&gt;

&lt;p&gt;I moved on to &lt;a href="https://en.wikipedia.org/wiki/Logistic_regression" rel="noopener noreferrer"&gt;Logistic Regression&lt;/a&gt;, which took less time to train and prepare the model as compared to the transformer and crazy thing was the binary is 27 KB :D&lt;/p&gt;

&lt;p&gt;I went on with adding deps, logic and voila 820 MB Docker image.&lt;/p&gt;

&lt;p&gt;So I went ahead and pushed the Docker image to &lt;a href="https://aws.amazon.com/ecr/" rel="noopener noreferrer"&gt;Elastic Container Registry&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;ECR is like &lt;a href="https://hub.docker.com/" rel="noopener noreferrer"&gt;Docker hub&lt;/a&gt; where I can store the Docker image I build.&lt;/p&gt;

&lt;p&gt;Then created a Lambda function which uses the docker image from the ECR repo I created earlier, so the cannon was prepared with the load and powder, just had to figure out the firing mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  Firing The Weapon
&lt;/h2&gt;

&lt;p&gt;The initial plan was to trigger the Lambda function directly using the &lt;code&gt;AWS CLI&lt;/code&gt; or &lt;code&gt;Boto3&lt;/code&gt; library.&lt;/p&gt;

&lt;p&gt;However, I needed a more user-friendly way to activate the bot detector from frontend.&lt;/p&gt;

&lt;p&gt;This led me to explore API Gateway.&lt;/p&gt;

&lt;p&gt;It's a good service that allows you to create a public endpoint (like a trigger point) that accepts requests and forwards them to your Lambda function behind the scenes.&lt;/p&gt;

&lt;p&gt;This was exactly what I needed – a way to invoke the Lambda function using a simple API call.&lt;/p&gt;

&lt;p&gt;Integrating the API Gateway with my signup form wasn't completely smooth sailing.&lt;/p&gt;

&lt;p&gt;I encountered some challenges mapping the data received by the API Gateway to the format expected by the Lambda function.&lt;/p&gt;

&lt;p&gt;Luckily, &lt;a href="https://aws.amazon.com/cloudwatch/" rel="noopener noreferrer"&gt;CloudWatch&lt;/a&gt; logs came to the rescue.&lt;/p&gt;

&lt;p&gt;With its detailed logs, I could easily debug the issue and get everything working seamlessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Killing The Enemy
&lt;/h2&gt;

&lt;p&gt;Now, whenever someone signs up for my newsletter, the API in my frontend form automatically triggers the Lambda function. Here's the magic that happens behind the scenes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The signup data is sent to the Lambda function.&lt;/li&gt;
&lt;li&gt;The function analyzes the data using the trained model to identify potential bots.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If a bot is detected, the function automatically blocks the subscriber using Listmonk's built-in &lt;a href="https://listmonk.app/docs/apis/subscribers/#put-apisubscriberssubscriber_idblocklist" rel="noopener noreferrer"&gt;Block API&lt;/a&gt;.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/botsignups.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/botsignups.png" alt="Alt text" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, the function sends a notification to my Discord channel, keeping me informed about signup activity (including any blocked bots).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&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%2F4mbiq65ho7c9iz5uukih.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%2F4mbiq65ho7c9iz5uukih.png" alt="Not bot" width="627" height="97"&gt;&lt;/a&gt;&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%2Fhuzc020dc332g57z4i7c.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%2Fhuzc020dc332g57z4i7c.png" alt="bot" width="800" height="85"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this system in place, I've successfully automated bot detection and eliminated the need for manual intervention.&lt;/p&gt;

&lt;p&gt;This feels like a victory in the fight against newsletter bot signups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Continue reading &lt;a href="https://journal.hexmos.com/from-idea-to-deployment-deploy-ml-model-aws-lambda-apigateway/#how-to-setup-a-bot-detector-for-yourself" rel="noopener noreferrer"&gt;How to setup a bot detector for yourself&lt;/a&gt; here.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This journey of deploying a machine learning model to fight newsletter bots has been a valuable learning experience.&lt;/p&gt;

&lt;p&gt;In my previous article, "&lt;a href="https://journal.hexmos.com/bots-invaded-my-newsletter-heres-how-i-fought-back-with-ml/" rel="noopener noreferrer"&gt;Bots Invaded My Newsletter. Here's How I Fought Back with ML ⚔️&lt;/a&gt;" I covered building the bot detector model.&lt;/p&gt;

&lt;p&gt;Now, we've explored the deployment side – a crucial step for putting your model to practical use.&lt;/p&gt;

&lt;p&gt;Here are some resources to help you get started on your own AI/ML adventure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building a Logistic Regression Model (ipynb file): &lt;a href="https://github.com/lovestaco/bot-spam-signup-to-newsletter" rel="noopener noreferrer"&gt;Logistic_regression.ipynb&lt;/a&gt; (This file demonstrates how I built the simpler and more efficient logistic regression model.)&lt;/li&gt;
&lt;li&gt;Lightweight Model File (23kb): &lt;a href="https://github.com/lovestaco/bot_detect_lambda/blob/main/dt_model_file.pkl" rel="noopener noreferrer"&gt;dt_model_file.pkl&lt;/a&gt; (Feel free to download and use this pre-trained model for basic bot detection in your own newsletter signup process.)&lt;/li&gt;
&lt;li&gt;Lambda Function Code Repository: &lt;a href="https://github.com/lovestaco/bot_detect_lambda" rel="noopener noreferrer"&gt;bot_detect_lambda&lt;/a&gt; (This repository contains the code for integrating the bot detection model with AWS Lambda for a serverless deployment.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Spread the Knowledge!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Share this blog post with your friends who are interested in getting started with AI and machine learning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to learn more or connect with me?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Reddit: &lt;a href="https://www.reddit.com/user/athreyaaaa" rel="noopener noreferrer"&gt;athreyaaaa&lt;/a&gt;&lt;br&gt;
LinkedIn: &lt;a href="https://www.linkedin.com/in/maneshwar-athreya" rel="noopener noreferrer"&gt;maneshwar-athreya&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__1002302"&gt;
    &lt;a href="/lovestaco" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&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%2Fuser%2Fprofile_image%2F1002302%2F5233b7df-6ee3-46b2-b8d7-1fafe103e8a3.jpg" alt="lovestaco image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/lovestaco"&gt;Athreya aka Maneshwar&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/lovestaco"&gt;Software Dev | Technical Writer | 400k+ Reads | Learning, building, improving, writing :)&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>machinelearning</category>
      <category>ai</category>
      <category>aws</category>
    </item>
    <item>
      <title>Credits got depleted and can't create AI images anymore? How to run your own image generator for free</title>
      <dc:creator>Rijul Rajesh</dc:creator>
      <pubDate>Sun, 07 Apr 2024 14:32:04 +0000</pubDate>
      <link>https://forem.com/hexmos/credits-got-depleted-and-cant-create-ai-images-anymore-how-to-run-your-own-image-generator-for-free-4a0e</link>
      <guid>https://forem.com/hexmos/credits-got-depleted-and-cant-create-ai-images-anymore-how-to-run-your-own-image-generator-for-free-4a0e</guid>
      <description>&lt;h3&gt;
  
  
  Struggles of Running Image Generators: Limited Use and Frustration 
&lt;/h3&gt;

&lt;p&gt;New innovations like AI image generators are gathering popularity. With tools like &lt;a href="https://www.midjourney.com/home" rel="noopener noreferrer"&gt;midjourney&lt;/a&gt; and &lt;a href="https://openai.com/dall-e-3" rel="noopener noreferrer"&gt;DALL-E 3&lt;/a&gt;, people can generate amazing images just with their imagination.&lt;/p&gt;

&lt;p&gt;The one thing common in all of the tools is that there is &lt;strong&gt;pricing involved&lt;/strong&gt;, So if you plan on &lt;strong&gt;generating images for free&lt;/strong&gt;, then you will be limited either by a &lt;strong&gt;free trial&lt;/strong&gt; or a &lt;strong&gt;limited credit system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, if you are a designer and you want to create image assets, mockups, or design ideas, there would be a lot of iterations involved, which is expensive.&lt;/p&gt;

&lt;p&gt;Sooner or later, after some usage, &lt;strong&gt;your credits will become depleted&lt;/strong&gt; and you will &lt;strong&gt;no longer be able to generate images&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What technology does Image Generation use?
&lt;/h3&gt;

&lt;p&gt;The technology that these image generators use is called &lt;a href="https://en.wikipedia.org/wiki/Stable_Diffusion" rel="noopener noreferrer"&gt;Stable Diffusion&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Stable diffusion &lt;strong&gt;requires a lot of resources&lt;/strong&gt;. Making it expensive to be given to everyone for free.&lt;/p&gt;

&lt;p&gt;So I thought, Why not run stable diffusion using my own resources? That way, I won't need to deal with payments, and I can create as many images as I want.&lt;/p&gt;

&lt;p&gt;So I will be demonstrating by the end of the article how I was able to run AI image generators on my own for &lt;strong&gt;absolutely free&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://journal-wa6509js.s3.ap-south-1.amazonaws.com/90d6fdad4b5f77e98d1bcdf413a53f4f803b41851e155b023455c72a80a4fa4e.png" rel="noopener noreferrer"&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%2Fbh3ewarkarlkzqvd2nf2.png" alt="Demo Image" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you can see that I have given a simple prompt, and I got the image generated in high quality.&lt;/p&gt;

&lt;p&gt;Let's see how the stable diffusion technology works, so we can start running it on our own.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the Tech Behind AI Image Generators Works
&lt;/h3&gt;

&lt;p&gt;To learn about how AI image generation works, we need to know about &lt;strong&gt;Stable Diffusion&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
We can think of stable diffusion like the real diffusion we have learned in school.&lt;/p&gt;

&lt;p&gt;There will be a &lt;strong&gt;clear beaker of water&lt;/strong&gt;, we will add a few drops of dye, The dye diffuses throughout the liquid, until it reaches a state of &lt;strong&gt;equilibrium.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://journal-wa6509js.s3.ap-south-1.amazonaws.com/60b78a48c83c0b6d4951f96759a56b05c45f0e7af4f5a2928dedc67cb9d43721.png" rel="noopener noreferrer"&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%2Fri7gaxztihuxk0pp2nrx.png" alt="Diffusion image" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let's apply the same concept to real stable diffusion.&lt;/p&gt;

&lt;p&gt;For training a stable diffusion model, we will start with a process called &lt;strong&gt;Forward diffusion&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://journal-wa6509js.s3.ap-south-1.amazonaws.com/f893aa44072c93e9b19a5585623e017dd5cb6a22c152e98528bf97113b8f6bd5.png" rel="noopener noreferrer"&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%2Fsufjecnbvzsam7qybfnv.png" alt="Forward Diffusion" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In forward diffusion, we take an image and add noise to it.&lt;br&gt;&lt;br&gt;
For those who don't know, think of noise like the static you see when the TV gets disconnected.&lt;/p&gt;

&lt;p&gt;The type of noise we are adding here is called &lt;strong&gt;gaussian noise&lt;/strong&gt;. This process is done multiple times, so there will be multiple layers of noise.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://journal-wa6509js.s3.ap-south-1.amazonaws.com/cfe18a55b7cf7e863489ee414915fea47f55d11f5864ee06f1069a4df9772992.png" rel="noopener noreferrer"&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%2Fdld2pou4pgdzux1l826x.png" alt="Reverse Diffusion" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After performing forward diffusion, &lt;strong&gt;Reverse Diffusion&lt;/strong&gt; is done. This involves reversing the gaussian noise until we get the original image.&lt;/p&gt;

&lt;p&gt;The model gradually starts learning how to predict images from noise.&lt;/p&gt;

&lt;p&gt;Similarly, forward and reverse diffusion is done on millions of images to properly train the model.&lt;/p&gt;

&lt;p&gt;After the training is done, we can make a random noise, and the model will predict the image.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://journal-wa6509js.s3.ap-south-1.amazonaws.com/74f8f5536987ef97f34c1d65d1c81f05d64130ceeef1ee359052c4454872c2db.png" rel="noopener noreferrer"&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%2Fzfu19y3xg630uotb07sk.png" alt="Noise guessing" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We may have a doubt: &lt;strong&gt;How is the model able to generate images from text prompts?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Images used for training have an alt text associated with them, which describes what the image is about.&lt;br&gt;&lt;br&gt;
This way, each image is linked to a text, and the model gradually finds the relationship between the text and the images.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://journal-wa6509js.s3.ap-south-1.amazonaws.com/ba2956cf7ed3e9b0c10568becccfcfaa7dad7cdd9ac16a64fe7e4486b9b7b604.png" rel="noopener noreferrer"&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%2Fx7fue6coeqkf35pnud0m.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is how stable diffusion models work in a simple way. Now let's get the stable diffusion running on your own.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's get stable diffusion running
&lt;/h3&gt;

&lt;p&gt;Continue reading the &lt;a href="https://journal.hexmos.com/run-your-image-generator/" rel="noopener noreferrer"&gt;rest of the article&lt;/a&gt; to see how we can get stable diffusion running on your machine!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aiart</category>
      <category>stablediffusion</category>
      <category>lora</category>
    </item>
    <item>
      <title>Bots Invaded My Newsletter. Here's How I Fought Back with ML ⚔️ 🤖</title>
      <dc:creator>Athreya aka Maneshwar</dc:creator>
      <pubDate>Sun, 31 Mar 2024 15:36:20 +0000</pubDate>
      <link>https://forem.com/hexmos/bots-invaded-my-newsletter-heres-how-i-fought-back-with-ml-1f65</link>
      <guid>https://forem.com/hexmos/bots-invaded-my-newsletter-heres-how-i-fought-back-with-ml-1f65</guid>
      <description>&lt;p&gt;My newsletter was overrun by bots! I decided to try a machine-learning solution. It was my first ML experiment and I learned a lot. Want to know how I built a bot detector and gained some ML skills along the way?&lt;/p&gt;

&lt;h2&gt;
  
  
  The bot invasion
&lt;/h2&gt;

&lt;p&gt;I have a free newsletter that encourages you to &lt;a href="https://hexmos.com/365reasons" rel="noopener noreferrer"&gt;read daily&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;There are 100+ subscribers, and recently a lot of bots have signed up too.&lt;/p&gt;

&lt;p&gt;Bots are signing up to market their own product, newsletters, etc.&lt;br&gt;
They usually have a link in the name field and the message that they want to convey.&lt;/p&gt;

&lt;p&gt;Ex: &lt;br&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%2Fg7d6ux838yuj14yyrq5f.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%2Fg7d6ux838yuj14yyrq5f.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Email&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="mailto:watcher2112@ecocryptolab.com"&gt;watcher2112@ecocryptolab.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;🔶 Withdrawing 32 911 Dollars. Gо tо withdrаwаl &amp;gt;&amp;gt;&amp;gt; &lt;a href="https://forms.yandex.com/cloud/65e6228102848f1a71edd8c9?hs=0cebe66d8b7ba4d5f0159e88dd472e8b&amp;amp;" rel="noopener noreferrer"&gt;https://forms.yandex.com/cloud/65e6228102848f1a71edd8c9?hs=0cebe66d8b7ba4d5f0159e88dd472e8b&amp;amp;&lt;/a&gt; 🔶&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These spammy signups aren't just annoying, they're a real headache! &lt;/p&gt;

&lt;p&gt;I was tired of manually blocking bot emails and worrying about how they might hurt my email reputation. &lt;/p&gt;

&lt;p&gt;I know I have numerous options to filter out the bot signups by embedding traditional methods like &lt;a href="https://www.cloudflare.com/en-gb/learning/bots/how-captchas-work/" rel="noopener noreferrer"&gt;CAPTCHA&lt;/a&gt;, &lt;a href="https://www.campaignmonitor.com/resources/glossary/double-opt-in/" rel="noopener noreferrer"&gt;Double Opt-in&lt;/a&gt;, &lt;a href="https://andrewwoods.net/blog/2018/name-validation-regex/" rel="noopener noreferrer"&gt;Regex patterns&lt;/a&gt;, or &lt;a href="https://stackoverflow.com/a/36227377/13791562" rel="noopener noreferrer"&gt;Honeypot Fields&lt;/a&gt;  in the form.&lt;/p&gt;

&lt;p&gt;At the same time, I also had a feeling like I was not trying to adapt to the newer technology, particularly the Machine Learning field, and wanted to get started but had no clue where to begin with.&lt;/p&gt;

&lt;p&gt;Then one of my mentors, &lt;a href="https://www.linkedin.com/in/shrijith-venkatramana-32741b2b0" rel="noopener noreferrer"&gt;Shrijith&lt;/a&gt; suggested why not try creating a solution for the bot signup problem using ML.&lt;/p&gt;

&lt;p&gt;I felt this was the right experiment I could begin with to learn ML.&lt;/p&gt;

&lt;p&gt;And so, I am here with my first machine learning experiment!&lt;/p&gt;
&lt;h2&gt;
  
  
  What should I expect from the model?
&lt;/h2&gt;

&lt;p&gt;Picture this: You've built a website with a newsletter signup form. You want to make sure your subscribers are real people, not automated bots. &lt;/p&gt;

&lt;p&gt;So, you implement a bot detection system. But what does it mean when someone tells you their system is "95% accurate"?&lt;/p&gt;

&lt;p&gt;Let me break it down:&lt;/p&gt;
&lt;h3&gt;
  
  
  Catching true bots
&lt;/h3&gt;

&lt;p&gt;Imagine 100 signups are actually bots. &lt;/p&gt;

&lt;p&gt;A 95% sensitive system should correctly identify 95 of them as bots. &lt;/p&gt;

&lt;p&gt;5 bots might slip through the cracks and be mistaken for humans (false negatives), which is okay and not a big deal.&lt;/p&gt;
&lt;h3&gt;
  
  
  Not mistaking humans
&lt;/h3&gt;

&lt;p&gt;Now, imagine 100 signups are from real humans.  &lt;/p&gt;

&lt;p&gt;A 95% specific system should accurately recognize 95 of these as humans. &lt;/p&gt;

&lt;p&gt;However, 5 people could be mistakenly labeled as bots (false positives), this is very bad as the human  is ignored, which is a loss of potential business lead(in general injustice).&lt;/p&gt;
&lt;h3&gt;
  
  
  The formulas
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sensitivity&lt;/strong&gt; = True Bots Detected / (True Bots Detected + Bots Missed)&lt;br&gt;
The system's ability to find true bots.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Specificity&lt;/strong&gt; = True Humans Detected / (True Humans Detected + Humans Mistaken for Bots)&lt;br&gt;
The system's ability to avoid mislabeling real people.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accuracy&lt;/strong&gt; = (True Bots Detected + True Humans Detected) / (Total Signups)&lt;br&gt;
Overall correctness, but it can be misleading if your dataset has way more of one type (bots or humans).&lt;/p&gt;

&lt;p&gt;If all three are 1.0 then congrats you have the perfect model. &lt;/p&gt;
&lt;h2&gt;
  
  
  One big mental mistake
&lt;/h2&gt;

&lt;p&gt;I used to underestimate the &lt;em&gt;power of data&lt;/em&gt; when training machine learning models. &lt;br&gt;
  I assumed that algorithms would simply "figure it out" no matter what I fed them. &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%2Fgiphy.com%2Fgifs%2Fufc-mma-3ohBVaelMOlKmNsLwk" 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%2Fgiphy.com%2Fgifs%2Fufc-mma-3ohBVaelMOlKmNsLwk"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With a small dataset of 103 signups (only 12 bots!), I threw it at &lt;a href="https://en.wikipedia.org/wiki/Decision_tree_learning" rel="noopener noreferrer"&gt;Decision Trees&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Logistic_regression" rel="noopener noreferrer"&gt;Logistic Regression&lt;/a&gt;, and &lt;a href="https://en.wikipedia.org/wiki/Random_forest" rel="noopener noreferrer"&gt;Random Forest&lt;/a&gt; models. &lt;/p&gt;

&lt;p&gt;I got an initial &lt;strong&gt;accuracy&lt;/strong&gt; of &lt;strong&gt;77%&lt;/strong&gt;, but that was a classic &lt;a href="https://en.wikipedia.org/wiki/Overfitting" rel="noopener noreferrer"&gt;overfitting&lt;/a&gt; trap. &lt;br&gt;
My models were just memorizing the training data, useless for real-world scenarios.&lt;/p&gt;

&lt;p&gt;Frustrated, I jumped to &lt;a href="https://en.wikipedia.org/wiki/Transformer_(deep_learning_architecture)" rel="noopener noreferrer"&gt;transformers&lt;/a&gt;, thinking the solution lay in fancy algorithms.&lt;br&gt;&lt;br&gt;
   I got a slight boost to &lt;strong&gt;87.4%&lt;/strong&gt;, which was a relief but still left much to be desired. &lt;/p&gt;

&lt;p&gt;To hit that &lt;strong&gt;90%&lt;/strong&gt; target,  I needed to debug. Using a &lt;a href="https://en.wikipedia.org/wiki/Confusion_matrix" rel="noopener noreferrer"&gt;confusion matrix&lt;/a&gt;,&lt;br&gt;&lt;br&gt;
   I finally saw the light:  &lt;strong&gt;it was the data, not the models&lt;/strong&gt;, holding me back.&lt;/p&gt;

&lt;p&gt;I used &lt;a href="https://imbalanced-learn.org/stable/references/generated/imblearn.over_sampling.SMOTE.html" rel="noopener noreferrer"&gt;SMOTE&lt;/a&gt; and simply balanced my dataset with equal numbers of bot and human signups, i.e 90 Human and 90 Bots then my accuracy shot up to &lt;strong&gt;94%&lt;/strong&gt;! &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%2Fgiphy.com%2Fgifs%2Fbreaking-bad-aaron-paul-yeah-science-QC7UQbxq89MnL9r6AN" 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%2Fgiphy.com%2Fgifs%2Fbreaking-bad-aaron-paul-yeah-science-QC7UQbxq89MnL9r6AN"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Long story short: How I got to the 100% accuracy bot detector
&lt;/h2&gt;

&lt;p&gt;Note: my training data is 180 rows&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Preparation
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Imports for models and packages&lt;/li&gt;
&lt;li&gt;Extracting data from my newsletter database to CSV.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  2. Creating the Dataset
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;I cannot input the database data directly for the &lt;a href="https://huggingface.co/google-bert/bert-base-cased" rel="noopener noreferrer"&gt;BERT&lt;/a&gt; to understand.&lt;/li&gt;
&lt;li&gt;I need to use a &lt;a href="https://huggingface.co/docs/transformers/v4.39.2/en/model_doc/bert#transformers.BertTokenizer" rel="noopener noreferrer"&gt;tokenizer&lt;/a&gt; to break the text into tokens (suitable units for BERT).
  Created a class(&lt;a href="https://colab.research.google.com/drive/1ZZIa5NG4tLHemsGOdWF5jl-ree8JtOqK#scrollTo=78kRG9Qkzi3W&amp;amp;line=4&amp;amp;uniqifier=1" rel="noopener noreferrer"&gt;&lt;code&gt;NewsletterCollectionDataset&lt;/code&gt;&lt;/a&gt;) to do the above things.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  3. Splitting data and loading
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;I split the data into three sets

&lt;ul&gt;
&lt;li&gt;training (to teach the model) 144 rows, &lt;/li&gt;
&lt;li&gt;validation (to check progress during training) 18 rows, and &lt;/li&gt;
&lt;li&gt;testing (for final evaluation) 18 rows.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Then a function(&lt;a href="https://colab.research.google.com/drive/1ZZIa5NG4tLHemsGOdWF5jl-ree8JtOqK#scrollTo=0RK3hJDCzS73&amp;amp;line=7&amp;amp;uniqifier=1" rel="noopener noreferrer"&gt;&lt;code&gt;create_data_loader&lt;/code&gt;&lt;/a&gt;) turns each of those data splits into '&lt;a href="https://pytorch.org/docs/stable/data.html" rel="noopener noreferrer"&gt;DataLoaders&lt;/a&gt;' which the model can easily train on. &lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  4. Building the model
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://colab.research.google.com/drive/1ZZIa5NG4tLHemsGOdWF5jl-ree8JtOqK#scrollTo=YXy7V_kJ8g7H&amp;amp;line=13&amp;amp;uniqifier=1" rel="noopener noreferrer"&gt;&lt;code&gt;BotClassifier&lt;/code&gt;&lt;/a&gt; is a class where my bot-detection model is defined. &lt;/li&gt;
&lt;li&gt;It's based on BERT but adds some extra layers:

&lt;ul&gt;
&lt;li&gt;bert: Loads the pre-trained BERT model.&lt;/li&gt;
&lt;li&gt;drop: A technique called 'dropout' to help prevent overfitting (the model memorizing too much about the training data).&lt;/li&gt;
&lt;li&gt;out: A final output layer to turn BERT's output into the prediction (bot or human).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Setting up the Model: 

&lt;ul&gt;
&lt;li&gt;Get the model ready to run. &lt;/li&gt;
&lt;li&gt;Specify an optimizer (&lt;a href="https://keras.io/api/optimizers/adamw/" rel="noopener noreferrer"&gt;AdamW&lt;/a&gt;). &lt;/li&gt;
&lt;li&gt;Learning rate scheduler for how the model's learning changes over time.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  5. Training the model
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Setting the model to training mode.&lt;/li&gt;
&lt;li&gt;Looping through the data and updating the model's knowledge(backward propagation) using the optimizer.
### 6. The main function&lt;/li&gt;
&lt;li&gt;A function(&lt;a href="https://colab.research.google.com/drive/1ZZIa5NG4tLHemsGOdWF5jl-ree8JtOqK#scrollTo=gN6kZep2aAsY&amp;amp;line=1&amp;amp;uniqifier=1" rel="noopener noreferrer"&gt;&lt;code&gt;start_training&lt;/code&gt;&lt;/a&gt;) where a loop is present.&lt;/li&gt;
&lt;li&gt;This loop runs for a fixed number of epochs (training cycles). 
   In each epoch:

&lt;ul&gt;
&lt;li&gt;The model &lt;a href="https://pytorch.org/docs/stable/generated/torch.nn.Module.html#torch.nn.Module.train" rel="noopener noreferrer"&gt;trains&lt;/a&gt; on the training data.&lt;/li&gt;
&lt;li&gt;The model is &lt;a href="https://pytorch.org/docs/stable/generated/torch.nn.Module.html#torch.nn.Module.eval" rel="noopener noreferrer"&gt;evaluated&lt;/a&gt; on the validation data.&lt;/li&gt;
&lt;li&gt;The best-performing model is saved.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  7. Final Evaluation
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;A function(&lt;a href="https://colab.research.google.com/drive/1ZZIa5NG4tLHemsGOdWF5jl-ree8JtOqK#scrollTo=G9PtqPEFenkA&amp;amp;line=4&amp;amp;uniqifier=1" rel="noopener noreferrer"&gt;&lt;code&gt;evaluate_model&lt;/code&gt;&lt;/a&gt;) to get the truest sense of how well the model has learned to generalize to unseen data.&lt;/li&gt;
&lt;li&gt;After training was done, I evaluated the model one more time on the held-out testing set (&lt;a href="https://colab.research.google.com/drive/1ZZIa5NG4tLHemsGOdWF5jl-ree8JtOqK#scrollTo=rSd4b1Mx6OR4&amp;amp;line=5&amp;amp;uniqifier=1" rel="noopener noreferrer"&gt;&lt;code&gt;test_data_loader&lt;/code&gt;&lt;/a&gt;). &lt;/li&gt;
&lt;li&gt;A function(&lt;a href="https://colab.research.google.com/drive/1ZZIa5NG4tLHemsGOdWF5jl-ree8JtOqK#scrollTo=G_yenye14IKI&amp;amp;line=14&amp;amp;uniqifier=1" rel="noopener noreferrer"&gt;&lt;code&gt;test_with_single_data&lt;/code&gt;&lt;/a&gt;) to test out a signup on the model.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now I will try to explain the above stages as simple as possible.&lt;/p&gt;
&lt;h2&gt;
  
  
  How did I create the Dataset?
&lt;/h2&gt;

&lt;p&gt;I have mainly name and email fields in the newsletter signup and there is no verification. &lt;br&gt;
  Then I manually blacklisted all the bots in the email service &lt;a href="https://listmonk.app/" rel="noopener noreferrer"&gt;Listmonk&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So the raw data was in the format of &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Email&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Available&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:athreyac4@gmail.com"&gt;athreyac4@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;athreya c&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blocklisted&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:watcher2112@ecocryptolab.com"&gt;watcher2112@ecocryptolab.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;🔶 Withdrawing 32 911 Dollars. Gо tо withdrаwаl &amp;gt;&amp;gt;&amp;gt; &lt;a href="https://forms.yandex.com/cloud/65e6228102848f1a71edd8c9?hs=0cebe66d8b7ba4d5f0159e88dd472e8b&amp;amp;" rel="noopener noreferrer"&gt;https://forms.yandex.com/cloud/65e6228102848f1a71edd8c9?hs=0cebe66d8b7ba4d5f0159e88dd472e8b&amp;amp;&lt;/a&gt; 🔶&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This was good enough for me to do an experiment.&lt;/p&gt;

&lt;p&gt;I used the above data to get it in a simple format so that I could train it easily.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://raw.githubusercontent.com/usrername/repo/dataset.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name_email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bot&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;head&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fg0gc705yvpmdsf8vczcg.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%2Fg0gc705yvpmdsf8vczcg.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the numbers for training and testing?
&lt;/h2&gt;

&lt;p&gt;I had 103 signup emails. 91 were human and 12 were bot.&lt;/p&gt;

&lt;p&gt;I used &lt;a href="https://imbalanced-learn.org/stable/references/generated/imblearn.over_sampling.SMOTE.html" rel="noopener noreferrer"&gt;SMOTE&lt;/a&gt; and generated data in a such way that I had 90 bots and 90 humans.&lt;/p&gt;

&lt;p&gt;Finally used 144 signup data for training the model,&lt;br&gt;
  18 for testing and 18 for validating.&lt;/p&gt;

&lt;p&gt;### Data preparation&lt;/p&gt;

&lt;p&gt;We use &lt;a href="https://pandas.pydata.org/docs/user_guide/index.html#user-guide" rel="noopener noreferrer"&gt;Pandas&lt;/a&gt;, &lt;a href="https://pytorch.org/docs/stable/torch.html" rel="noopener noreferrer"&gt;Torch&lt;/a&gt;, and &lt;a href="https://scikit-learn.org/stable/" rel="noopener noreferrer"&gt;Sklearn&lt;/a&gt; packages to make use of their utils   for splitting data into training and testing sets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;  &lt;span class="n"&gt;sklearn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;tts&lt;/span&gt;

  &lt;span class="n"&gt;INITIAL_TEST_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;
  &lt;span class="n"&gt;RANDOM_SEED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
  &lt;span class="n"&gt;VALIDATION_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;

&lt;span class="c1"&gt;# Splits the dataset into a training set (for model training) and a testing set (for evaluating its performance).
&lt;/span&gt;
  &lt;span class="n"&gt;df_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;df_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;test_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;INITIAL_TEST_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RANDOM_SEED&lt;/span&gt;
                        &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Further splits the testing set into a validation set (for tuning model parameters) and a final testing set.
&lt;/span&gt;
&lt;span class="n"&gt;df_val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;df_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="n"&gt;test_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;VALIDATION_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RANDOM_SEED&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;&lt;strong&gt;Custom Dataset&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://colab.research.google.com/drive/1ZZIa5NG4tLHemsGOdWF5jl-ree8JtOqK#scrollTo=78kRG9Qkzi3W&amp;amp;line=23&amp;amp;uniqifier=1" rel="noopener noreferrer"&gt;&lt;code&gt;NewsletterCollectionDataset&lt;/code&gt;&lt;/a&gt; Class&lt;br&gt;
    This class defines a dataset that can be used with PyTorch models. &lt;/p&gt;

&lt;p&gt;It takes care of preprocessing the raw name email data using a BERT tokenizer and &lt;br&gt;
   converting it into suitable input for a machine-learning model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;   &lt;span class="c1"&gt;# Provide tools for creating custom datasets and loading data in batches for machine learning. 
&lt;/span&gt;   &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
   &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;torch.utils.data&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NewsletterCollectionDataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Args:
        bot: Labels for each sample (0 or 1).
        name_emails:  List of name email text samples.
        tokenizer: BERT tokenizer for preprocessing.
        max_len:  Maximum sequence length.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bots&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name_emails&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_len&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_emails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name_emails&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bots&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max_len&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__len__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_emails&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 the heart of the class. Here's what happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grabs a name email signup and its bot/human label.&lt;/li&gt;
&lt;li&gt;Uses the BERT tokenizer to turn the text into numbers the model understands.&lt;/li&gt;
&lt;li&gt;Bundles everything neatly with labels ready for PyTorch.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getitem__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;name_email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_emails&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;bot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bots&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;encoding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode_plus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;add_special_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_len&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;truncation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;return_token_type_ids&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;pad_to_max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;return_attention_mask&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;return_tensors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name_email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;name_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input_ids&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input_ids&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attention_mask&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attention_mask&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bot&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;long&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;&lt;strong&gt;Data Loaders&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://colab.research.google.com/drive/1ZZIa5NG4tLHemsGOdWF5jl-ree8JtOqK#scrollTo=0RK3hJDCzS73&amp;amp;line=1&amp;amp;uniqifier=1" rel="noopener noreferrer"&gt;&lt;code&gt;create_data_loader&lt;/code&gt;&lt;/a&gt; Function&lt;/p&gt;

&lt;p&gt;Creates DataLoader objects, which handle loading data in batches and&lt;br&gt;
   shuffling for the training, validation, and testing sets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;torch.utils.data&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DataLoader&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BertTokenizer&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_data_loader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_len&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Args:
        df (pandas.DataFrame): The DataFrame containing email name data and &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bot&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; labels.
        tokenizer: The BERT tokenizer for text preprocessing.
        max_len (int): The maximum length for tokenized sequences.
        batch_size (int): Number of samples per batch.

    Returns:
        torch.utils.data.DataLoader: A DataLoader instance for iterating over the dataset.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;ds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NewsletterCollectionDataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;bots&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bot&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_numpy&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;name_emails&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name_email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_numpy&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;max_len&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_len&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;DataLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;num_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creating model data for training, validation, and testing using the data loaders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Loads the BERT tokenizer for text preprocessing.
&lt;/span&gt;&lt;span class="n"&gt;PRE_TRAINED_MODEL_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bert-base-cased&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;TOKENIZER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BertTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PRE_TRAINED_MODEL_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Maximum sequence length for tokenization.
&lt;/span&gt;&lt;span class="n"&gt;MAX_LEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;

&lt;span class="c1"&gt;# Batch size for training.
&lt;/span&gt;&lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;

&lt;span class="n"&gt;train_data_loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_data_loader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TOKENIZER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MAX_LEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;test_data_loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_data_loader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TOKENIZER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MAX_LEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;val_data_loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_data_loader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df_val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TOKENIZER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MAX_LEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;## The Model: BERT Plus a Bit More&lt;br&gt;
  My core model (&lt;a href="https://colab.research.google.com/drive/1ZZIa5NG4tLHemsGOdWF5jl-ree8JtOqK#scrollTo=YXy7V_kJ8g7H&amp;amp;line=4&amp;amp;uniqifier=1" rel="noopener noreferrer"&gt;&lt;code&gt;BotClassifier&lt;/code&gt;&lt;/a&gt;) isn't crazy complex. Think of it like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BERT Does the Heavy Lifting&lt;/strong&gt;: I feed BERT those name email signups and it turns them into meaningful representations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch.nn&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BertModel&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BotClassifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Args:
        n_classes (int): The number of output classes (e.g., 2 for bot vs. human).
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_classes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BotClassifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BertModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PRE_TRAINED_MODEL_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Dropout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hidden_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_classes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Dropout&lt;/strong&gt;: Little Bit of Randomness Dropout randomly zeroes out some connections during training, making the model less prone to overfitting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Output Layer&lt;/strong&gt;: "Bot" or "Not"? A simple linear layer takes BERT's output and makes the final prediction.&lt;/p&gt;

&lt;p&gt;Defines the forward pass through the spam classification model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attention_mask&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        Args:
            input_ids (torch.Tensor): Tokenized input sequences.
            attention_mask (torch.Tensor): Attention mask indicating real vs. padded tokens.

        Returns:
            torch.Tensor: The model&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s output logits (un normalized class probabilities).
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

        &lt;span class="n"&gt;pooled_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;input_ids&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;input_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;attention_mask&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;attention_mask&lt;/span&gt;
        &lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pooled_output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;out&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Check for CUDA (GPU) availability; otherwise defaults to CPU.
&lt;/span&gt;&lt;span class="n"&gt;DEVICE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;device&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cuda&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_available&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cpu&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BotClassifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_classes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DEVICE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;## What did the training involve?&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://colab.research.google.com/drive/1ZZIa5NG4tLHemsGOdWF5jl-ree8JtOqK#scrollTo=7sad0YiJRVjH&amp;amp;line=7&amp;amp;uniqifier=1" rel="noopener noreferrer"&gt;&lt;code&gt;train&lt;/code&gt;&lt;/a&gt; function is where I teach this model to spot the bots.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;loss_fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;data_loader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n_examples&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Args:
        model (nn.Module): The PyTorch model to train.
        loss_fn (nn.Module): The loss function for calculating error.
        optimizer (torch.optim.Optimizer): The optimizer used for updating model parameters.
        scheduler: A learning rate scheduler to adjust learning rate during training.
        device (torch.device): The device where the model and data should be loaded (&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cpu&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; or &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cuda&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)
        data_loader (torch.utils.data.DataLoader): A DataLoader providing batches of training data.
        n_examples (int): The total number of training examples in the dataset.

    Returns:
        tuple: A tuple containing:
            * train_acc (float): Training accuracy for the epoch.
            * train_loss (float): Average training loss for the epoch.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Sets the model to training mode
&lt;/span&gt;
    &lt;span class="n"&gt;losses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;correct_predictions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;For each batch of data, it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feeds data to the model.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data_loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Data preparation
&lt;/span&gt;        &lt;span class="n"&gt;input_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input_ids&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;attention_mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attention_mask&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;targets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bot&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Forward pass
&lt;/span&gt;        &lt;span class="n"&gt;outputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_ids&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;input_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attention_mask&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;attention_mask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Calculates how wrong the model was (that's the loss).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;        &lt;span class="c1"&gt;# Loss calculation
&lt;/span&gt;        &lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loss_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Accuracy calculation
&lt;/span&gt;        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;preds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;correct_predictions&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;preds&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;item&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Tweaks the model to be better next time (backpropagation and the optimizer).&lt;/li&gt;
&lt;li&gt;Learning rate magic: The scheduler adjusts the learning rate, so the model learns quickly at first and then fine-tunes itself.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;        &lt;span class="c1"&gt;# Back propagation
&lt;/span&gt;        &lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clip_grad_norm_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;max_norm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Gradient clipping
&lt;/span&gt;
        &lt;span class="c1"&gt;# Optimization
&lt;/span&gt;        &lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zero_grad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;train_acc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;correct_predictions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;double&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;n_examples&lt;/span&gt;
    &lt;span class="n"&gt;train_loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;train_acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;train_loss&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;
&lt;span class="n"&gt;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;EPOCHS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_training&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;best_accuracy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;epoch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EPOCHS&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Epoch &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;epoch&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;EPOCHS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&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 where the core learning happens for one epoch. Accuracy and loss (how wrong the model is) are calculated on your training data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;        &lt;span class="n"&gt;train_acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;train_loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;loss_fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;DEVICE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;train_data_loader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Train loss &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;train_loss&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; accuracy &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;train_acc&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://colab.research.google.com/drive/1ZZIa5NG4tLHemsGOdWF5jl-ree8JtOqK#scrollTo=G9PtqPEFenkA&amp;amp;line=1&amp;amp;uniqifier=1" rel="noopener noreferrer"&gt;&lt;code&gt;evaluate_model&lt;/code&gt;&lt;/a&gt; function tests how well the model is doing on a validation dataset it hasn't seen before. &lt;br&gt;
This helps prevent overfitting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;        &lt;span class="n"&gt;val_acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val_loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;evaluate_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;loss_fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;DEVICE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;val_data_loader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df_val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Validation loss &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;val_loss&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; accuracy &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;val_acc&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the model beats its previous best performance on the validation set, it's saved.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;        &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;train_acc&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;train_acc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;train_loss&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;train_loss&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;val_acc&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val_acc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;val_loss&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val_loss&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;val_acc&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;best_accuracy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;state_dict&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;best_detector_model.bin&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;best_accuracy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;val_acc&lt;/span&gt;

&lt;span class="nf"&gt;start_training&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Is it working?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Testing the model with a signup
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Single Signups&lt;/strong&gt;: The &lt;a href="https://colab.research.google.com/drive/1ZZIa5NG4tLHemsGOdWF5jl-ree8JtOqK#scrollTo=G_yenye14IKI&amp;amp;line=11&amp;amp;uniqifier=1" rel="noopener noreferrer"&gt;&lt;code&gt;test_with_single_data&lt;/code&gt;&lt;/a&gt; Function&lt;br&gt;
demonstrates how to use the model on one signup at a time&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prepping the Input&lt;/strong&gt;: Just like during training, we use our trusty BERT tokenizer (TOKENIZER) to turn a new signup into the right format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_with_single_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_to_test&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests a single signup to determine if it&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s likely from a bot or human.

    Args:
        data_to_test (str): The name and email data from a newsletter signup.

    Prints:
        The input signup data along with the model&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s prediction (bot or human).
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="c1"&gt;# Tokenize and prepare input data for the model
&lt;/span&gt;    &lt;span class="n"&gt;encoding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TOKENIZER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode_plus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;data_to_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;add_special_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MAX_LEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;truncation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;return_token_type_ids&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;pad_to_max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;return_attention_mask&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;return_tensors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;input_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_ids&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DEVICE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;attention_mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attention_mask&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DEVICE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;To the Model!&lt;/strong&gt;: The model spits out a prediction, and we turn its numbers into a probability using torch.nn.functional.softmax.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="c1"&gt;# Set model to evaluation mode and run prediction
&lt;/span&gt;    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;no_grad&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;outputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_ids&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;input_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attention_mask&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;attention_mask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;functional&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;softmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Get the class prediction (0 = human, 1 = bot)
&lt;/span&gt;    &lt;span class="n"&gt;prediction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;argmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;item&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Bot or Not&lt;/strong&gt;? Based on that probability, we decide whether it's likely a bot or a real human signup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="c1"&gt;# Print the input data and the prediction result
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Input Name Email: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data_to_test&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;prediction&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The signup is likely from a bot.  &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The signup is likely from a human. &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rishic2013@gmail.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rishi C &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;email2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lama2@hexmos.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;name2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🔶Lama2.  G t 12     &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nf"&gt;test_with_single_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;test_with_single_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name2&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;email2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Ffc5fyilzt1n9wlc1rp21.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%2Ffc5fyilzt1n9wlc1rp21.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Method I used for debugging and achieved 94% from 87%
&lt;/h2&gt;

&lt;p&gt;When I wanted to gain more accuracy, I didn't exactly know what was going wrong. &lt;/p&gt;

&lt;p&gt;So when I implemented and understood the Confusion Matrix,&lt;br&gt;
it was showing one False Positive.&lt;/p&gt;

&lt;p&gt;So let me exlain what is confusion matrix is &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%2Fpjlbkywua3102asrnwvh.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%2Fpjlbkywua3102asrnwvh.png" alt="confusion matrix"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The confusion matrix is a simple and powerful tool that provides a clear picture of how well the classification happens.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://scikit-learn.org/stable/" rel="noopener noreferrer"&gt;&lt;em&gt;Sklearn&lt;/em&gt;&lt;/a&gt; provides a function called &lt;em&gt;confusion_matrix&lt;/em&gt; to visualize the classification.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;confusion_matrix&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;seaborn&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;sns&lt;/span&gt; 

&lt;span class="n"&gt;cm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;confusion_matrix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;numpy&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;y_pred&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;numpy&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; 
&lt;span class="n"&gt;custom_colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#f0a9b1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#a9f0b9&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;sns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;heatmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;annot&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;custom_colors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;d&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Predicted&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;True&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For plotting the confusion matrix, I used &lt;a href="https://matplotlib.org/stable/users/getting_started/" rel="noopener noreferrer"&gt;Matplotlib&lt;/a&gt; and the &lt;a href="https://seaborn.pydata.org/tutorial/introduction.html" rel="noopener noreferrer"&gt;Seaborn&lt;/a&gt; library in Python.&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%2Fxub9berhu10461w70eu7.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%2Fxub9berhu10461w70eu7.png" alt="confuse"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Think of it like a truth table for your model. It lays everything out:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;True Negative&lt;/strong&gt; (Top left): 6 - The model correctly identified 7 human signups.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;False Positive&lt;/strong&gt; (Top right): 0 - The model incorrectly identified 0 human signup as a bot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;False Negative&lt;/strong&gt; (Bottom Left): 1 - The model incorrectly identified 1 bot signup as human.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;True Positive&lt;/strong&gt; (Bottom right): 11 - The model correctly identified 11 bot signups.&lt;/p&gt;

&lt;p&gt;Coming back to the original problem, I had one False Positive&lt;/p&gt;

&lt;p&gt;That meant the model was wrongly flagging a real person as a bot! A quick look at my data with my &lt;a href="https://colab.research.google.com/drive/1ZZIa5NG4tLHemsGOdWF5jl-ree8JtOqK#scrollTo=Hl3_-YevJHtW&amp;amp;line=14&amp;amp;uniqifier=1" rel="noopener noreferrer"&gt;&lt;code&gt;show_misclasified()&lt;/code&gt;&lt;/a&gt; function&lt;/p&gt;

&lt;p&gt;I realized I had mislabeled data during my balancing act. &lt;br&gt;
A single human mislabeled as a bot was causing the dip. &lt;/p&gt;

&lt;p&gt;One fix, one retrain, and done – 94% accuracy!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&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%2Fk80gdlgmrcwi2pon5qgw.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%2Fk80gdlgmrcwi2pon5qgw.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My bot detector achieved a 91.6% success rate catching bots, with a perfect score (100%) identifying real subscribers.&lt;/p&gt;

&lt;p&gt;Not bad, since accidentally blocking a real person (false positive) is a much bigger concern than missing a sneaky bot.&lt;/p&gt;

&lt;p&gt;This is a good start, but I'm always looking to improve. I'll be gathering more data and experimenting to see if I can boost the accuracy even further.&lt;/p&gt;

&lt;p&gt;Want to stay updated on my progress? Subscribe to our journal for next week's content on &lt;strong&gt;fine-tuning Stable Diffusion!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Originally published at &lt;a href="https://journal.hexmos.com/bots-invaded-my-newsletter-heres-how-i-fought-back-with-ml/" rel="noopener noreferrer"&gt;https://journal.hexmos.com&lt;/a&gt; on March 31, 2024.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>webdev</category>
      <category>ai</category>
      <category>python</category>
    </item>
    <item>
      <title>Training LLMs Taking Too Much Time? Technique you need to know to train it faster</title>
      <dc:creator>Rijul Rajesh</dc:creator>
      <pubDate>Sun, 03 Mar 2024 15:48:23 +0000</pubDate>
      <link>https://forem.com/hexmos/training-llms-taking-too-much-time-technique-you-need-to-know-to-train-it-faster-3k8d</link>
      <guid>https://forem.com/hexmos/training-llms-taking-too-much-time-technique-you-need-to-know-to-train-it-faster-3k8d</guid>
      <description>&lt;h4&gt;
  
  
  The Challenges of Training LLMs: Lots of Time and Resources
&lt;/h4&gt;

&lt;p&gt;Suppose you want to train a &lt;strong&gt;Large Language Model(LLM)&lt;/strong&gt;, which can understand and produce human-like text. You want to input questions related to your organization and get answers from it.&lt;/p&gt;

&lt;p&gt;The problem is that the LLM doesn't know your organization, It only knows general things. That is where applying techniques to the models like &lt;strong&gt;Finetuning&lt;/strong&gt;, &lt;strong&gt;RAG&lt;/strong&gt; and many others comes up.&lt;/p&gt;

&lt;p&gt;If we want to train Big LLMs, It requires &lt;strong&gt;a lot of resources and time&lt;/strong&gt;. So it's a hefty task unless you have the proper machine to do the job.&lt;/p&gt;

&lt;h4&gt;
  
  
  Story of How We Solved The Problem of Time and Resources
&lt;/h4&gt;

&lt;p&gt;Suppose we want to train the &lt;a href="https://llama.meta.com/" rel="noopener noreferrer"&gt;Llama 2&lt;/a&gt; LLM based on the information of our organization, and we are using &lt;strong&gt;Google Colab&lt;/strong&gt; to train it. The free version of Colab provides a single &lt;a href="https://www.nvidia.com/en-in/data-center/tesla-t4/" rel="noopener noreferrer"&gt;Nvidia T4&lt;/a&gt; GPU, which provides &lt;strong&gt;16GB&lt;/strong&gt; of memory.&lt;/p&gt;

&lt;p&gt;But for training the Llama 2 - 7 Billion Parameter model we require &lt;strong&gt;28GB&lt;/strong&gt; of memory.&lt;br&gt;
This is a problem, We can't train the model with only &lt;strong&gt;16GB&lt;/strong&gt; of memory.&lt;/p&gt;

&lt;p&gt;So to solve this, we tried researching into some optimization techniques and we found &lt;a href="https://arxiv.org/abs/2106.09685" rel="noopener noreferrer"&gt;LoRA&lt;/a&gt;, Which stands for &lt;em&gt;Low-Rank Adaptation of Large Language Models&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LoRA&lt;/strong&gt; adds a layer of finetuning to the model, without modifying the existing model. This consumes less time and memory.&lt;/p&gt;

&lt;p&gt;By using LoRA, I was able to finetune the Llama-2 Model and get the outputs from it from a single T4 GPU.&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%2F0n4jcarbqim1g2iej58g.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%2F0n4jcarbqim1g2iej58g.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Refer to the above image. I asked the Llama2 model without finetuning a question, &lt;em&gt;How many servers does Hexmos Have?&lt;/em&gt; It gave the reply that it is unable to provide the information.&lt;/p&gt;

&lt;p&gt;After finetuning I asked the same question, and it gave me this reply&lt;br&gt;
&lt;em&gt;Hexmos has 2 servers in Azure and 4 servers in AWS&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's see how &lt;strong&gt;LoRA&lt;/strong&gt; helped me achieve this.&lt;/p&gt;

&lt;h4&gt;
  
  
  How LoRA Helps with Finetuning More Efficiently
&lt;/h4&gt;

&lt;p&gt;Let's have a deeper dive into how &lt;strong&gt;LoRA&lt;/strong&gt; works.&lt;/p&gt;

&lt;p&gt;When training large models like &lt;strong&gt;GPT-3&lt;/strong&gt;, it has 175 Billion Parameters. &lt;strong&gt;Parameters&lt;/strong&gt; are like numbers that are stored in Matrices, it is like the knobs and dials that the model tweaks to get better at its task. Fully Finetuning them to our needs is a daunting task and requires &lt;strong&gt;a lot of computational resources&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LoRA&lt;/strong&gt;, takes a different approach to this problem, Instead of fine-tuning the entire model, it focuses on modifying a smaller set of parameters.&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%2F2fqu3x205herl5wgoqjg.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%2F2fqu3x205herl5wgoqjg.png" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
Consider the above 2 boxes. One represents the weights for the existing model, the second one represents our fine-tuned weights(Based on our custom dataset). These are added together to form our &lt;strong&gt;fine-tuned model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So by this method, We don't need to change the existing weights in the model. Instead, we add our fine-tuned weights on top of the original weights, this makes it less computationally expensive.&lt;/p&gt;

&lt;p&gt;So another question may arise, how are these finetuned weights calculated?&lt;br&gt;
In Matrices, we have a concept called Rank.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rank&lt;/strong&gt;, in simple words, determines the precision of the model after finetuning, If the Rank is low, There will be more optimization. But at the same time, you will be sacrificing the accuracy of the model.&lt;/p&gt;

&lt;p&gt;If the Rank is high, the precision will be higher but there will be lesser optimization.&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%2F3b931npayq5sg5azu1d0.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%2F3b931npayq5sg5azu1d0.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The LoRA weight matrix is calculated by multiplying 2 smaller matrices.&lt;/p&gt;

&lt;p&gt;For example, we have to multiply &lt;strong&gt;1x5&lt;/strong&gt; and &lt;strong&gt;5x1&lt;/strong&gt; together to form a &lt;strong&gt;5x5&lt;/strong&gt; LoRA weight matrix.&lt;/p&gt;

&lt;p&gt;We can set the rank of the smaller matrix to determine the balance between precision and optimization.&lt;/p&gt;

&lt;h4&gt;
  
  
  Real Life Example: Training a Llama2 Model with Custom Dataset
&lt;/h4&gt;

&lt;p&gt;Continue reading &lt;a href="https://journal.hexmos.com/train-llm-faster/" rel="noopener noreferrer"&gt;the article&lt;/a&gt;&lt;/p&gt;

</description>
      <category>llms</category>
      <category>ai</category>
      <category>llama2</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
