<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fahim Faisaal</title>
    <description>The latest articles on DEV Community by Fahim Faisaal (@fahimfaisaal).</description>
    <link>https://dev.to/fahimfaisaal</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2905947%2Fd228da39-9e24-4d24-8022-7e4d3dfffe15.png</url>
      <title>DEV Community: Fahim Faisaal</title>
      <link>https://dev.to/fahimfaisaal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fahimfaisaal"/>
    <language>en</language>
    <item>
      <title>How to Verify Your Git Commits and Tags with GPG: A Step-by-Step Guide</title>
      <dc:creator>Fahim Faisaal</dc:creator>
      <pubDate>Mon, 29 Dec 2025 05:37:50 +0000</pubDate>
      <link>https://dev.to/fahimfaisaal/how-to-verify-your-git-commits-with-gpg-a-step-by-step-guide-3f86</link>
      <guid>https://dev.to/fahimfaisaal/how-to-verify-your-git-commits-with-gpg-a-step-by-step-guide-3f86</guid>
      <description>&lt;p&gt;In the world of open source and collaborative development, identity is everything. When you see a commit from &lt;code&gt;torvalds&lt;/code&gt; on the Linux kernel, how do you know it's &lt;em&gt;actually&lt;/em&gt; Linus Torvalds and not an impersonator?&lt;/p&gt;

&lt;p&gt;The answer is GPG signing.&lt;/p&gt;

&lt;p&gt;By signing your commits with a GPG (GNU Privacy Guard) key, you cryptographically verify that the code came from you and hasn't been altered. GitHub (and GitLab) rewards this with a shiny green &lt;strong&gt;"Verified"&lt;/strong&gt; badge.&lt;/p&gt;

&lt;p&gt;Here is a straightforward guide to setting this up.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Install GPG
&lt;/h2&gt;

&lt;p&gt;First, you need the GPG tool installed on your machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;macOS (Homebrew):&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;brew &lt;span class="nb"&gt;install &lt;/span&gt;gnupg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Linux (Debian/Ubuntu/Pop!_OS):&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;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;gnupg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Windows:&lt;/strong&gt;&lt;br&gt;
Download and install &lt;a href="https://www.gpg4win.org/" rel="noopener noreferrer"&gt;Gpg4win&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Generate a GPG Key
&lt;/h2&gt;

&lt;p&gt;Run the following command to generate a new key pair.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gpg &lt;span class="nt"&gt;--batch&lt;/span&gt; &lt;span class="nt"&gt;--passphrase&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nt"&gt;--quick-gen-key&lt;/span&gt; &lt;span class="s2"&gt;"Your Name &amp;lt;your_email@example.com&amp;gt;"&lt;/span&gt; rsa4096 default never
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Get Your Key ID
&lt;/h2&gt;

&lt;p&gt;Once generated, list your keys to find the Key ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gpg &lt;span class="nt"&gt;--list-secret-keys&lt;/span&gt; &lt;span class="nt"&gt;--keyid-format&lt;/span&gt; LONG
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sec   rsa4096/3AA5C34371567BD2 2024-01-01 [SC]
      ...
uid                 [ultimate] John Doe &amp;lt;john@example.com&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;strong&gt;&lt;code&gt;3AA5C34371567BD2&lt;/code&gt;&lt;/strong&gt; is your Key ID.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Tell Git About Your Key
&lt;/h2&gt;

&lt;p&gt;Now configure Git to use this key for signing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set the key:&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;git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.signingkey 3AA5C34371567BD2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Replace &lt;code&gt;3AA5C34371567BD2&lt;/code&gt; with your actual Key ID)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enable automatic signing (Optional but recommended):&lt;/strong&gt;&lt;br&gt;
This ensures every commit you make is signed by default.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; commit.gpgsign &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Add Your Public Key to GitHub
&lt;/h2&gt;

&lt;p&gt;Git knows about your key, but GitHub doesn't yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Export your public key:&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;gpg &lt;span class="nt"&gt;--armor&lt;/span&gt; &lt;span class="nt"&gt;--export&lt;/span&gt; 3AA5C34371567BD2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Copy the output:&lt;/strong&gt;&lt;br&gt;
Copy the entire block (including &lt;code&gt;-----BEGIN PGP PUBLIC KEY BLOCK-----&lt;/code&gt; and &lt;code&gt;-----END...&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Add to GitHub:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Go to &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;SSH and GPG keys&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;New GPG key&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Paste your key and save.&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  6. Verify It Works
&lt;/h2&gt;

&lt;p&gt;Make a commit in any repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"My first signed commit"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Push it to GitHub. You should now see the verified badge next to your commit in the history!&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%2Fvw28ym06s2hhr7gxvdjm.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%2Fvw28ym06s2hhr7gxvdjm.png" alt="Verified Badge Example" width="384" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;If you see "Unverified":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Check that the email in &lt;code&gt;git config user.email&lt;/code&gt; matches the email in your GPG key.&lt;/li&gt;
&lt;li&gt; Ensure that exact email is added and verified in your GitHub account settings.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>git</category>
      <category>gpg</category>
      <category>signature</category>
    </item>
    <item>
      <title>Try vzib to visualize the go ugly benchmarks to interactive charts</title>
      <dc:creator>Fahim Faisaal</dc:creator>
      <pubDate>Tue, 24 Jun 2025 15:19:55 +0000</pubDate>
      <link>https://dev.to/fahimfaisaal/try-vzib-to-visualize-the-go-ugly-benchmarks-to-interactive-charts-fp7</link>
      <guid>https://dev.to/fahimfaisaal/try-vzib-to-visualize-the-go-ugly-benchmarks-to-interactive-charts-fp7</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/fahimfaisaal" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__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%2F2905947%2Fd228da39-9e24-4d24-8022-7e4d3dfffe15.png" alt="fahimfaisaal"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/fahimfaisaal/vizb-beautiful-go-benchmark-visualization-made-simple-4c0c" class="ltag__link__link" rel="noopener noreferrer"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Vizb: Beautiful Go Benchmark Visualization Made Simple&lt;/h2&gt;
      &lt;h3&gt;Fahim Faisaal ・ Jun 24&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#showdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#go&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#cli&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>showdev</category>
      <category>go</category>
      <category>cli</category>
    </item>
    <item>
      <title>Vizb: Beautiful Go Benchmark Visualization Made Simple</title>
      <dc:creator>Fahim Faisaal</dc:creator>
      <pubDate>Tue, 24 Jun 2025 14:32:37 +0000</pubDate>
      <link>https://dev.to/fahimfaisaal/vizb-beautiful-go-benchmark-visualization-made-simple-4c0c</link>
      <guid>https://dev.to/fahimfaisaal/vizb-beautiful-go-benchmark-visualization-made-simple-4c0c</guid>
      <description>&lt;p&gt;Benchmarking in Go is straightforward, but visualizing those results? That's where things get messy. When you're comparing different libraries or implementations across various workloads, staring at raw benchmark numbers just doesn't cut it.&lt;/p&gt;

&lt;p&gt;I found myself in this exact situation while working with my message queue library, &lt;a href="https://github.com/goptics/varmq" rel="noopener noreferrer"&gt;varmq&lt;/a&gt;. I needed to compare performance across different scenarios, but existing visualization tools either didn't exist or didn't fit my needs.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Vizb&lt;/strong&gt; – a CLI tool that transforms Go benchmark results into beautiful, interactive HTML charts with a single command.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Magic is in the Simplicity
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-bench&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;^&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nt"&gt;-json&lt;/span&gt; | vizb &lt;span class="nt"&gt;-o&lt;/span&gt; output.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. One command, and you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Interactive HTML charts&lt;/strong&gt; showing execution time, memory usage, and allocations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Responsive design&lt;/strong&gt; that works on any device&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export capability&lt;/strong&gt; – download charts as PNG images directly from your browser&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart grouping&lt;/strong&gt; that automatically organizes your benchmarks by workload and subject&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Features That Actually Matter
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Multiple Metrics in One View&lt;/strong&gt;: Compare execution time, memory usage, and allocation counts side by side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customizable Units&lt;/strong&gt;: Display metrics in your preferred units (ns/μs/ms/s for time, B/KB/MB/GB for memory).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flexible Input&lt;/strong&gt;: Works with files or piped input – whatever fits your workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JSON Output&lt;/strong&gt;: Need the data in a different format? Use &lt;code&gt;--format json&lt;/code&gt; to get structured data instead of HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Example
&lt;/h2&gt;

&lt;p&gt;Here's how Vizb automatically groups your benchmarks. If you have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BenchmarkEncoder/small/json
BenchmarkEncoder/small/msgpack
BenchmarkEncoder/large/json
BenchmarkEncoder/large/msgpack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vizb creates charts where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test&lt;/strong&gt;: Encoder&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workloads&lt;/strong&gt;: small, large
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subjects&lt;/strong&gt;: json, msgpack&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result? Clean, organized charts that make performance differences immediately obvious.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why It Matters
&lt;/h2&gt;

&lt;p&gt;When you're optimizing code or choosing between libraries, you need insights, not raw numbers. Vizb turns your benchmark data into actionable visualizations that help you make better decisions faster.&lt;/p&gt;

&lt;p&gt;Check it out: &lt;a href="https://github.com/goptics/vizb" rel="noopener noreferrer"&gt;github.com/goptics/vizb&lt;/a&gt;&lt;br&gt;
Check out the live chart benchmarks: &lt;a href="https://varmq-benchmarks.netlify.app" rel="noopener noreferrer"&gt;varmq-benchmark&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Install it with: &lt;code&gt;go install github.com/goptics/vizb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Happy benchmarking! 🚀&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>showdev</category>
      <category>go</category>
      <category>cli</category>
    </item>
    <item>
      <title>sync.Cond in Go: Efficient Goroutine Signaling Without Channels</title>
      <dc:creator>Fahim Faisaal</dc:creator>
      <pubDate>Wed, 04 Jun 2025 12:50:51 +0000</pubDate>
      <link>https://dev.to/fahimfaisaal/synccond-in-go-efficient-goroutine-signaling-without-channels-2eh3</link>
      <guid>https://dev.to/fahimfaisaal/synccond-in-go-efficient-goroutine-signaling-without-channels-2eh3</guid>
      <description>&lt;p&gt;Concurrency in Go often brings us to channels, but there’s another synchronization primitive that may be exactly what you need in some scenarios: &lt;code&gt;sync.Cond&lt;/code&gt;. If you’ve ever wondered why you’d reach for a &lt;code&gt;sync.Cond&lt;/code&gt; instead of using channels alone, this article is for you. By the end, you’ll see a simple custom implementation, understand how the real &lt;code&gt;sync.Cond&lt;/code&gt; works under the hood, and know when to choose it in your own projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use &lt;code&gt;sync.Cond&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Most Go developers instinctively reach for channels to coordinate goroutines: sending values, waiting for results, and so on. However, channels also carry data. What if all you need is a simple “wake-up” signal, without any payload? That’s exactly where &lt;code&gt;sync.Cond&lt;/code&gt; shines. It’s a lightweight way to block one or more goroutines until a condition becomes true, without transferring actual data.&lt;/p&gt;

&lt;p&gt;Think of it like a broadcast system: goroutines can call &lt;code&gt;Wait()&lt;/code&gt; and suspend until somebody calls &lt;code&gt;Signal()&lt;/code&gt; (wake a single waiter) or &lt;code&gt;Broadcast()&lt;/code&gt; (wake all waiters). Underneath, &lt;code&gt;sync.Cond&lt;/code&gt; doesn’t allocate a channel for each goroutine; instead, it maintains a small linked list of waiting goroutines, making it more memory-efficient when you just need signaling.&lt;/p&gt;

&lt;p&gt;To illustrate, let’s build our own “poor man’s” condition variable using channels. Once you see the analogy, switching to &lt;code&gt;sync.Cond&lt;/code&gt; becomes straightforward.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a Custom &lt;code&gt;Cond&lt;/code&gt; with Channels
&lt;/h2&gt;

&lt;p&gt;Here’s a minimal struct that mimics &lt;code&gt;sync.Cond&lt;/code&gt; by using a slice of channels and a mutex:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;MyCond&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;chs&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;mu&lt;/span&gt;  &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mutex&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;
&lt;code&gt;chs&lt;/code&gt; holds one channel per waiting goroutine.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mu&lt;/code&gt; ensures that appending or removing from &lt;code&gt;chs&lt;/code&gt; is safe.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below are three methods &lt;code&gt;Wait()&lt;/code&gt;, &lt;code&gt;Signal()&lt;/code&gt;, and &lt;code&gt;Broadcast()&lt;/code&gt; that emulate the core behavior of &lt;code&gt;sync.Cond&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MyCond&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// wait for a signal&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MyCond&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&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="c"&gt;// pick the first channel and send signal&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}{}&lt;/span&gt;
    &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// remove that channel from the slice&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&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;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MyCond&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Broadcast&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}{}&lt;/span&gt;
        &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// reset the slice so no stale channels remain&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="m"&gt;0&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;h3&gt;
  
  
  What’s happening here?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Wait()&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Lock the mutex.&lt;/li&gt;
&lt;li&gt;Create a new “signal” channel (&lt;code&gt;ch&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Append it to &lt;code&gt;c.chs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Unlock, then block on &lt;code&gt;&amp;lt;-ch&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When someone calls &lt;code&gt;Signal()&lt;/code&gt; or &lt;code&gt;Broadcast()&lt;/code&gt;, that channel is closed (and a value is sent), letting this goroutine resume.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Signal()&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Lock the mutex.&lt;/li&gt;
&lt;li&gt;If there’s at least one waiting channel, pick the first.&lt;/li&gt;
&lt;li&gt;Send a dummy &lt;code&gt;struct{}{}&lt;/code&gt; onto it, then &lt;code&gt;close(ch)&lt;/code&gt; so that any extra &lt;code&gt;&amp;lt;-ch&lt;/code&gt; receives don’t hang.&lt;/li&gt;
&lt;li&gt;Remove that channel from the slice.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Broadcast()&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Lock the mutex.&lt;/li&gt;
&lt;li&gt;Loop over every waiting channel: send a dummy signal and close it.&lt;/li&gt;
&lt;li&gt;Reset the slice to empty, so future waiters start fresh.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This simple approach shows how condition variables signal “ready to go” without passing actual payloads just signals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Our Custom &lt;code&gt;MyCond&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;To see &lt;code&gt;MyCond&lt;/code&gt; in action, imagine spawning multiple worker goroutines that all wait for a signal. Then, from another goroutine, send one signal at a time. Finally, switch to broadcasting to wake everyone at once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;cond&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;MyCond&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;wg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WaitGroup&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
    &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// add tasks count to the wait group&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// create separate go routine for each task&lt;/span&gt;
        &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&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="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Waiting"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&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="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Done"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// send signal to one routine in every 1 second&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;

    &lt;span class="c"&gt;// wait for all routines to finish&lt;/span&gt;
    &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&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;h3&gt;
  
  
  The output
&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%2F5kjw2coi8w8tqr3j5j5e.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%2F5kjw2coi8w8tqr3j5j5e.gif" alt="Signal Outputs Preview" width="1186" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you run that, each goroutine hangs on &lt;code&gt;cond.Wait()&lt;/code&gt;. Every second, &lt;code&gt;Signal()&lt;/code&gt; wakes exactly one goroutine, until all 5 finish.&lt;/p&gt;

&lt;h3&gt;
  
  
  Switching to Broadcast
&lt;/h3&gt;

&lt;p&gt;Instead of signaling one by one, you can broadcast after a delay to wake all of them at once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// just change the signal to broadcast go routine&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// - for range tasks {&lt;/span&gt;
    &lt;span class="c"&gt;// -    time.Sleep(1 * time.Second)&lt;/span&gt;
    &lt;span class="c"&gt;// -    cond.Signal() // send signal to one routine in every 1 second&lt;/span&gt;
    &lt;span class="c"&gt;// - }&lt;/span&gt;

    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Broadcast&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// send signal to all routines at once after 2 seconds&lt;/span&gt;
&lt;span class="p"&gt;}()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The output
&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%2Fwic55gk8ltg34fn0uve1.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%2Fwic55gk8ltg34fn0uve1.gif" alt="Cond Broadcast Preview" width="1186" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this modification, all 5 goroutines sleep in &lt;code&gt;cond.Wait()&lt;/code&gt;. After two seconds, a single &lt;code&gt;Broadcast()&lt;/code&gt; wakes everybody, and you’ll see all “Done” messages in rapid succession.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replacing &lt;code&gt;MyCond&lt;/code&gt; with &lt;code&gt;sync.Cond&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Once you’ve verified the custom behavior, swapping in the real &lt;code&gt;sync.Cond&lt;/code&gt; is straightforward. Anywhere you wrote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//  cond := &amp;amp;MyCond{}&lt;/span&gt;
&lt;span class="n"&gt;cond&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewCond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;

&lt;span class="c"&gt;// cond.Wait()&lt;/span&gt;
&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll get the same “Waiting … Done” behavior as before, but now backed by the official, optimized implementation.&lt;/p&gt;

&lt;p&gt;Under the hood, &lt;code&gt;sync.Cond&lt;/code&gt; doesn’t spin up a channel per waiter. Instead it uses an internal &lt;code&gt;notifyList&lt;/code&gt; a small doubly linked list of waiting goroutines ) and uses low‐level runtime primitives to park and wake goroutines. Each call to &lt;code&gt;Wait()&lt;/code&gt; enqueues the goroutine on that list. &lt;code&gt;Signal()&lt;/code&gt; removes one link and wakes its goroutine; &lt;code&gt;Broadcast()&lt;/code&gt; traverses the whole list and wakes every waiter. Memory-wise, this is much cheaper than allocating a channel per waiter, especially if you have hundreds or thousands of goroutines occasionally blocking on the same condition.&lt;/p&gt;

&lt;p&gt;For a deeper dive, check out the source code for &lt;a href="https://github.com/golang/go/blob/master/src/sync/cond.go" rel="noopener noreferrer"&gt;&lt;code&gt;sync.Cond&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Choose &lt;code&gt;sync.Cond&lt;/code&gt; Over Channels
&lt;/h2&gt;

&lt;p&gt;Here are a few scenarios where &lt;code&gt;sync.Cond&lt;/code&gt; makes sense:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simple Signaling&lt;/strong&gt;&lt;br&gt;
If goroutines only need a “go now” notification no data passed &lt;code&gt;sync.Cond&lt;/code&gt; provides a clearer, more intent-expressive API than channels filled with dummy values.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Broadcast Semantics&lt;/strong&gt;&lt;br&gt;
Channels lack a built-in “wake everyone” primitive. You could loop over a list of channels, but managing that list is extra boilerplate. &lt;code&gt;sync.Cond.Broadcast()&lt;/code&gt; does exactly what it says: wake all waiters at once.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lower Memory Overhead&lt;/strong&gt;&lt;br&gt;
Each Go channel has internal buffers, mutexes, and so on. If you merely need a “signal,” channels allocate more than necessary. A &lt;code&gt;sync.Cond&lt;/code&gt; maintains a minimal linked list of waiters, which is especially noticeable if you have thousands of goroutines waiting occasionally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Condition-Based Waiting&lt;/strong&gt;&lt;br&gt;
Often you combine &lt;code&gt;sync.Cond&lt;/code&gt; with a separate shared value. For example:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;   &lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;conditionMet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="c"&gt;// now the condition is true; proceed&lt;/span&gt;
   &lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This “wait in a loop” pattern is common in concurrent structures like pools, queues, or buffered buffers. Channels alone can’t you’d have to juggle extra variables or use &lt;code&gt;select&lt;/code&gt;, which can get messy.&lt;/p&gt;

&lt;p&gt;In short, if your goroutines coordinate purely on a boolean or numerical condition and you want to wake either one waiter or all waiters &lt;code&gt;sync.Cond&lt;/code&gt; shines. If you need to send actual data, channels remain the more idiomatic choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open-Source Promotions
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/goptics/varmq" rel="noopener noreferrer"&gt;VarMQ&lt;/a&gt; - A Simplest Storage-Agnostic and Zero-dep Message Queue for Your Concurrent Go Program&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/goptics/vizb" rel="noopener noreferrer"&gt;Vizb&lt;/a&gt; - An interactive go benchmarks visualizer&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>go</category>
      <category>beginners</category>
    </item>
    <item>
      <title>VarMQ Tuning Worker Pool</title>
      <dc:creator>Fahim Faisaal</dc:creator>
      <pubDate>Sun, 18 May 2025 03:34:15 +0000</pubDate>
      <link>https://dev.to/fahimfaisaal/varmq-tuning-worker-pool-1bo6</link>
      <guid>https://dev.to/fahimfaisaal/varmq-tuning-worker-pool-1bo6</guid>
      <description>&lt;p&gt;I've created a "tune API" for the next version of &lt;strong&gt;&lt;a href="https://github.com/goptics/varmq" rel="noopener noreferrer"&gt;VarMQ&lt;/a&gt;&lt;/strong&gt;. Essentially, "Tune" allows you to increase or decrease the size of the worker/thread pool at runtime.&lt;/p&gt;

&lt;p&gt;For example, when the load on your server is high, you'll need to process more concurrent jobs. Conversely, when the load is low, you don't need as many workers, because workers consume resources.&lt;/p&gt;

&lt;p&gt;Therefore, based on your custom logic, you can dynamically change the worker pool size using this tune API.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/XQZtr9DxCsw"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In this video, I've &lt;strong&gt;enqueued 1000 jobs&lt;/strong&gt; into &lt;strong&gt;&lt;a href="https://github.com/goptics/varmq" rel="noopener noreferrer"&gt;VarMQ&lt;/a&gt;&lt;/strong&gt;, and I've set the initial worker pool size to &lt;strong&gt;10&lt;/strong&gt; (the concurrency value).&lt;/p&gt;

&lt;p&gt;Every second, using the tune API, I'm increasing the worker pool size by 10 until it &lt;strong&gt;reaches 100&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once it reaches a size of &lt;strong&gt;100&lt;/strong&gt;, then I start removing &lt;strong&gt;10 workers&lt;/strong&gt; at a time from the pool.&lt;/p&gt;

&lt;p&gt;This way, I'm decreasing and then increasing the worker pool size.&lt;/p&gt;

&lt;p&gt;Cool, right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/goptics/varmq" rel="noopener noreferrer"&gt;VarMQ&lt;/a&gt;&lt;/strong&gt; primarily uses an &lt;strong&gt;Event-Loop&lt;/strong&gt; internally to handle this concurrency. &lt;/p&gt;

&lt;p&gt;This event loop checks if there are any pending jobs in the queue and if any workers are available in the worker pool. If there are, it distributes jobs to all available workers and then goes back into sleep mode.&lt;/p&gt;

&lt;p&gt;When a worker becomes free, it then tells the event loop, "Hey, I'm free now; if you have any jobs, you can give them to me."&lt;/p&gt;

&lt;p&gt;The event loop then checks again if there are any pending jobs in the queue. If there are, it continues to distribute them to the workers.&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;&lt;a href="https://github.com/goptics/varmq" rel="noopener noreferrer"&gt;VarMQ's&lt;/a&gt;&lt;/strong&gt; concurrency model.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>go</category>
      <category>worker</category>
      <category>programming</category>
    </item>
    <item>
      <title>A Story of Building a Storage-Agnostic Message Queue</title>
      <dc:creator>Fahim Faisaal</dc:creator>
      <pubDate>Fri, 09 May 2025 13:41:34 +0000</pubDate>
      <link>https://dev.to/fahimfaisaal/a-story-of-building-a-storage-agnostic-message-queue-3lo4</link>
      <guid>https://dev.to/fahimfaisaal/a-story-of-building-a-storage-agnostic-message-queue-3lo4</guid>
      <description>&lt;p&gt;A year ago, I was knee-deep in Golang, trying to build a simple &lt;a href="https://github.com/fahimfaisaal/exp-go-concurrency/blob/main/concurrent-queue/queue.go" rel="noopener noreferrer"&gt;concurrent queue&lt;/a&gt; as a learning project. Coming from a Node.js background, where I’d spent years working with tools like BullMQ and RabbitMQ, Go’s concurrency model felt like a puzzle. My first attempt a minimal queue with round-robin channel selection was, well, buggy. Let’s just say it worked until it didn’t.&lt;/p&gt;

&lt;p&gt;But that’s how learning goes, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Spark of an Idea
&lt;/h2&gt;

&lt;p&gt;In my professional work, I’ve used tools like BullMQ and RabbitMQ for event-driven solutions, and p-queue and p-limit for handling concurrency. Naturally, I began wondering if there were similar tools in Go. I found packages like &lt;code&gt;asynq&lt;/code&gt;, &lt;code&gt;ants&lt;/code&gt;, and various worker pools solid, battle-tested options. But suddenly, a thought struck me: what if I built something different? A package with zero dependencies, high concurrency control, and designed as a message queue rather than submitting functions?&lt;/p&gt;

&lt;p&gt;With that spark, I started building my first Go package, released it, and named it &lt;a href="https://pkg.go.dev/github.com/fahimfaisaal/gocq" rel="noopener noreferrer"&gt;Gocq&lt;/a&gt; (Go Concurrent Queue). The core API was straightforward, as you can see here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Create a queue with 2 concurrent workers&lt;/span&gt;
&lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gocq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Millisecond&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;data&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;// Add a single job&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&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="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Output: 10&lt;/span&gt;

&lt;span class="c"&gt;// Add multiple jobs&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Output: 2, 4, 6, 8, 10 (unordered)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the excitement, I &lt;a href="https://www.reddit.com/r/golang/comments/1j5k9g8/i_built_a_concurrency_queue_that_might_bring_some/?utm_source=share&amp;amp;utm_medium=web3x&amp;amp;utm_name=web3xcss&amp;amp;utm_term=1&amp;amp;utm_content=share_button" rel="noopener noreferrer"&gt;posted&lt;/a&gt; it on Reddit. To my surprise, it got traction upvotes, comments, and appreciations. Here’s the fun part: coming from the Node.js ecosystem, I totally messed up Go’s package system at first.&lt;/p&gt;

&lt;p&gt;Within a week, I released the next version with a few major changes and shared it on Reddit &lt;a href="https://www.reddit.com/r/golang/comments/1jcdsxo/gocq_is_now_on_v2_now_faster_smarter_and_fancier/?utm_source=share&amp;amp;utm_medium=web3x&amp;amp;utm_name=web3xcss&amp;amp;utm_term=1&amp;amp;utm_content=share_button" rel="noopener noreferrer"&gt;again&lt;/a&gt;. More feedback rolled in, and one person asked for "persistence abstractions support".&lt;/p&gt;

&lt;h2&gt;
  
  
  The Missing Piece
&lt;/h2&gt;

&lt;p&gt;That hit home, I’d felt this gap before, &lt;strong&gt;Persistence&lt;/strong&gt;. It’s the backbone of any reliable queue system. Without persistence, the package wouldn’t be complete. But then a question is: if I add persistence, would I have to tie it to a specific tool like Redis or another database?&lt;/p&gt;

&lt;p&gt;I didn’t want to lock users into Redis, SQLite, or any specific storage. What if the queue could adapt to any database?&lt;/p&gt;

&lt;p&gt;So I tore gocq apart.&lt;/p&gt;

&lt;p&gt;I rewrote most of it, splitting the core into two parts: a worker pool and a queue interface. The worker would pull jobs from the queue without caring where those jobs lived.&lt;/p&gt;

&lt;p&gt;The result? &lt;a href="https://github.com/goptics/varmq" rel="noopener noreferrer"&gt;VarMQ&lt;/a&gt;, a queue system that doesn’t care if your storage is Redis, SQLite, or even in-memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works Now
&lt;/h2&gt;

&lt;p&gt;Imagine you need a simple, in-memory queue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;varmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="n"&gt;varmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c"&gt;// your heavy work&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BindQueue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Done. No setup, no dependencies.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if you want persistence, just plug in an adapter. Let’s say SQLite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"github.com/goptics/sqliteq"&lt;/span&gt;

&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sqliteq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test.db"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithPersistentQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Now your jobs survive restarts.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or Redis for distributed workloads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"github.com/goptics/redisq"&lt;/span&gt;

&lt;span class="n"&gt;rdb&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;redisq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"redis://localhost:6379"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pq&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rdb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDistributedQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"transactions"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithDistributedQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Scale across servers.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The magic? The worker doesn’t know—or care—what’s behind the queue. It just processes jobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons from the Trenches
&lt;/h2&gt;

&lt;p&gt;Building this taught me two big things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity is hard&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feedback is gold&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Message queues are everywhere—order processing, notifications, data pipelines. But not every project needs Redis. Sometimes you just want SQLite for simplicity, or to switch databases later without rewriting code.&lt;/p&gt;

&lt;p&gt;With Varmq, you’re not boxed in. Need persistence? Add it. Need scale? Swap adapters. It’s like LEGO for queues.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;The next step is to integrate the PostgreSQL adapter and a monitoring system. &lt;/p&gt;

&lt;p&gt;If you’re curious, check out &lt;a href="https://github.com/goptics/varmq" rel="noopener noreferrer"&gt;Varmq on GitHub&lt;/a&gt;. Feel free to share your thoughts and opinions in the comments below, and let's make this Better together.&lt;/p&gt;

</description>
      <category>go</category>
      <category>queue</category>
      <category>distributedsystems</category>
      <category>worker</category>
    </item>
  </channel>
</rss>
