<?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: medunes</title>
    <description>The latest articles on DEV Community by medunes (@medunes).</description>
    <link>https://dev.to/medunes</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%2F516325%2Fcfbcc2e9-8a95-44b6-a102-cebf53382341.jpg</url>
      <title>DEV Community: medunes</title>
      <link>https://dev.to/medunes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/medunes"/>
    <language>en</language>
    <item>
      <title>Your Connection is Not Private: What Your Browser Is Warning You About?</title>
      <dc:creator>medunes</dc:creator>
      <pubDate>Wed, 21 Jan 2026 14:09:00 +0000</pubDate>
      <link>https://dev.to/medunes/your-connection-is-not-private-what-your-browser-is-exactly-warning-you-about-59n3</link>
      <guid>https://dev.to/medunes/your-connection-is-not-private-what-your-browser-is-exactly-warning-you-about-59n3</guid>
      <description>&lt;p&gt;We’ve all been there. You are building a local web application, you fire up &lt;code&gt;https://localhost&lt;/code&gt; to test a new feature, and bam! your browser slaps you with a giant red warning: &lt;strong&gt;"Your connection is not private."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As a developer, your first instinct is frustration. "I know it's not private! It's running on my laptop! Just let me in!" You click &lt;em&gt;Advanced -&amp;gt; Proceed to localhost (unsafe)&lt;/em&gt; and move on.&lt;/p&gt;

&lt;p&gt;But have you ever stopped to ask: &lt;strong&gt;Why&lt;/strong&gt; does the browser care so much? How does it actually "know" who is trustworthy and who isn't? And why is "fixing" this properly so much more complicated than just generating a file?&lt;/p&gt;

&lt;p&gt;The answer lies in a rigid, bureaucratic hierarchy known as the &lt;strong&gt;Chain of Trust&lt;/strong&gt;. Let’s dismantle it, layer by layer, starting from that annoying red warning.&lt;/p&gt;




&lt;h3&gt;
  
  
  Level 1: The "Handwritten ID" Problem (Self-Signed vs. CA)
&lt;/h3&gt;

&lt;p&gt;When you generate a certificate locally (using OpenSSL or a script), you create two files: a &lt;strong&gt;Private Key&lt;/strong&gt; (kept secret) and a &lt;strong&gt;Certificate&lt;/strong&gt; (public). You configure your server to use them, but the browser still hates it. Why?&lt;/p&gt;

&lt;p&gt;The browser isn't saying your &lt;strong&gt;encryption&lt;/strong&gt; is bad. The math works fine; your traffic is encrypted. The browser is saying your &lt;strong&gt;identity&lt;/strong&gt; is unverified.&lt;/p&gt;

&lt;p&gt;Imagine trying to enter a high-security government building.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real World:&lt;/strong&gt; You show a Passport. The guard trusts it because the &lt;strong&gt;Government&lt;/strong&gt; issued it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Localhost:&lt;/strong&gt; You show a napkin where you scribbled &lt;em&gt;"I am Admin"&lt;/em&gt;. The guard (Browser) rejects it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you create a "Self-Signed Certificate," you are essentially being your own government. You are vouching for yourself. The browser’s job is to protect the user from imposters, so it treats any ID not issued by a known "Government" as a fake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;br&gt;
To make the warning go away, you have to perform a "manual override." When you import your certificate into your OS (Windows Cert Store) or Linux (&lt;code&gt;/etc/ssl/certs&lt;/code&gt;), you are effectively telling your computer: &lt;em&gt;"I personally trust this Napkin ID as much as a Passport."&lt;/em&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Level 2: The Anatomy of Trust (How "Belief" Works)
&lt;/h3&gt;

&lt;p&gt;This begs the question: &lt;strong&gt;Who are these "Governments" the browser actually trusts?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you dig into your operating system (or browser settings), you will find a folder or database called the &lt;strong&gt;Trust Store&lt;/strong&gt;. This is a pre-installed list of about 100-150 organizations known as &lt;strong&gt;Root Certificate Authorities (Root CAs)&lt;/strong&gt; companies like DigiCert, GlobalSign, and Let's Encrypt.&lt;/p&gt;

&lt;p&gt;When you visit &lt;code&gt;google.com&lt;/code&gt;, the browser performs a cryptographic background check:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Handshake:&lt;/strong&gt; Google presents its Certificate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Signature Check:&lt;/strong&gt; The browser looks at the "digital signature" on the certificate. A signature is created by taking the certificate's data, hashing it, and encrypting that hash with an &lt;strong&gt;Issuer's Private Key&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Verification:&lt;/strong&gt; The browser decrypts the signature using the &lt;strong&gt;Issuer's Public Key&lt;/strong&gt; (which it finds in your local Trust Store).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the math adds up, it proves two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Integrity:&lt;/strong&gt; The certificate hasn't been altered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authenticity:&lt;/strong&gt; It was definitely issued by a Trusted Authority.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And if you have ever wondered what does a certificate concretely contain after all, let's say if you opened the certificate file in a text editor, it would show a block of text starting with&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-----BEGIN CERTIFICATE-----&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;If you decoded it (viewed its details), you would see various information, but most importantly three critical things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Identity: "This cert belongs to &lt;a href="http://www.example.com." rel="noopener noreferrer"&gt;www.example.com.&lt;/a&gt;"&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Server's Public Key: "The public key for &lt;a href="http://www.example.com" rel="noopener noreferrer"&gt;www.example.com&lt;/a&gt; is [Key Data Here]."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Other Meta Data: "Ex: This cert is valid from Jan 1st to April 1st."&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Certificate Authority (CA), like Let's Encrypt, signs a data block called the &lt;code&gt;TBSCertificate&lt;/code&gt; (To-Be-Signed Certificate). &lt;/p&gt;

&lt;p&gt;The Signature Logic is simple: Let's Encrypt takes that entire block of text, hashes it (e.g., SHA-256), and then encrypts that hash with their Private Key. That encrypted hash is the "Signature" attached to the bottom of the CRT file. &lt;/p&gt;

&lt;p&gt;Now as your browser/OS has the public key of the authority, it can decrypt it (this is how asymmetric cryptography works) and get an evidence this is only possible by the owner of the private key that corresponds to this public key, hence the authenticity!  &lt;/p&gt;




&lt;h3&gt;
  
  
  Level 3: The Intermediate Gap (Why R3?)
&lt;/h3&gt;

&lt;p&gt;Here is where developers often get confused. If you inspect the certificate for a site like Wikipedia, you won't see "Signed by Root CA." You’ll see it was signed by something like "R3" or "Let's Encrypt Authority X3."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt; &lt;em&gt;Wait, my browser trusts the Root. It doesn't know who "R3" is. Why does this work?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is the &lt;strong&gt;Intermediate CA&lt;/strong&gt;. The Root Authority almost never signs websites directly. Instead, they use a delegation system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Root CA&lt;/strong&gt; (Offline, highly secure) signs the &lt;strong&gt;Intermediate CA&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Intermediate CA&lt;/strong&gt; (Online, automated) signs &lt;strong&gt;Your Website&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When your server responds to a user, it doesn't just send its own certificate. It sends a &lt;strong&gt;Certificate Bundle&lt;/strong&gt; (or "Chain") that includes the Intermediate cert.&lt;/p&gt;

&lt;p&gt;The browser follows the breadcrumbs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;"I see &lt;code&gt;example.com&lt;/code&gt;. It was signed by R3."&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"I see R3. It was signed by ISRG Root X1."&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"I know ISRG Root X1! It's in my local Trust Store. Access Granted."&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;But why go through this trouble? Why not just use the Root?&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Level 4: The "Blast Radius" (Risk Management)
&lt;/h3&gt;

&lt;p&gt;This is the most critical architectural decision in web security.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt; &lt;em&gt;Why don't we just use the Root Key to sign everything? It would be simpler.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; Because if the Root Key is stolen, the internet breaks.&lt;/p&gt;

&lt;p&gt;The Root Key is the "Crown Jewels." It is usually kept on a computer that is &lt;strong&gt;physically air-gapped&lt;/strong&gt; (never connected to the internet), stored in a secure vault. It is only powered on rarely, under strict supervision, to sign a new Intermediate.&lt;/p&gt;

&lt;p&gt;If a hacker steals the &lt;strong&gt;Intermediate Key (R3)&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Authority &lt;strong&gt;revokes&lt;/strong&gt; R3.&lt;/li&gt;
&lt;li&gt;They spin up a new Intermediate (R4).&lt;/li&gt;
&lt;li&gt;Websites update their config to use R4.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crucially:&lt;/strong&gt; Users don't have to do anything. Their browsers still trust the Root, so they automatically trust R4.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If a hacker steals the &lt;strong&gt;Root Key&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Root must be revoked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every device on the planet&lt;/strong&gt; (phones, laptops, smart fridges) needs a System Update to remove the old Root and add a new one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Consequence:&lt;/strong&gt; Older devices that no longer receive updates (e.g., an old Android phone) would be &lt;strong&gt;permanently bricked&lt;/strong&gt;. They would never trust the new Root and would lose access to the secure internet forever.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Level 5: The Loophole (Revocation &amp;amp; The "Bricked" vs. "Vulnerable" Trade-off)
&lt;/h3&gt;

&lt;p&gt;You might argue: &lt;em&gt;"If an Intermediate is stolen, old devices won't know it was revoked anyway, so they will trust the hacker. Isn't that just as bad?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You are right to spot this flaw. An old device that doesn't get updates won't know "Intermediate R3" has been blacklisted. It will trust the hacker's site.&lt;/p&gt;

&lt;p&gt;However, the industry prefers &lt;strong&gt;Vulnerability&lt;/strong&gt; over &lt;strong&gt;Obsolescence&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scenario A (Root Breach):&lt;/strong&gt; The old phone cannot connect to &lt;code&gt;google.com&lt;/code&gt; at all. It is useless e-waste.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scenario B (Intermediate Breach):&lt;/strong&gt; The old phone &lt;em&gt;can&lt;/em&gt; connect to &lt;code&gt;google.com&lt;/code&gt;. It might &lt;em&gt;also&lt;/em&gt; connect to a hacker's site if tricked, but the device still functions for legitimate use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Modern browsers mitigate this using &lt;strong&gt;OCSP (Online Certificate Status Protocol)&lt;/strong&gt;. Instead of downloading a massive list of banned certs, the browser can ping the Authority in real-time: &lt;em&gt;"Hey, is this certificate still valid?"&lt;/em&gt; This allows us to revoke trust instantly without needing to push a full system update to the user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;The "Not Secure" warning isn't just an annoyance; it's the tip of an iceberg that keeps the internet from descending into chaos.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Trust Stores&lt;/strong&gt; act as the anchor (The "Governments").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signatures&lt;/strong&gt; prove identity, not just encryption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intermediates&lt;/strong&gt; protect the Root Keys from theft.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revocation&lt;/strong&gt; protocols try to balance security with usability.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next time you bypass that warning on localhost, remember: you're momentarily stepping out of the rigid bureaucracy that keeps your banking data safe.&lt;/p&gt;




&lt;h3&gt;
  
  
  Further Reading (References)
&lt;/h3&gt;

&lt;p&gt;For those who want to dive deeper into the protocols and standards mentioned above, here are 5 key references:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cloudflare Learning Center:&lt;/strong&gt; &lt;a href="https://www.cloudflare.com/learning/ssl/how-does-ssl-work/" rel="noopener noreferrer"&gt;How SSL/TLS Works&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;IETF RFC 5280:&lt;/strong&gt; &lt;a href="https://datatracker.ietf.org/doc/html/rfc5280" rel="noopener noreferrer"&gt;Internet X.509 Public Key Infrastructure Certificate and CRL Profile&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;IETF RFC 6960:&lt;/strong&gt; &lt;a href="https://datatracker.ietf.org/doc/html/rfc6960" rel="noopener noreferrer"&gt;X.509 Internet Public Key Infrastructure Online Certificate Status Protocol - OCSP&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Let's Encrypt:&lt;/strong&gt; &lt;a href="https://letsencrypt.org/certificates/" rel="noopener noreferrer"&gt;Chain of Trust&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mozilla Root Store Policy:&lt;/strong&gt; &lt;a href="https://wiki.mozilla.org/CA" rel="noopener noreferrer"&gt;Mozilla CA Certificate Policy&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>cybersecurity</category>
      <category>security</category>
    </item>
    <item>
      <title>We don't do that here: How to Go the Go way - Part 1</title>
      <dc:creator>medunes</dc:creator>
      <pubDate>Sat, 03 Jan 2026 21:42:16 +0000</pubDate>
      <link>https://dev.to/medunes/we-dont-do-that-here-how-to-go-the-go-way-part-1-25p7</link>
      <guid>https://dev.to/medunes/we-dont-do-that-here-how-to-go-the-go-way-part-1-25p7</guid>
      <description>&lt;p&gt;If you are like me transitioning to Go from another tech-stack or ecosystem, you might have found yourself unarmed from some of your most powerful tactics and weapons you gathered throughout your career. In simpler words, you would hit a wall on which is written: "we don't do this here :)."&lt;/p&gt;

&lt;p&gt;Some of the best practices you learned in &lt;code&gt;Java/Spring&lt;/code&gt; &lt;code&gt;OOP&lt;/code&gt; or &lt;code&gt;PHP/Symfony&lt;/code&gt; would be considered anti-patterns and red flags in Go. What to do? Not many options, indeed. You only have to "go the Go way."&lt;/p&gt;

&lt;p&gt;Here we explain a common confusion for the newcomer: the straightforward dependency injection is not (from what I could research at least) often the very welcome within the idiomatic Go republic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: There might be no single place to dictate to you "we don't do this here," but based on a couple of widely used open-source projects, language authors and maintainers, and famous blog posts you might conclude this.&lt;/p&gt;

&lt;h3&gt;
  
  
  How they do it the Go way?
&lt;/h3&gt;

&lt;p&gt;Have you ever came across this code style, and had couple question marks itching your brain for a while?&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;UserAggregator&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;profileFetcher&lt;/span&gt; &lt;span class="n"&gt;Fetcher&lt;/span&gt;
    &lt;span class="n"&gt;ordersFetcher&lt;/span&gt;  &lt;span class="n"&gt;Fetcher&lt;/span&gt;
    &lt;span class="n"&gt;timeout&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;Duration&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;         &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Option&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserAggregator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;WithTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&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;Duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Option&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&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;u&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserAggregator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;WithLogger&lt;/span&gt;&lt;span class="p"&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;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Option&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&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;u&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserAggregator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;l&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="n"&gt;NewUserAggregator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="n"&gt;Fetcher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="n"&gt;Fetcher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserAggregator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Default configuration&lt;/span&gt;
    &lt;span class="n"&gt;agg&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;UserAggregator&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;profileFetcher&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ordersFetcher&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="m"&gt;5&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;logger&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Default&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="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opt&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;opts&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agg&lt;/span&gt;&lt;span class="p"&gt;)&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;agg&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I assume you got that moment of confusion: "Isn't this just a Builder or Setters with extra steps?"&lt;/p&gt;

&lt;p&gt;You are spotting a pattern correctly. The code snippet above is known as the &lt;strong&gt;Functional Options Pattern&lt;/strong&gt; (popularized by Dave Cheney and &lt;a href="https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html" rel="noopener noreferrer"&gt;Rob Pike&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;To answer your question: &lt;strong&gt;Yes, this is analogous to the Builder pattern or Setter injection in &lt;code&gt;OOP&lt;/code&gt;, but with a twist regarding mutability and safety.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a typical &lt;code&gt;OOP&lt;/code&gt; container (such as &lt;code&gt;Spring Boot&lt;/code&gt; or &lt;code&gt;Symfony&lt;/code&gt;), you often have two phases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Instantiation:&lt;/strong&gt; The object is created (often with a no-arg constructor).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hydration (Setters):&lt;/strong&gt; The container calls &lt;code&gt;setLogger()&lt;/code&gt;, &lt;code&gt;setTimeout()&lt;/code&gt;, etc.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Go "Anti-Pattern" concern here is state validity.&lt;/strong&gt;&lt;br&gt;
If you use setters (&lt;code&gt;u.SetLogger(l)&lt;/code&gt;), you introduce a period of time where the object exists but is incomplete (e.g., the logger is nil). You also make the object mutable: anyone can change the logger halfway through the program's lifecycle.&lt;/p&gt;

&lt;p&gt;The Functional Options pattern allows you to simulate a constructor that accepts optional parameters while ensuring that &lt;strong&gt;once the function returns, the object is immutable, fully configured, and ready to use.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Alternatives: Why not &lt;code&gt;New(a, b, c)&lt;/code&gt; or &lt;code&gt;New(Config)&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;Have you just wondered why Go ditches the explicit configuration object. Actually, Go doesn't ditch it entirely (you will see &lt;code&gt;New(Config)&lt;/code&gt; in standard libraries), but Functional Options are preferred for libraries for at least the following two reasons:&lt;/p&gt;
&lt;h4&gt;
  
  
  1. The "Massive Constructor" Problem
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;New(logger, timeout, db, cache, metrics, ...)&lt;/code&gt;&lt;br&gt;
This is brittle. If you add a new dependency, you break every caller in your code-base.&lt;/p&gt;
&lt;h4&gt;
  
  
  2. The &lt;code&gt;New(Config)&lt;/code&gt; Problem
&lt;/h4&gt;

&lt;p&gt;Passing a struct, like &lt;code&gt;New(Config{Timeout: 10s})&lt;/code&gt;, seems clean, but it has hidden edges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ambiguity of Zero Values:&lt;/strong&gt; If you pass &lt;code&gt;Config{Timeout: 0}&lt;/code&gt;, does that mean "No Timeout" or "Default Timeout"? In Go, 0 is the default value for integers, It's hard to distinguish "I forgot to set this" from "I explicitly want 0". There isn't the luxury of the "nullable" parameter as you'd face in PHP as &lt;code&gt;?int&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Boilerplate:&lt;/strong&gt; If you only want to change &lt;em&gt;one&lt;/em&gt; setting, you still have to construct the struct, potentially dealing with pointers to optional fields to distinguish &lt;code&gt;nil&lt;/code&gt; from &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  The Java/OOP Equivalent (The Builder Pattern)
&lt;/h4&gt;

&lt;p&gt;In Java/Spring, to achieve the same "safety" (immutability + optional parameters), you wouldn't typically use raw Setters; you would use a &lt;strong&gt;Builder&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Usage&lt;/span&gt;
&lt;span class="nc"&gt;UserAggregator&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserAggregator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myLogger&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Inside the class&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserAggregator&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// final = immutable, like Go's approach&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;UserAggregator&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Default handling&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="c1"&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;em&gt;Comparison:&lt;/em&gt; Go achieves this using functions as first-class citizens, whereas Java requires a separate inner class (&lt;code&gt;Builder&lt;/code&gt;) to hold the temporary state.&lt;/p&gt;

&lt;h4&gt;
  
  
  The PHP/Symfony Equivalent (Options Resolver)
&lt;/h4&gt;

&lt;p&gt;In PHP, specifically Symfony, this is often handled via the &lt;code&gt;OptionsResolver&lt;/code&gt; component because PHP lacks named arguments (historically) and strict typing (historically).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Usage&lt;/span&gt;
&lt;span class="nv"&gt;$aggregator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserAggregator&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'timeout'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'logger'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$logger&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// Inside the class&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserAggregator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$options&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="nv"&gt;$resolver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OptionsResolver&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$resolver&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setDefaults&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'timeout'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="nv"&gt;$resolver&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setRequired&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'logger'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$resolver&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// ... set properties&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;em&gt;Comparison:&lt;/em&gt; This is much looser. The Go pattern provides compile-time safety (you can't pass a "&lt;em&gt;timout&lt;/em&gt;" typo option), whereas the PHP array approach needs runtime validation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;Functional Options pattern&lt;/em&gt; is idiomatic in Go because it aligns with the language's philosophy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Explicit over implicit, as this pattern often won't rely on magical DI hydration, and you witness the override with nude eyes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stay away from the &lt;a href="https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/" rel="noopener noreferrer"&gt;million dollar mistake&lt;/a&gt; and avoid &lt;code&gt;nil&lt;/code&gt; pointer exceptions that occur when you forget to call a Setter in OOP. The &lt;code&gt;New&lt;/code&gt; function guarantees a valid object.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;"Infinite" extensibility: you can add &lt;code&gt;WithDatabase()&lt;/code&gt; later without breaking existing code that calls &lt;code&gt;New()&lt;/code&gt;. The options logic is delegated and encapsulated within each corresponding function.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>go</category>
      <category>programming</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Demystifying Go Mutex: How Synchronization Works Under the Hood</title>
      <dc:creator>medunes</dc:creator>
      <pubDate>Wed, 27 Aug 2025 13:17:17 +0000</pubDate>
      <link>https://dev.to/medunes/demystifying-go-mutex-how-synchronization-works-under-the-hood-1ken</link>
      <guid>https://dev.to/medunes/demystifying-go-mutex-how-synchronization-works-under-the-hood-1ken</guid>
      <description>&lt;p&gt;Have you ever wondered what &lt;em&gt;really&lt;/em&gt; happens behind the scenes when you use a &lt;code&gt;sync.Mutex&lt;/code&gt; in your Go code? It feels like magic: you call &lt;code&gt;Lock()&lt;/code&gt;, and somehow, all other goroutines patiently wait their turn. But it's not magic, it's a clever collaboration between the Go runtime scheduler and the CPU itself.&lt;/p&gt;

&lt;p&gt;Let's peel back the layers and see how this elegant dance of synchronization actually works.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Code We'll Analyze
&lt;/h2&gt;

&lt;p&gt;To ground our discussion, we'll use this classic concurrent counter program. The goal is simple: have 10,000 goroutines all increment a single shared variable &lt;code&gt;x&lt;/code&gt;. Without a mutex, this would be a chaotic race condition. With it, the final result is a perfect 10,000.&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;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"sync"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;m&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="c"&gt;// inc increments our shared counter safely.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;inc&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="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;m&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;x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
    &lt;span class="n"&gt;m&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="p"&gt;}&lt;/span&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="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;wg&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="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="m"&gt;10000&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;i&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="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;inc&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;wg&lt;/span&gt;&lt;span class="p"&gt;)&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;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;"Final value of x:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&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;The &lt;code&gt;sync.Mutex&lt;/code&gt; is the hero here. But how does it enforce this order at the machine level?&lt;/p&gt;




&lt;h2&gt;
  
  
  Busting a Common Myth: It's Not Memory Protection
&lt;/h2&gt;

&lt;p&gt;First, let's clear up a common misconception. A &lt;code&gt;Mutex&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; work by telling the hardware to protect a region of memory. The CPU has no idea what a mutex is. The protection is a &lt;strong&gt;cooperative software agreement&lt;/strong&gt;. Any buggy code could technically ignore the lock and access &lt;code&gt;x&lt;/code&gt; directly, causing a race condition.&lt;/p&gt;

&lt;p&gt;The "magic" isn't in hardware memory barriers, but in a sophisticated two-phase strategy built on a single, powerful CPU feature: &lt;strong&gt;atomic operations&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Magic: A Two-Phase Strategy
&lt;/h2&gt;

&lt;p&gt;Go's mutex is designed for high performance. It assumes that locks are usually "uncontended" (not held by another goroutine). So, it uses a "spin-then-park" approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: The Fast Path (Atomic Optimism)
&lt;/h3&gt;

&lt;p&gt;When a goroutine calls &lt;code&gt;m.Lock()&lt;/code&gt;, it first tries the fast path.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;It uses an atomic CPU instruction&lt;/strong&gt;, like &lt;strong&gt;Compare-And-Swap (CAS)&lt;/strong&gt;. On x86, this might be the &lt;code&gt;LOCK CMPXCHG&lt;/code&gt; instruction.&lt;/li&gt;
&lt;li&gt; This instruction does one indivisible thing: "Check the mutex's state variable in memory. If it's &lt;code&gt;0&lt;/code&gt; (unlocked), set it to &lt;code&gt;1&lt;/code&gt; (locked) and tell me I succeeded."&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;If it succeeds&lt;/strong&gt;, the goroutine has acquired the lock in just a few nanoseconds without any help from the OS kernel. It continues executing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the lock is already held, the CAS fails. But the goroutine doesn't give up immediately. It &lt;strong&gt;"spins"&lt;/strong&gt; for a very short time, retrying the atomic CAS in a tight loop. This is an optimistic bet that the lock will be released very soon, avoiding the much higher cost of involving the scheduler.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: The Slow Path (A Little Nap)
&lt;/h3&gt;

&lt;p&gt;If spinning for a few cycles doesn't work, the goroutine gives up on the fast path and takes the slow path.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; It adds itself to a &lt;strong&gt;wait queue&lt;/strong&gt; specific to that mutex.&lt;/li&gt;
&lt;li&gt; It calls into the Go scheduler, which &lt;strong&gt;parks&lt;/strong&gt; the goroutine.&lt;/li&gt;
&lt;li&gt; "Parking" means the goroutine is put to sleep. It's removed from the runnable queue and won't consume any CPU time until it's woken up. The OS thread is now free to run a different, ready-to-go goroutine.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is far more efficient than the goroutine just "skipping its round" and trying again later. It yields the CPU completely.&lt;/p&gt;




&lt;h2&gt;
  
  
  Visualizing the Goroutine Dance
&lt;/h2&gt;

&lt;p&gt;This diagram illustrates the lifecycle of goroutines interacting with the mutex.&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%2Fqw23q4azcl6d774nki53.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%2Fqw23q4azcl6d774nki53.png" alt=" " width="800" height="803"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  A Step-by-Step Walkthrough
&lt;/h2&gt;

&lt;p&gt;Let's trace our 10,000 goroutines with this new understanding.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;G1 Takes the Lock (Fast Path):&lt;/strong&gt; The scheduler picks the first goroutine, &lt;code&gt;G1&lt;/code&gt;. It calls &lt;code&gt;m.Lock()&lt;/code&gt;, executes a successful CAS, and acquires the lock. It now begins to execute &lt;code&gt;x++&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;G2 Tries and Parks (Slow Path):&lt;/strong&gt; Before &lt;code&gt;G1&lt;/code&gt; finishes, the scheduler might run &lt;code&gt;G2&lt;/code&gt;. &lt;code&gt;G2&lt;/code&gt; calls &lt;code&gt;m.Lock()&lt;/code&gt;, but its atomic CAS fails because the lock state is &lt;code&gt;1&lt;/code&gt;. After a brief spin, &lt;code&gt;G2&lt;/code&gt; gives up, adds itself to the mutex's wait queue, and the scheduler parks it. &lt;code&gt;G2&lt;/code&gt; is now asleep.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;G3, G4... also Park:&lt;/strong&gt; The same thing happens to &lt;code&gt;G3&lt;/code&gt;, &lt;code&gt;G4&lt;/code&gt;, and any other goroutine that runs before &lt;code&gt;G1&lt;/code&gt; is done. They all try the lock, fail, and end up in the wait queue, sleeping peacefully.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;G1 Unlocks and Wakes Another:&lt;/strong&gt; Eventually, &lt;code&gt;G1&lt;/code&gt; gets CPU time again. It finishes &lt;code&gt;x++&lt;/code&gt; and calls &lt;code&gt;m.Unlock()&lt;/code&gt;. The &lt;code&gt;Unlock&lt;/code&gt; function atomically sets the lock state back to &lt;code&gt;0&lt;/code&gt; and, crucially, notifies the scheduler that there are goroutines in the wait queue.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;G2 Wakes Up:&lt;/strong&gt; The scheduler takes one goroutine from the wait queue,say, &lt;code&gt;G2&lt;/code&gt;, and moves it back to the runnable queue. When &lt;code&gt;G2&lt;/code&gt; is scheduled next, it will retry its &lt;code&gt;m.Lock()&lt;/code&gt; call, which will now succeed on the &lt;strong&gt;fast path&lt;/strong&gt;. It will then increment &lt;code&gt;x&lt;/code&gt;, unlock the mutex, and wake up the next in line.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This orderly process continues until all 10,000 goroutines have had their turn.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Simple Analogy: The Single Bathroom Key
&lt;/h2&gt;

&lt;p&gt;Think of the critical section (&lt;code&gt;x++&lt;/code&gt;) as a public bathroom that can only hold one person.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Mutex (&lt;code&gt;m&lt;/code&gt;)&lt;/strong&gt; is the single key to the bathroom.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast Path:&lt;/strong&gt; You walk up and the door is open. You take the key, go in, and lock it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slow Path:&lt;/strong&gt; You arrive and the door is locked. You don't just walk away and come back randomly. You &lt;strong&gt;form an orderly queue&lt;/strong&gt; and wait. When the person inside comes out, they give the key directly to the &lt;em&gt;first person in line&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This queueing is exactly what the Go scheduler does, ensuring fairness and efficiency.&lt;/p&gt;

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

&lt;p&gt;The beauty of &lt;code&gt;sync.Mutex&lt;/code&gt; is this seamless collaboration. The &lt;strong&gt;CPU&lt;/strong&gt; provides the foundational guarantee with atomic operations, ensuring that changing the "locked" flag is an indivisible action. The &lt;strong&gt;Go Scheduler&lt;/strong&gt; provides the intelligence, efficiently parking and waking goroutines so that no CPU time is wasted on waiting.&lt;/p&gt;

&lt;p&gt;Next time you type &lt;code&gt;m.Lock()&lt;/code&gt;, you can appreciate the sophisticated dance happening underneath, a dance that makes your concurrent programs correct, efficient, and robust.&lt;/p&gt;




&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Go Scheduler:&lt;/strong&gt; &lt;a href="https://www.google.com/search?q=https://go.dev/blog/go-scheduler" rel="noopener noreferrer"&gt;A deep dive into the Go scheduler from the official blog.&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Goroutines:&lt;/strong&gt; &lt;a href="https://go.dev/tour/concurrency/1" rel="noopener noreferrer"&gt;The official Go Tour explanation of goroutines.&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic Operations (CAS):&lt;/strong&gt; &lt;a href="https://en.wikipedia.org/wiki/Compare-and-swap" rel="noopener noreferrer"&gt;A great overview of Compare-And-Swap.&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go's Mutex Implementation:&lt;/strong&gt; &lt;a href="https://victoriametrics.com/blog/go-sync-mutex/" rel="noopener noreferrer"&gt;Go sync.Mutex: Normal and Starvation Mode
&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>distributedsystems</category>
      <category>cpp</category>
      <category>rust</category>
    </item>
    <item>
      <title>Creating a REST API in Go with Gin: A Pragmatic, Spec-First Guide</title>
      <dc:creator>medunes</dc:creator>
      <pubDate>Wed, 06 Aug 2025 17:16:55 +0000</pubDate>
      <link>https://dev.to/medunes/creating-a-rest-api-in-go-with-gin-a-pragmatic-spec-first-guide-13de</link>
      <guid>https://dev.to/medunes/creating-a-rest-api-in-go-with-gin-a-pragmatic-spec-first-guide-13de</guid>
      <description>&lt;p&gt;Let's cut to the chase. You need to build a REST API in Go. You want it to be modern, maintainable, and built following best practices: this is a hands-on guide to doing it the right way.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: Specification Hell or Heaven?
&lt;/h3&gt;

&lt;p&gt;So, you're starting a greenfield project. The eternal question arises: do you write the code first and then generate the API specification (like OpenAPI/Swagger)? Or do you write the spec first and then generate the code?&lt;/p&gt;

&lt;p&gt;This isn't just a philosophical debate. It has real-world consequences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code-First:&lt;/strong&gt; You move fast initially, but the spec often becomes an afterthought. It drifts from the actual implementation, leading to confused frontend teams, incorrect documentation, and integration nightmares.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spec-First:&lt;/strong&gt; This seems slower upfront, but it forces you to think through your API design. The spec becomes the &lt;strong&gt;source of truth&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Our Approach:&lt;/strong&gt; We're going all-in on &lt;strong&gt;spec-first&lt;/strong&gt;. Why? Because it unlocks massive benefits. By creating an &lt;code&gt;openapi.yaml&lt;/code&gt; file first, we establish a contract. Other teams (frontend, QA, other backend services) can immediately use this contract to generate mock servers and start their work. They aren't blocked by our implementation. We are all working in parallel, protected by a clear, agreed-upon interface.&lt;/p&gt;

&lt;p&gt;This also sets us up to leverage powerful code generators. We can automatically create server stubs, data models, and even client-side code, all from that single YAML file. This isn't just efficient; it's a key to long-term maintainability.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Plan: Spec -&amp;gt; Generate -&amp;gt; Implement
&lt;/h3&gt;

&lt;p&gt;Here’s how we're going to tackle this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Define the API Contract:&lt;/strong&gt; We'll write an &lt;code&gt;openapi.yaml&lt;/code&gt; file. This is our single source of truth.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Structure the Project:&lt;/strong&gt; We'll lay out our Go project in a clean, idiomatic way.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Generate Code:&lt;/strong&gt; Using &lt;code&gt;oapi-codegen&lt;/code&gt;, we'll generate the server scaffolding and data types directly from our YAML file.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Implement Business Logic:&lt;/strong&gt; We'll write the actual logic for our API endpoints.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Automate with Make:&lt;/strong&gt; We'll use a &lt;code&gt;Makefile&lt;/code&gt; to streamline the code generation process, making it a repeatable, one-command step.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Project Structure: Go Standard Layout
&lt;/h3&gt;

&lt;p&gt;A good project structure is crucial for maintainability. We'll follow a layout that is common in the Go community and promotes a clean separation of concerns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/my-api
├── cmd/
│   └── api/
│       └── main.go         # Entry point of our application
├── docs/
│   └── openapi.yaml        # Our API specification (source of truth)
├── internal/
│   ├── api/
│   │   ├── openapi_server.gen.go  # Generated server code
│   │   └── openapi_types.gen.go   # Generated type definitions
│   └── handler/
│       └── handler.go      # Our business logic implementation
├── go.mod
├── go.sum
└── Makefile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cmd/api/main.go&lt;/code&gt;&lt;/strong&gt;: This is where our application starts. Its job is to initialize the database connections, set up the Gin router, and wire everything together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;docs/openapi.yaml&lt;/code&gt;&lt;/strong&gt;: The heart of our spec-first approach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;internal/&lt;/code&gt;&lt;/strong&gt;: This is a special directory in Go. Code inside &lt;code&gt;internal&lt;/code&gt; can only be imported by code within the same project, preventing other projects from accidentally depending on our internal logic.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;api/&lt;/code&gt;&lt;/strong&gt;: This package will hold the code generated by &lt;code&gt;oapi-codegen&lt;/code&gt;. We keep it separate to make it clear what is machine-generated vs. hand-written.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;handler/&lt;/code&gt;&lt;/strong&gt;: This is where we'll write the actual implementation for our API endpoints (the "business logic").&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;&lt;code&gt;Makefile&lt;/code&gt;&lt;/strong&gt;: A simple script to automate our development tasks, like code generation.&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  Step 1: Define the API with OpenAPI 3
&lt;/h3&gt;

&lt;p&gt;First, let's create our API specification in &lt;code&gt;docs/openapi.yaml&lt;/code&gt;. We'll define two simple endpoints: one to get a list of users and another to get a list of products.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docs/openapi.yaml&lt;/span&gt;
&lt;span class="na"&gt;openapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.0.3&lt;/span&gt;
&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Simple&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;API"&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0"&lt;/span&gt;
&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/users&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Get&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;list&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;users"&lt;/span&gt;
      &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;getUsers"&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;200'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;list&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;users"&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;array&lt;/span&gt;
                &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/User'&lt;/span&gt;
  &lt;span class="na"&gt;/products&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Get&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;list&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;products"&lt;/span&gt;
      &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;getProducts"&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;200'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;list&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;products"&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;array&lt;/span&gt;
                &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/Product'&lt;/span&gt;

&lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schemas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;User&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;integer&lt;/span&gt;
          &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;int64&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
    &lt;span class="na"&gt;Product&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;sku&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;number&lt;/span&gt;
          &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;float&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 2: Automate Code Generation with a &lt;code&gt;Makefile&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Now for the magic. We'll use &lt;code&gt;oapi-codegen&lt;/code&gt; to read our YAML file and generate Go code. With Go 1.24+, we can manage tools like this directly in our &lt;code&gt;go.mod&lt;/code&gt; file, which is fantastic for ensuring consistent builds.&lt;/p&gt;

&lt;p&gt;First, add the tool to your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get &lt;span class="nt"&gt;-u&lt;/span&gt; github.com/gin-gonic/gin
go get &lt;span class="nt"&gt;-tool&lt;/span&gt; github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command does two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Adds &lt;code&gt;gin&lt;/code&gt; as a project dependency.&lt;/li&gt;
&lt;li&gt; Adds &lt;code&gt;oapi-codegen&lt;/code&gt; as a tracked tool in &lt;code&gt;go.mod&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, create the &lt;code&gt;Makefile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="c"&gt;# Makefile
&lt;/span&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gen-code&lt;/span&gt;

&lt;span class="nl"&gt;gen-code&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Generating OpenAPI server and types ---"&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;go tool oapi-codegen &lt;span class="nt"&gt;--config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;codegen.yaml docs/openapi.yaml

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;tidy&lt;/span&gt;
&lt;span class="nl"&gt;tidy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;go mod tidy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more complex projects, using a configuration file for &lt;code&gt;oapi-codegen&lt;/code&gt; is cleaner. Create &lt;code&gt;codegen.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# codegen.yaml&lt;/span&gt;
&lt;span class="na"&gt;package&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt;
&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;types&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gin&lt;/span&gt;
&lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;internal/api/openapi.gen.go&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration tells the tool to generate the &lt;code&gt;types&lt;/code&gt; and &lt;code&gt;gin&lt;/code&gt; server code into a single file. Now, running &lt;code&gt;make gen-code&lt;/code&gt; will generate our server interface and all the required structs into &lt;code&gt;internal/api/openapi.gen.go&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3: Implement the Business Logic
&lt;/h3&gt;

&lt;p&gt;The generated code provides us with a &lt;code&gt;ServerInterface&lt;/code&gt;. Our job is to create a struct that implements this interface. This is where our actual business logic lives.&lt;/p&gt;

&lt;p&gt;Create the file &lt;code&gt;internal/handler/handler.go&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="c"&gt;// internal/handler/handler.go&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;

    &lt;span class="s"&gt;"my-api/internal/api"&lt;/span&gt; &lt;span class="c"&gt;// Import the generated package&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/gin-gonic/gin"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Server implements the generated ServerInterface.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c"&gt;// Make sure we conform to the interface&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServerInterface&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// getUsers implements the business logic for the /users endpoint.&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;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetUsers&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;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&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="s"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Id&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="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Bob"&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;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// getProducts implements the business logic for the /products endpoint.&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;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetProducts&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;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Sku&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"A123"&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="s"&gt;"Laptop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Price&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1200.50&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Sku&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"B456"&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="s"&gt;"Mouse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Price&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;25.00&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;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;products&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;Notice how we're using the &lt;code&gt;api.User&lt;/code&gt; and &lt;code&gt;api.Product&lt;/code&gt; structs that were generated for us. If we change the spec and regenerate, the compiler will tell us exactly what we need to update in our handler. This is a remarkable win for maintainability.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 4: Wire It All Together
&lt;/h3&gt;

&lt;p&gt;Finally, let's write our &lt;code&gt;main.go&lt;/code&gt; to start the server.&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;// cmd/api/main.go&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"my-api/internal/api"&lt;/span&gt;
    &lt;span class="s"&gt;"my-api/internal/handler"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/gin-gonic/gin"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&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="c"&gt;// Create our handler which satisfies the generated interface&lt;/span&gt;
    &lt;span class="n"&gt;myHandler&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;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c"&gt;// Set up the Gin router&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Use the generated RegisterHandlers function to wire up our handlers&lt;/span&gt;
    &lt;span class="c"&gt;// to the Gin router.&lt;/span&gt;
    &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterHandlers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;myHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Start the server&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&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;The &lt;code&gt;api.RegisterHandlers&lt;/code&gt; function is another powerful piece of the generated code. It takes care of all the boilerplate for routing requests to the correct handler methods.&lt;/p&gt;




&lt;h3&gt;
  
  
  Conclusion: A Maintainable, Scalable Foundation
&lt;/h3&gt;

&lt;p&gt;And that's it! You now have a fully functional, spec-driven REST API.&lt;/p&gt;

&lt;p&gt;Let's recap the benefits of this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single Source of Truth:&lt;/strong&gt; &lt;code&gt;openapi.yaml&lt;/code&gt; is the contract. All code is generated or validated against it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallel Development:&lt;/strong&gt; Frontend and backend teams can work simultaneously, unlocked by the API spec.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type Safety:&lt;/strong&gt; Go's strong type system, combined with code generation, catches errors at compile time, not runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Boilerplate:&lt;/strong&gt; &lt;code&gt;oapi-codegen&lt;/code&gt; handles the tedious work of routing and data model creation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency:&lt;/strong&gt; The &lt;code&gt;Makefile&lt;/code&gt; and &lt;code&gt;go tool&lt;/code&gt; support ensure that every developer on your team generates code the exact same way.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This spec-first methodology provides a robust foundation for building APIs in Go that are not only fast and efficient but also a pleasure to maintain and scale over time. Happy coding!&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://go.dev/doc/modules/layout" rel="noopener noreferrer"&gt;Go's Standard Project Layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/oapi-codegen/oapi-codegen" rel="noopener noreferrer"&gt;oapi-codegen GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gin-gonic.com/" rel="noopener noreferrer"&gt;Gin Web Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.openapis.org/" rel="noopener noreferrer"&gt;OpenAPI Specification&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>go</category>
      <category>devops</category>
    </item>
    <item>
      <title>Build C Projects Like a Pro: A Guide to Idiomatic Makefiles</title>
      <dc:creator>medunes</dc:creator>
      <pubDate>Tue, 05 Aug 2025 07:16:35 +0000</pubDate>
      <link>https://dev.to/medunes/build-c-projects-like-a-pro-a-guide-to-idiomatic-makefiles-53b6</link>
      <guid>https://dev.to/medunes/build-c-projects-like-a-pro-a-guide-to-idiomatic-makefiles-53b6</guid>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If you've ever worked with C, you've likely encountered the rite of passage that is the &lt;code&gt;Makefile&lt;/code&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For many, it's a cryptic, frustrating file full of strange symbols and tab-sensitive lines. But what if you could not only understand it but wield it to create clean, professional, and scalable C projects?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This guide is for you. We'll move beyond simple, single-file compilation and build a complete, idiomatic &lt;code&gt;Makefile&lt;/code&gt; from scratch. We'll break down every line and concept so you understand not just &lt;em&gt;what&lt;/em&gt; it does, but &lt;em&gt;why&lt;/em&gt; it's a best practice. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;By the end, you'll have a powerful template you can adapt for all your future C projects.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Project: A Simple Frequency Counter
&lt;/h2&gt;

&lt;p&gt;To make this practical, we'll build a real command-line tool. Our program, &lt;code&gt;freq-counter&lt;/code&gt;, will take a filename as an argument and count the frequency of each alphabet character in that file.&lt;/p&gt;

&lt;p&gt;Our project will be structured professionally with separate directories for source code, headers, and tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;main.c&lt;/code&gt;: Handles command-line arguments and file I/O.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;freq.c&lt;/code&gt;: Contains the core logic for counting character frequencies.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;freq.h&lt;/code&gt;: The header file declaring our frequency-counting functions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test_freq.c&lt;/code&gt;: A test file to verify that our counting logic is correct.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Makefile: An Overview
&lt;/h2&gt;

&lt;p&gt;We will create a &lt;code&gt;Makefile&lt;/code&gt; that handles everything:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Configuration:&lt;/strong&gt; Easily change project names, directories, and compiler flags.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Discovery:&lt;/strong&gt; Automatically find all &lt;code&gt;.c&lt;/code&gt; source files without manual updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build Targets:&lt;/strong&gt; Compile the main program and a separate test executable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle Management:&lt;/strong&gt; Install, uninstall, and clean the project with simple commands.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Let's dive in.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Makefile Breakdown
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Part 1: Configuration
&lt;/h3&gt;

&lt;p&gt;This first section contains all the variables we need. Centralizing them here makes the project easy to configure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="c"&gt;# ---- configuration ---------------------------------------------------------
&lt;/span&gt;&lt;span class="nv"&gt;BIN&lt;/span&gt;       &lt;span class="o"&gt;:=&lt;/span&gt; freq-counter
&lt;span class="nv"&gt;PREFIX&lt;/span&gt;    &lt;span class="o"&gt;?=&lt;/span&gt; /usr/local

&lt;span class="nv"&gt;SRC_DIR&lt;/span&gt;   &lt;span class="o"&gt;:=&lt;/span&gt; src
&lt;span class="nv"&gt;INC_DIR&lt;/span&gt;   &lt;span class="o"&gt;:=&lt;/span&gt; include
&lt;span class="nv"&gt;TEST_DIR&lt;/span&gt;  &lt;span class="o"&gt;:=&lt;/span&gt; tests

&lt;span class="nv"&gt;SRCS&lt;/span&gt;      &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;wildcard &lt;span class="p"&gt;$(&lt;/span&gt;SRC_DIR&lt;span class="p"&gt;)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.c&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;OBJS&lt;/span&gt;      &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;SRCS:.c&lt;span class="o"&gt;=&lt;/span&gt;.o&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;TEST_SRCS&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;wildcard &lt;span class="p"&gt;$(&lt;/span&gt;TEST_DIR&lt;span class="p"&gt;)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.c&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;TEST_OBJS&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;TEST_SRCS:.c&lt;span class="o"&gt;=&lt;/span&gt;.o&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;CC&lt;/span&gt;        &lt;span class="o"&gt;?=&lt;/span&gt; cc
&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;    &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nt"&gt;-std&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;c11 &lt;span class="nt"&gt;-Wall&lt;/span&gt; &lt;span class="nt"&gt;-Wextra&lt;/span&gt; &lt;span class="nt"&gt;-I&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;INC_DIR&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-O2&lt;/span&gt;
&lt;span class="nv"&gt;LDLIBS&lt;/span&gt;    &lt;span class="o"&gt;:=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;BIN := freq-counter&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; Assigns the name of our final program, "freq-counter," to the &lt;code&gt;BIN&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it does this (The Idiom):&lt;/strong&gt; It defines the executable's name in one place. If you want to rename your project, you only need to change this line. &lt;code&gt;:=&lt;/code&gt; assigns the value immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your Takeaway:&lt;/strong&gt; Always define output filenames as variables at the top of your file.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;PREFIX ?= /usr/local&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; Conditionally assigns &lt;code&gt;/usr/local&lt;/code&gt; to the &lt;code&gt;PREFIX&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it does this (The Idiom):&lt;/strong&gt; The &lt;code&gt;?=&lt;/code&gt; operator means "assign this value ONLY if &lt;code&gt;PREFIX&lt;/code&gt; is not already defined." This is a powerful idiom that allows users to easily override the installation path from the command line (e.g., &lt;code&gt;make install PREFIX=/opt/custom&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your Takeaway:&lt;/strong&gt; Use &lt;code&gt;?=&lt;/code&gt; for any variable you want to allow a user to easily customize, especially installation paths.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;SRC_DIR&lt;/code&gt;, &lt;code&gt;INC_DIR&lt;/code&gt;, &lt;code&gt;TEST_DIR&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; Defines variables for the names of our source, include, and test directories.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it does this (The Idiom):&lt;/strong&gt; This avoids hardcoding directory names throughout the &lt;code&gt;Makefile&lt;/code&gt;, making the structure configurable and the script cleaner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your Takeaway:&lt;/strong&gt; Define your directory layout as variables.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;SRCS := $(wildcard $(SRC_DIR)/*.c)&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; Creates a list of all files ending in &lt;code&gt;.c&lt;/code&gt; inside the &lt;code&gt;src&lt;/code&gt; directory and assigns this list to the &lt;code&gt;SRCS&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it does this (The Idiom):&lt;/strong&gt; &lt;code&gt;wildcard&lt;/code&gt; is a &lt;code&gt;make&lt;/code&gt; function that expands shell globbing patterns. This is the idiomatic way to get a list of source files. It automatically finds new &lt;code&gt;.c&lt;/code&gt; files as you add them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your Takeaway:&lt;/strong&gt; This is the best practice for discovering source files.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;OBJS := $(SRCS:.c=.o)&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; It takes the list of source files (e.g., &lt;code&gt;src/main.c src/freq.c&lt;/code&gt;) and creates a corresponding list of object file names by replacing the &lt;code&gt;.c&lt;/code&gt; suffix with &lt;code&gt;.o&lt;/code&gt; (e.g., &lt;code&gt;src/main.o src/freq.o&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it does this (The Idiom):&lt;/strong&gt; This is a "substitution reference," a classic &lt;code&gt;make&lt;/code&gt; pattern for generating target file lists from source lists. It's concise and powerful.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your Takeaway:&lt;/strong&gt; This is a very common and efficient way to define your object files.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;TEST_SRCS&lt;/code&gt; and &lt;code&gt;TEST_OBJS&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; Exactly the same logic as above, but for the files in the &lt;code&gt;tests&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it does this (The Idiom):&lt;/strong&gt; It cleanly separates the main application build from the test build.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your Takeaway:&lt;/strong&gt; Keep your test sources and objects separate from your main application sources and objects.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;CC ?= cc&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; Conditionally sets the C compiler to &lt;code&gt;cc&lt;/code&gt; (the system's default C compiler).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it does this (The Idiom):&lt;/strong&gt; This allows a user to easily specify a different compiler (e.g., &lt;code&gt;make CC=clang&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your Takeaway:&lt;/strong&gt; Always make the compiler configurable.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;CFLAGS := -std=c11 -Wall -Wextra -I$(INC_DIR) -O2&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; Defines the flags to pass to the compiler: use the C11 standard, enable all and extra warnings, look for headers in our &lt;code&gt;include&lt;/code&gt; directory, and apply level-2 optimizations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it does this (The Idiom):&lt;/strong&gt; It centralizes compiler options for consistency and quality control. &lt;code&gt;-Wall -Wextra&lt;/code&gt; is a non-negotiable best practice for catching bugs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your Takeaway:&lt;/strong&gt; Centralize your compiler flags and always compile with warnings enabled.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;LDLIBS :=&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; Defines the libraries to link against. It's empty for this project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it does this (The Idiom):&lt;/strong&gt; This is a placeholder. If your project needed the math library, you would set it to &lt;code&gt;LDLIBS := -lm&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your Takeaway:&lt;/strong&gt; Use &lt;code&gt;LDLIBS&lt;/code&gt; to specify linker dependencies.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Part 2: Build Targets
&lt;/h3&gt;

&lt;p&gt;This section defines what &lt;code&gt;make&lt;/code&gt; can actually &lt;em&gt;do&lt;/em&gt;. These are the commands you'll run, like &lt;code&gt;make&lt;/code&gt; or &lt;code&gt;make test&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="c"&gt;# ---- build targets ---------------------------------------------------------
&lt;/span&gt;&lt;span class="nl"&gt;$(BIN)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;$(OBJS)&lt;/span&gt;
    &lt;span class="p"&gt;$(&lt;/span&gt;CC&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$^&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;$@&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;LDLIBS&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nl"&gt;%.o&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;%.c&lt;/span&gt;
    &lt;span class="p"&gt;$(&lt;/span&gt;CC&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;CFLAGS&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;$&amp;lt;&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;$@&lt;/span&gt;

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;test test-bin install uninstall clean&lt;/span&gt;

&lt;span class="nl"&gt;test-bin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;$(TEST_OBJS) $(OBJS)&lt;/span&gt;
    &lt;span class="p"&gt;$(&lt;/span&gt;CC&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;TEST_OBJS&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;filter-out &lt;span class="p"&gt;$(&lt;/span&gt;SRC_DIR&lt;span class="p"&gt;)&lt;/span&gt;/main.o,&lt;span class="p"&gt;$(&lt;/span&gt;OBJS&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;TEST_DIR&lt;span class="p"&gt;)&lt;/span&gt;/test_runner

&lt;span class="nl"&gt;test&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;test-bin&lt;/span&gt;
    &lt;span class="p"&gt;$(&lt;/span&gt;TEST_DIR&lt;span class="p"&gt;)&lt;/span&gt;/run.sh

&lt;span class="nl"&gt;install&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;$(BIN)&lt;/span&gt;
    &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-Dm755&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;BIN&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;DESTDIR&lt;span class="p"&gt;)$(&lt;/span&gt;PREFIX&lt;span class="p"&gt;)&lt;/span&gt;/bin/&lt;span class="p"&gt;$(&lt;/span&gt;BIN&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nl"&gt;uninstall&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;DESTDIR&lt;span class="p"&gt;)$(&lt;/span&gt;PREFIX&lt;span class="p"&gt;)&lt;/span&gt;/bin/&lt;span class="p"&gt;$(&lt;/span&gt;BIN&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nl"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;OBJS&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;TEST_OBJS&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;BIN&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;TEST_DIR&lt;span class="p"&gt;)&lt;/span&gt;/test_runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Main Executable Rule: &lt;code&gt;$(BIN): $(OBJS)&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; This defines our main target. It states that the final executable (&lt;code&gt;freq-counter&lt;/code&gt;) depends on all the object files (&lt;code&gt;.o&lt;/code&gt;) listed in &lt;code&gt;$(OBJS)&lt;/code&gt;. The recipe below it links those object files together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it does this (The Idiom):&lt;/strong&gt; It separates the final &lt;strong&gt;linking&lt;/strong&gt; step from the intermediate &lt;strong&gt;compilation&lt;/strong&gt; steps. The automatic variables &lt;code&gt;$^&lt;/code&gt; (all prerequisites) and &lt;code&gt;$@&lt;/code&gt; (the target) keep the rule generic and clean.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your Takeaway:&lt;/strong&gt; Your primary build rule should depend on all object files and perform the final linking.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;The Pattern Rule: &lt;code&gt;%.o: %.c&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; This is the powerful engine of our &lt;code&gt;Makefile&lt;/code&gt;. It's a pattern that tells &lt;code&gt;make&lt;/code&gt;: "To create &lt;em&gt;any&lt;/em&gt; file that ends in &lt;code&gt;.o&lt;/code&gt;, find the corresponding file that ends in &lt;code&gt;.c&lt;/code&gt; and run this recipe."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it does this (The Idiom):&lt;/strong&gt; This single, generic rule handles the compilation for every &lt;code&gt;.c&lt;/code&gt; file in our project. You never need to write another compilation rule. The &lt;code&gt;-c&lt;/code&gt; flag tells the compiler to compile only and not link.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your Takeaway:&lt;/strong&gt; This pattern rule is the standard, most efficient way to handle C compilation in a &lt;code&gt;Makefile&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;The Phony Targets: &lt;code&gt;.PHONY: ...&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; It declares that targets like &lt;code&gt;clean&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, and &lt;code&gt;install&lt;/code&gt; are "phony," meaning they don't produce an actual file with that name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it does this (The Idiom):&lt;/strong&gt; This prevents conflicts if you ever had a file named &lt;code&gt;test&lt;/code&gt; and improves performance slightly. It's a declaration of intent and a core best practice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your Takeaway:&lt;/strong&gt; Always declare non-file targets as &lt;code&gt;.PHONY&lt;/code&gt; at the top of your targets section.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;The Test Build Rule: &lt;code&gt;test-bin: ...&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; It builds a special test executable named &lt;code&gt;test_runner&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it does this (The Idiom):&lt;/strong&gt; It links the test code with your application code, but with a crucial exception. The &lt;code&gt;$(filter-out $(SRC_DIR)/main.o,$(OBJS))&lt;/code&gt; function removes your application's &lt;code&gt;main.o&lt;/code&gt; file from the list before linking. This is necessary because your test code has its &lt;em&gt;own&lt;/em&gt; &lt;code&gt;main()&lt;/code&gt; function, and you can't have two in one program.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your Takeaway:&lt;/strong&gt; Use the &lt;code&gt;filter-out&lt;/code&gt; pattern to exclude your main application's &lt;code&gt;main.c&lt;/code&gt; when building a test runner.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;The Test Run Rule: &lt;code&gt;test: test-bin&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; Defines an easy-to-use &lt;code&gt;test&lt;/code&gt; target. It first ensures the &lt;code&gt;test-bin&lt;/code&gt; executable is built and then runs an external shell script to perform the actual tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it does this (The Idiom):&lt;/strong&gt; It cleanly separates the concern of &lt;em&gt;building&lt;/em&gt; the test executable from &lt;em&gt;running&lt;/em&gt; the tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your Takeaway:&lt;/strong&gt; A &lt;code&gt;make test&lt;/code&gt; command should be simple for the user; hide the complexity in dependent targets and scripts.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;The Install/Uninstall Rules: &lt;code&gt;install:&lt;/code&gt; and &lt;code&gt;uninstall:&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; The &lt;code&gt;install&lt;/code&gt; target copies the compiled program (&lt;code&gt;freq-counter&lt;/code&gt;) into a system-wide directory (&lt;code&gt;/usr/local/bin&lt;/code&gt;). The &lt;code&gt;uninstall&lt;/code&gt; target removes it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it does this (The Idiom):&lt;/strong&gt; It provides a standard way to make the tool available to all users on a system. Using the &lt;code&gt;install&lt;/code&gt; command is better than &lt;code&gt;cp&lt;/code&gt; because it can set permissions (&lt;code&gt;-m755&lt;/code&gt;) and create parent directories (&lt;code&gt;-D&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your Takeaway:&lt;/strong&gt; If you provide an &lt;code&gt;install&lt;/code&gt; target, always provide &lt;code&gt;uninstall&lt;/code&gt;. Use the &lt;code&gt;install&lt;/code&gt; command for professional-grade installations.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;The Clean Rule: &lt;code&gt;clean:&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; Removes all files generated during the build process (all &lt;code&gt;.o&lt;/code&gt; files and both executables).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it does this (The Idiom):&lt;/strong&gt; This allows you to guarantee a fresh build from a clean state, which is essential for debugging and release packaging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your Takeaway:&lt;/strong&gt; A &lt;code&gt;clean&lt;/code&gt; target is a mandatory part of any serious &lt;code&gt;Makefile&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  Final Project Structure
&lt;/h3&gt;

&lt;p&gt;After creating all the files, your project directory should look 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;&lt;span class="nb"&gt;.&lt;/span&gt;
├── Makefile
├── include
│   └── freq.h
├── src
│   ├── freq.c
│   └── main.c
└── tests
    ├── run.sh
    └── test_freq.c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Full Code Listing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In this &lt;a href="https://github.com/MedUnes/c-build" rel="noopener noreferrer"&gt;Github repository&lt;/a&gt; you can find the complete code for all the files so you can build and run this project yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bonus&lt;/strong&gt; The project includes a CI/CD setup based on Github actions, which automatically handles the build, test and even release to a binary for further download. &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>c</category>
      <category>linux</category>
      <category>sre</category>
    </item>
    <item>
      <title>Stop Saying "It Just Works." Save Your Credibility as a Senior and Say This Instead</title>
      <dc:creator>medunes</dc:creator>
      <pubDate>Fri, 25 Jul 2025 14:58:02 +0000</pubDate>
      <link>https://dev.to/medunes/stop-saying-it-just-works-save-your-credibility-as-a-senior-and-say-this-instead-1cpm</link>
      <guid>https://dev.to/medunes/stop-saying-it-just-works-save-your-credibility-as-a-senior-and-say-this-instead-1cpm</guid>
      <description>&lt;p&gt;As senior engineers, we are not allowed to report a "solved" issue with the following explanation: "Well, it just started working. I don't know how, but it's fixed for now."&lt;/p&gt;

&lt;p&gt;Why? Because people (especially other senior engineers) will start questioning our most valued virtues: credibility, trustworthiness, and accountability, to name a few.&lt;/p&gt;

&lt;p&gt;I know the ego might whisper, "That's exactly what a senior does: navigates disasters while keeping the show on the road." Well, that's not quite accurate. Let me tell you why.&lt;/p&gt;

&lt;p&gt;While seniority is often defined as "the ability to WORK under ambiguity," the illusion our ego presents is "the ability to AVOID ambiguity." Notice the difference: "work under" versus "avoid."&lt;/p&gt;

&lt;p&gt;A junior engineer is hired to "avoid" ambiguity. Once they get stuck, they are supposed to reach out to a senior colleague who knows how to "work under" ambiguity.&lt;/p&gt;

&lt;p&gt;Too much philosophy, right? I promise I'm neither a TEDx speaker nor a Scrum evangelist. I'm a hands-on person.&lt;/p&gt;

&lt;p&gt;To make things concise and to the point, here is a list of responses that senior engineers can use to maintain their credibility and prove they are capable of working under ambiguity:&lt;/p&gt;

&lt;p&gt;1- "I performed a time-boxed debugging session for one hour. As it was a showstopper, I had to do a hard reset, and it's working now. I have documented the entire debugging process and can get back to it later."&lt;/p&gt;

&lt;p&gt;2- "I have at least three theories as to why it suddenly started working again, but no way to prove any of them for now. I have an idea for a Proof of Concept (PoC) to validate them next sprint."&lt;/p&gt;

&lt;p&gt;3- "Although there were no code changes or recent deployments, I will reach out to the operations team to check if they changed anything related to server resources."&lt;/p&gt;

&lt;p&gt;4- "It started working again after a short outage. I will schedule a brief meeting to brainstorm with the relevant people about the situation."&lt;/p&gt;

&lt;p&gt;5- "I would be lying if I told you the specific reason it started working again. I'll be honest with you: the context is very low-priority, as this is a local sandbox for a PoC. Therefore, I recommend that we simply ignore it for now."&lt;/p&gt;

&lt;p&gt;6- "Even though it started working again 'by itself,' I consider this a showstopper that we should tackle ASAP. This area of the application is tightly bound to both integrity and security, and we cannot tolerate ignoring known threat vectors."&lt;/p&gt;

&lt;p&gt;7- Finally, for the truly desperate, this is a professional, drop-in replacement for, "I have no clue; it just worked": "I could not find any sign of a change that might explain why it broke and then started working again. Let's attribute this single incident to a race condition, but if it happens again, we must perform a Root Cause Analysis (RCA)."&lt;/p&gt;

&lt;p&gt;P.S. Needless to say, it doesn't help to just say these things. We have to be honest when we use them.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>ai</category>
      <category>rust</category>
    </item>
    <item>
      <title>Clean Code = Change Insurance. Pay the Premium or Face Bankruptcy.</title>
      <dc:creator>medunes</dc:creator>
      <pubDate>Sat, 12 Jul 2025 10:20:57 +0000</pubDate>
      <link>https://dev.to/medunes/clean-code-change-insurance-pay-the-premium-or-face-bankruptcy-9no</link>
      <guid>https://dev.to/medunes/clean-code-change-insurance-pay-the-premium-or-face-bankruptcy-9no</guid>
      <description>&lt;p&gt;I have always been obsessed with understanding the deepest meanings and "metaphysics" of clean-code, or coding best practices in general. Then, I started to literally hate and reject most of what comes from that "movement", because some people and entities turned it into religions, hypes, brainwash, my way or the highway, ending up with it as a product to print on tee-shirts to wear in conferences.&lt;/p&gt;

&lt;p&gt;Took a breath and stepped back and said: honestly, out of all that noise, are there facts I can convince my self with, as a pragmatic programmer, and argue with others based on them?&lt;/p&gt;

&lt;p&gt;I ended up with the following seven "principles":&lt;/p&gt;

&lt;p&gt;1- "ANY production code you write WILL have to change"&lt;/p&gt;

&lt;p&gt;2- "Changing code costs way more than writing new code."&lt;/p&gt;

&lt;p&gt;3- "Writing new code is not same as REWRITING old code to new code, the latter is also a type of code change"&lt;/p&gt;

&lt;p&gt;4- "Clean Code is a set of empirically proven methods which reduce the cost of code change: the cleanest code is changeable with the lowest cost."&lt;/p&gt;

&lt;p&gt;5- "Writing clean code costs more than writing dirty code. But modifying dirty code costs more than modifying clean code"&lt;/p&gt;

&lt;p&gt;6- "There is no ONE RULE explaining when to write clean code: it is a matter of value for money decision."&lt;/p&gt;

&lt;p&gt;7-  "Clean code is not always optional: the cost of changing code can sometimes be infinite, under certain deadline restrictions. In this case a company can go bankrupt, or a government can be under permanent security threat."&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>career</category>
    </item>
    <item>
      <title>ROUTE vs. INETNUM: What’s the Story?</title>
      <dc:creator>medunes</dc:creator>
      <pubDate>Wed, 30 Apr 2025 11:50:59 +0000</pubDate>
      <link>https://dev.to/medunes/route-vs-inetnum-whats-the-story-1pia</link>
      <guid>https://dev.to/medunes/route-vs-inetnum-whats-the-story-1pia</guid>
      <description>&lt;p&gt;The distinction between &lt;strong&gt;ROUTE&lt;/strong&gt; and &lt;strong&gt;INETNUM&lt;/strong&gt; objects is critical in understanding how CIDRs are managed and associated with ASNs. These are database objects used by RIRs to track IP address allocations and routing information.  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;INETNUM Object&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Definition&lt;/strong&gt;: An &lt;strong&gt;INETNUM&lt;/strong&gt; (Internet Number) object represents an allocated or assigned range of IP addresses (a CIDR or IP range) managed by an RIR or delegated to an organization (e.g., ISP, end-user).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Purpose&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Tracks the &lt;strong&gt;allocation&lt;/strong&gt; or &lt;strong&gt;assignment&lt;/strong&gt; of an IP address range.
&lt;/li&gt;
&lt;li&gt;Records administrative details, such as the organization holding the IP range, contact information, and status (e.g., allocated, assigned).
&lt;/li&gt;
&lt;li&gt;Indicates &lt;strong&gt;who owns or manages&lt;/strong&gt; the IP range, not necessarily how it’s routed.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Scope&lt;/strong&gt;: Primarily administrative. It’s about IP address ownership or stewardship, not routing.
&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Key Attributes&lt;/strong&gt; (in RIPE, for example):

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;inetnum&lt;/code&gt;: The IP range (e.g., &lt;code&gt;203.0.113.0 - 203.0.113.255&lt;/code&gt; or &lt;code&gt;203.0.113.0/24&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;netname&lt;/code&gt;: A descriptive name for the network.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt;: Allocation status (e.g., ALLOCATED, ASSIGNED).
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;org&lt;/code&gt;: The organization responsible for the range.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;admin-c&lt;/code&gt;/&lt;code&gt;tech-c&lt;/code&gt;: Contact details.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;ASN Association&lt;/strong&gt;: An INETNUM object &lt;strong&gt;does not directly specify an ASN&lt;/strong&gt;. It’s about allocation, not routing. However, the organization listed may also control an ASN, or the CIDR may be referenced in a ROUTE object for routing purposes.
&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example&lt;/strong&gt;:
&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  inetnum: 203.0.113.0 - 203.0.113.255  
  netname: EXAMPLE-NET  
  org: ORG-EX1-RIPE  
  status: ASSIGNED  
  admin-c: EX1-RIPE  
  tech-c: EX1-RIPE  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;ROUTE Object&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Definition&lt;/strong&gt;: A &lt;strong&gt;ROUTE&lt;/strong&gt; object represents a CIDR (or IP range) that is announced in the global BGP routing table by a specific ASN.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Purpose&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Tracks the &lt;strong&gt;routing&lt;/strong&gt; of an IP prefix, specifying which ASN is responsible for announcing it to the internet.
&lt;/li&gt;
&lt;li&gt;Helps ensure that only authorized ASNs announce a given CIDR, preventing route hijacking.
&lt;/li&gt;
&lt;li&gt;Used by network operators to validate BGP announcements.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Scope&lt;/strong&gt;: Routing-focused. It’s about how the CIDR is advertised on the internet.
&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Key Attributes&lt;/strong&gt; (in RIPE, for example):

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;route&lt;/code&gt;: The CIDR being announced (e.g., &lt;code&gt;203.0.113.0/24&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;origin&lt;/code&gt;: The ASN announcing the route (e.g., &lt;code&gt;AS12345&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;descr&lt;/code&gt;: Description of the route.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mnt-by&lt;/code&gt;: Maintainer responsible for the object.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;ASN Association&lt;/strong&gt;: The ROUTE object &lt;strong&gt;directly specifies the ASN&lt;/strong&gt; via the &lt;code&gt;origin&lt;/code&gt; attribute, explicitly linking the CIDR to the autonomous system announcing it.
&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example&lt;/strong&gt;:
&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  route: 203.0.113.0/24  
  origin: AS12345  
  descr: Example Network Route  
  mnt-by: EXAMPLE-MNT  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;Key Differences Between INETNUM and ROUTE&lt;/strong&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Aspect&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;INETNUM&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;ROUTE&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tracks IP address allocation/assignment.&lt;/td&gt;
&lt;td&gt;Tracks BGP routing of an IP prefix.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scope&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Administrative (ownership/management).&lt;/td&gt;
&lt;td&gt;Operational (routing/announcement).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ASN Association&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No direct ASN link; tied to an organization.&lt;/td&gt;
&lt;td&gt;Directly specifies ASN via &lt;code&gt;origin&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Content&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;IP range, org, contacts, status.&lt;/td&gt;
&lt;td&gt;CIDR, ASN, routing details.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Who owns or manages the IP range?&lt;/td&gt;
&lt;td&gt;Which ASN announces the IP range?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;203.0.113.0/24&lt;/code&gt; assigned to an organization.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;203.0.113.0/24&lt;/code&gt; announced by &lt;code&gt;AS12345&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Relationship Between INETNUM and ROUTE&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Complementary Roles&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;INETNUM&lt;/strong&gt; object defines the allocation of a CIDR (who it’s assigned to).
&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;ROUTE&lt;/strong&gt; object defines how that CIDR is routed (which ASN announces it).
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Overlap&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A CIDR in an INETNUM object may have a corresponding ROUTE object if it’s publicly routable. For example, &lt;code&gt;203.0.113.0/24&lt;/code&gt; might be allocated to an ISP in an INETNUM and announced by that ISP’s ASN in a ROUTE object.
&lt;/li&gt;
&lt;li&gt;Not all INETNUM CIDRs have ROUTE objects. For example, private IPs or non-routed allocations (e.g., for internal use) won’t have ROUTE objects.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Sub-Allocations&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An INETNUM might cover a large block (e.g., &lt;code&gt;203.0.112.0/20&lt;/code&gt;), which is sub-allocated into smaller CIDRs (e.g., &lt;code&gt;203.0.113.0/24&lt;/code&gt;). Each sub-CIDR might have its own ROUTE object, potentially announced by different ASNs (e.g., if sub-allocated to different organizations).
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Validation&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network operators use ROUTE objects to validate BGP announcements.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;Got more questions about the topic? Drop a comment below!   &lt;/p&gt;

</description>
      <category>networking</category>
      <category>cybersecurity</category>
      <category>devops</category>
      <category>infosec</category>
    </item>
    <item>
      <title>From PHPUnit to Go: Data-Driven Unit Testing for Go Developers</title>
      <dc:creator>medunes</dc:creator>
      <pubDate>Sun, 10 Nov 2024 21:48:36 +0000</pubDate>
      <link>https://dev.to/medunes/from-phpunit-to-go-data-driven-unit-testing-for-go-developers-lb4</link>
      <guid>https://dev.to/medunes/from-phpunit-to-go-data-driven-unit-testing-for-go-developers-lb4</guid>
      <description>&lt;p&gt;In this post, we'll explore how to bring the PHP unit testing mindset, particularly the PHPUnit framework's data provider approach, into Go. If you're an experienced PHP developer, you’re likely familiar with the data provider model: gathering test data separately in raw arrays and feeding this data into a test function. This approach makes unit tests cleaner, more maintainable, and adheres to principles like Open/Closed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the data provider Approach?
&lt;/h2&gt;

&lt;p&gt;Using a data provider approach to structure unit tests in Go provides several advantages, including:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enhanced Readability and Extensibility&lt;/strong&gt;: Tests become visually organized, with clearly separated arrays at the top representing each test scenario. Each array's key describes the scenario, while its content holds the data to test that scenario. This structure makes the file pleasant to work on and easy to extend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Separation of Concerns&lt;/strong&gt;: The data provider model keeps data and test logic apart, resulting in a lightweight, decoupled function that can remain largely unchanged over time. Adding a new scenario only requires appending more data to the provider, keeping the test function open for extensions but closed for modification—a practical application of the Open/Closed Principle in testing.&lt;/p&gt;

&lt;p&gt;In some projects, I’ve even seen scenarios dense enough to warrant using a separate &lt;code&gt;JSON&lt;/code&gt; file as the data source, manually built and fed to the provider, which in turn supplies data to the test function.&lt;/p&gt;

&lt;h2&gt;
  
  
  When it is very encouraged to use data providers?
&lt;/h2&gt;

&lt;p&gt;Using data providers is especially encouraged when you have a large number of test cases with varying data: each test case is conceptually similar but differs only in input and expected output.&lt;/p&gt;

&lt;p&gt;Intermingling data and logic in a single test function can reduce Developer Experience (DX). It often leads to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verbosity Overload&lt;/strong&gt;: Redundant code that repeats statements with slight data variations, leading to a codebase that is verbose without added benefit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reduced Clarity&lt;/strong&gt;: Scanning through the test function becomes a chore when trying to isolate the actual test data from the surrounding code, which the data provider approach naturally alleviates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nice, so what is exactly a data provider?
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://docs.phpunit.de/en/10.5/writing-tests-for-phpunit.html#data-providers" rel="noopener noreferrer"&gt;DataProvider&lt;/a&gt; pattern in &lt;code&gt;PHPUnit&lt;/code&gt; where basically the provider function supplies the test function with different sets of data that gets consumed in an implicit loop. It ensures the &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself" rel="noopener noreferrer"&gt;DRY&lt;/a&gt; (Don't Repeat Yourself) principle, and aligns with the &lt;a href="https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle" rel="noopener noreferrer"&gt;Open/Closed Principle&lt;/a&gt; as well,by making it easier to add or modify test scenarios without altering the core test function logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solving the problem without a data provider?
&lt;/h2&gt;

&lt;p&gt;To illustrate the drawbacks of verbosity, code duplication, and maintenance challenges, here is a snippet of an example of unit test for the bubble sort function without the help of data providers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&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="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;PHPUnit\Framework\TestCase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BubbleSortTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TestCase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testBubbleSortEmptyArray&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="nf"&gt;BubbleSort&lt;/span&gt;&lt;span class="p"&gt;([]));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testBubbleSortOneElement&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;BubbleSort&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testBubbleSortTwoElementsSorted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;144&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;BubbleSort&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;144&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testBubbleSortTwoElementsUnsorted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;BubbleSort&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testBubbleSortMultipleElements&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;BubbleSort&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&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;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// And so on for each test case, could be 30 cases for example.&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testBubbleSortDescendingOrder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;BubbleSort&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testBubbleSortBoundaryValues&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2147483647&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2147483648&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;BubbleSort&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;2147483648&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2147483647&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Are there issues with the above code? sure:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verbosity&lt;/strong&gt;: Each test case requires a separate method, resulting in a large, repetitive code-base.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Duplication&lt;/strong&gt;: Test logic is repeated in each method, only varying by input and expected output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open/Closed Violation&lt;/strong&gt;: Adding new test cases requires altering the test class structure by creating more methods.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solving the problem with data provider!
&lt;/h2&gt;

&lt;p&gt;Here’s the same test suite refactored to use a data provider&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&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="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;PHPUnit\Framework\TestCase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BubbleSortTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TestCase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * Provides test data for bubble sort algorithm.
     *
     * @return array&amp;lt;string, array&amp;gt;
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;bubbleSortDataProvider&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&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="s1"&gt;'empty'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[],&lt;/span&gt; &lt;span class="p"&gt;[]],&lt;/span&gt;
            &lt;span class="s1"&gt;'oneElement'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="s1"&gt;'twoElementsSorted'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;144&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;144&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="s1"&gt;'twoElementsUnsorted'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="s1"&gt;'moreThanOneElement'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&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;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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="s1"&gt;'moreThanOneElementWithRepetition'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&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;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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="s1"&gt;'moreThanOneElement2'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&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="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="s1"&gt;'sameElement'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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="mi"&gt;1&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;1&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="s1"&gt;'negativeNumbers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&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="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&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="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&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;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&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="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="s1"&gt;'descendingOrder'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="s1"&gt;'randomOrder'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;9&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;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&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="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="s1"&gt;'duplicateElements'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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;span class="mi"&gt;2&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;1&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;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="s1"&gt;'largeArray'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[&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="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2032&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1024354&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;155&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;174&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1955&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;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;322&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4741&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;96524&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&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="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2032&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1024354&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;155&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;174&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1955&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;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;322&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4741&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;96524&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="s1"&gt;'singleNegativeElement'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="s1"&gt;'arrayWithZeroes'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="s1"&gt;'ascendingOrder'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="s1"&gt;'descendingOrderWithDuplicates'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="s1"&gt;'boundaryValues'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;2147483648&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2147483647&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2147483647&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2147483648&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="s1"&gt;'mixedSignNumbers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[&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="mi"&gt;0&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="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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&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="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="mi"&gt;0&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="mi"&gt;2&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="cd"&gt;/**
     * @dataProvider bubbleSortDataProvider
     *
     * @param array&amp;lt;int&amp;gt; $input
     * @param array&amp;lt;int&amp;gt; $expected
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testBubbleSort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$expected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;BubbleSort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Are there any advantages of using the data provider? oh yeah:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conciseness&lt;/strong&gt;: All test data is centralized in a single method, removing the need for multiple functions for each scenario.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enhanced Readability&lt;/strong&gt;: Each test case is well-organized, with descriptive keys for each scenario.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open/Closed Principle&lt;/strong&gt;: New cases can be added to the data provider without altering the core test logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improved DX (Developer Experience)&lt;/strong&gt;: Test structure is clean, appealing to the eyes, making even those lazy developers motivated to extend, debug, or update it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bringing Data Providers to Go
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Go doesn't have a native data provider model like PHPUnit, so we need to use a different approach. There could be many implementation with several levels complexity, the following is an average one that might be a candidate to simulate data provider in Go land
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"testing"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/stretchr/testify/assert"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;TestData&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;ArrayList&lt;/span&gt;    &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;][]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;ExpectedList&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&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="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;maxInt32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;int32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="kt"&gt;uint32&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="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;minInt32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;maxInt32&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;testData&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;TestData&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&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="s"&gt;"empty"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                            &lt;span class="p"&gt;{},&lt;/span&gt;
        &lt;span class="s"&gt;"oneElement"&lt;/span&gt;&lt;span class="o"&gt;:&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="s"&gt;"twoElementsSorted"&lt;/span&gt;&lt;span class="o"&gt;:&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="m"&gt;144&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"twoElementsUnsorted"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;              &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"moreThanOneElement"&lt;/span&gt;&lt;span class="o"&gt;:&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;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;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"moreThanOneElementWithRepetition"&lt;/span&gt;&lt;span class="o"&gt;:&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;4&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;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"moreThanOneElement2"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;              &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;7&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;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"sameElement"&lt;/span&gt;&lt;span class="o"&gt;:&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;1&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;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"negativeNumbers"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&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="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"descendingOrder"&lt;/span&gt;&lt;span class="o"&gt;:&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="m"&gt;4&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;2&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="s"&gt;"randomOrder"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;9&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;7&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;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;6&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;8&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="s"&gt;"duplicateElements"&lt;/span&gt;&lt;span class="o"&gt;:&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;2&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;1&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;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;4&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"largeArray"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                       &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;2032&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;23&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="m"&gt;0&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="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1024354&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;155&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;174&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1955&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;255&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;322&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4741&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;96524&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"singleNegativeElement"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"arrayWithZeroes"&lt;/span&gt;&lt;span class="o"&gt;:&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="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="m"&gt;0&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;0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"ascendingOrder"&lt;/span&gt;&lt;span class="o"&gt;:&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="s"&gt;"descendingOrderWithDuplicates"&lt;/span&gt;&lt;span class="o"&gt;:&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="m"&gt;5&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;3&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;2&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="s"&gt;"boundaryValues"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;2147483648&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;2147483647&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"mixedSignNumbers"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&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;0&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="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="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;ExpectedList&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&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="s"&gt;"empty"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                            &lt;span class="p"&gt;{},&lt;/span&gt;
        &lt;span class="s"&gt;"oneElement"&lt;/span&gt;&lt;span class="o"&gt;:&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="s"&gt;"twoElementsSorted"&lt;/span&gt;&lt;span class="o"&gt;:&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="m"&gt;144&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"twoElementsUnsorted"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;              &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"moreThanOneElement"&lt;/span&gt;&lt;span class="o"&gt;:&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="s"&gt;"moreThanOneElementWithRepetition"&lt;/span&gt;&lt;span class="o"&gt;:&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;4&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="s"&gt;"moreThanOneElement2"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;              &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;5&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="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;99&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"sameElement"&lt;/span&gt;&lt;span class="o"&gt;:&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;1&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;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"negativeNumbers"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&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="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"descendingOrder"&lt;/span&gt;&lt;span class="o"&gt;:&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="s"&gt;"randomOrder"&lt;/span&gt;&lt;span class="o"&gt;:&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="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"duplicateElements"&lt;/span&gt;&lt;span class="o"&gt;:&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;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;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;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;4&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"largeArray"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                       &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;2032&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;23&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="m"&gt;0&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="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1024354&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;155&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;174&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1955&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;255&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;322&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4741&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;96524&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"singleNegativeElement"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"arrayWithZeroes"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                  &lt;span class="p"&gt;{&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="m"&gt;0&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="m"&gt;0&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="s"&gt;"ascendingOrder"&lt;/span&gt;&lt;span class="o"&gt;:&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="s"&gt;"descendingOrderWithDuplicates"&lt;/span&gt;&lt;span class="o"&gt;:&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;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="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"boundaryValues"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;2147483647&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2147483648&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"mixedSignNumbers"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                 &lt;span class="p"&gt;{&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="o"&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;0&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="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="n"&gt;TestBubble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&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="n"&gt;testCase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;array&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;testData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testCase&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;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Bubble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ElementsMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;testData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExpectedList&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;testCase&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We basically define two maps/lists: one for the input data and the second for the expected data. We ensure that each case scenario on both sides is referred through the same map key on both sides. &lt;/li&gt;
&lt;li&gt;Executing the tests is then a matter of a loop in a simple function that iterates over the prepared input/expected lists.&lt;/li&gt;
&lt;li&gt;Except some one-time boiler-plate of types, modifications to tests should only happen on the data side, mostly no change should alter the logic of the function executing tests, thus achieving the goals we've talked about above: reducing test work down to a matter of raw data preparation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bonus: A Github repository implementing the logic presented in this blogpost can be found here &lt;a href="https://github.com/MedUnes/dsa-go" rel="noopener noreferrer"&gt;https://github.com/MedUnes/dsa-go&lt;/a&gt;. So far it contains Github actions running these tests and even showing that super famous green badge ;)&lt;/p&gt;

&lt;p&gt;See you in the next [hopefully] informative post!&lt;/p&gt;

</description>
      <category>go</category>
      <category>php</category>
      <category>tdd</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Combining 2FA and Public Key Authentication for a better Linux SSH security</title>
      <dc:creator>medunes</dc:creator>
      <pubDate>Wed, 06 Dec 2023 10:36:47 +0000</pubDate>
      <link>https://dev.to/medunes/combining-2fa-and-public-key-authentication-for-a-better-linux-ssh-security-4i35</link>
      <guid>https://dev.to/medunes/combining-2fa-and-public-key-authentication-for-a-better-linux-ssh-security-4i35</guid>
      <description>&lt;h2&gt;
  
  
  What are we trying to improve?
&lt;/h2&gt;

&lt;p&gt;SSH (Secure Shell Connection) is a secure way to login to a Linux server and remotely work with it. However there might be a good margin of further improvements to secure even more this.&lt;/p&gt;

&lt;p&gt;1- &lt;strong&gt;Substitute password authentication with public key authentication&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: &lt;a href="https://en.wikipedia.org/wiki/Public-key_cryptography" rel="noopener noreferrer"&gt;Public key authentication&lt;/a&gt; is more secure than passwords. Passwords can be brute-forced or guessed, while a cryptographic key pair (public and private keys) is practically impossible to crack with current technology. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Replay Attacks&lt;/strong&gt;: Since the private key is never transmitted over the network, there's no risk of interception and replay attacks, unlike passwords. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation&lt;/strong&gt;: Public keys are great for automated processes. You can use SSH without entering a password each time, which is essential for scripts and system administration tasks. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Management&lt;/strong&gt;: It's easier to manage and revoke access with public keys. Instead of changing a password (which might be shared or used on multiple systems), you just remove the public key from the server. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phishing Resistant&lt;/strong&gt;: Users are less susceptible to phishing attacks since they're not entering a password that could be captured.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2- &lt;strong&gt;Disable root login from the SSH configuration&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mitigate Brute Force Attacks&lt;/strong&gt;: Root is a known username and often targeted by brute force attacks. Disabling root login means attackers can't directly access the most privileged account. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit Privilege Escalation&lt;/strong&gt;: Even if an attacker compromises a regular user account, they still need to find a way to escalate privileges to root, adding an extra layer of security. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit Trails&lt;/strong&gt;: Using &lt;code&gt;sudo&lt;/code&gt; from a regular account to perform administrative tasks leaves a trail, helping in auditing and monitoring activities. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Risk of Accidental Damage&lt;/strong&gt;: Preventing direct root access reduces the risk of accidental high-impact changes by system administrators. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance&lt;/strong&gt;: Many security standards and best practices advise against using root accounts for routine administration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3- &lt;strong&gt;Force 2FA authentication with TOTP&lt;/strong&gt;: Adding Time-based One-Time Password (TOTP) as an additional layer to SSH authentication significantly boosts security: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Two-Factor Authentication:&lt;/strong&gt; TOTP introduces a second factor of authentication. Even if an attacker steals a user's SSH key or password, they still need the TOTP, which is typically generated on a device the user possesses. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Codes&lt;/strong&gt;: TOTPs change every 30-60 seconds. This makes stolen codes useless after a very short time, countering replay attacks. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mitigates Phishing:&lt;/strong&gt; TOTPs are harder to phish than static passwords because they are constantly changing and are only valid for a short period. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Network Dependency:&lt;/strong&gt; TOTPs are generated by an algorithm based on time and a secret key, so they don't require network connectivity, making them reliable and efficient. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Harder to Intercept:&lt;/strong&gt; Unlike static passwords, intercepting a TOTP doesn't compromise future logins, as each password is valid for only one login session.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4- &lt;strong&gt;Change SSH server port from default 22 to a less common port number&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reduce Automated Attacks&lt;/strong&gt;: Many bots and scripts target port 22 for SSH attacks. Changing the port can reduce the volume of these automated attacks. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower Profile&lt;/strong&gt;: By not using the default port, your server becomes less obvious to casual scanners who are looking for easy targets. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter Out Noise&lt;/strong&gt;: Changing the port can reduce log noise from repeated login attempts, making it easier to spot genuine security threats.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configuring your local machine
&lt;/h2&gt;

&lt;p&gt;1- &lt;strong&gt;Setup Public Key Authentication&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Generate Key Pair:&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;To make the public key authentication work, we need to generate the called "key-pair": the public key which you share with the remote server, and the private key which must be kept &lt;strong&gt;private&lt;/strong&gt; and secure on your local machine. &lt;/li&gt;
&lt;li&gt;For enhancing even more security, you can add a passphrase to your private key so that is won't be possible to use it until the passphrase is entered. &lt;/li&gt;
&lt;li&gt;Fortunately some commonly used tools you can achieve all the burden above with the simple commands below:
&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="c"&gt;# replace my_server_name here eventually&lt;/span&gt;
&lt;span class="c"&gt;# when prompted with passphrase, enter it with keyboard&lt;/span&gt;
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/my_server_name
&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="c"&gt;# replace my_server_name here eventually&lt;/span&gt;
ssh-copy-id &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/my_server_name.pub user@remote_host
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;If &lt;code&gt;ssh-copy-id&lt;/code&gt; is not an option, you still can manually copy the public key to the remote server. Follow the steos below &lt;strong&gt;only if&lt;/strong&gt; &lt;code&gt;ssh-copy-id&lt;/code&gt; doesn't work for you: &lt;/li&gt;
&lt;li&gt;First display and copy your public key: (should be available after the generation steps above)
&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;cat&lt;/span&gt; ~/.ssh/my_server_name.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Then open the &lt;code&gt;authorized_keys&lt;/code&gt; file on the remote server and paste the copied key to its end.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano ~/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now an important step, do not forget to apply necessary permissions
&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;chmod &lt;/span&gt;700 ~/.ssh
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2- &lt;strong&gt;Add SSH configuration (optional)&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This will improve the UX while logging into your remote server making it more convenient. &lt;/li&gt;
&lt;li&gt;For this we suppose that your remote host IP is &lt;code&gt;192.168.12.34&lt;/code&gt; and we will suppose the SSH port has been set on the remote server to, for example, &lt;code&gt;4898&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;Based on these parameters, you should add this section to your &lt;code&gt;~/.ssh/config&lt;/code&gt; file.
&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="c"&gt;#~/.ssh/config&lt;/span&gt;
Host my_server_name
    HostName 192.168.12.34
    User my_username
    IdentityFile /home/my_username/.ssh/my_server_name
    Port 4898
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3- &lt;strong&gt;Have a TOTP client&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You will need this to be able to generate the TOTP number once prompted from the remote server. &lt;/li&gt;
&lt;li&gt;You have couple options here: Mobile apps like &lt;a href="//play.google.com/store/apps/details?id=com.google.android.apps.authenticator2"&gt;Google Authenticator&lt;/a&gt; or &lt;a href="https://authy.com/download/" rel="noopener noreferrer"&gt;Authy&lt;/a&gt;, Desktop applications like &lt;a href="https://www.linux.org/threads/in-depth-tutorial-how-to-set-up-2fa-totp-with-keepassxc-aegis-and-authy.36577/" rel="noopener noreferrer"&gt;Keepass TOTP&lt;/a&gt; or even more advanced tools like &lt;a href="https://support.yubico.com/hc/en-us/articles/360013789259-Using-Your-YubiKey-with-Authenticator-Codes" rel="noopener noreferrer"&gt;YubiKey&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configuring your remote server
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Instructions here are tested on Debian based Linux distributions, but should work on other distros as well without "big" differences. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1- &lt;strong&gt;Install google authenticator&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Here you run the authenticator installation then you execute it, and we will answer "y" for all the prompts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&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;libpam-google-authenticator
...
...
...

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

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;google-authenticator
Do you want authentication tokens to be time-based &lt;span class="o"&gt;(&lt;/span&gt;y/n&lt;span class="o"&gt;)&lt;/span&gt; y
█████████████████████████
██ ▄▄▄▄▄ █▀▄█▄█ █ ▄▄▄▄▄ ██
██ █   █ █▀▄▀█▄█ █   █ ██
██ █▄▄▄█ █ ▀ ▀ ▄ █▄▄▄█ ██
██▄▄▄▄▄▄▄█▄█ ▀ █▄▄▄▄▄▄▄██
██▄▀▄▀▄█ ▀▄█▀▄█▄█▀█▀██▀██
██▄█▄██▄▀▄▄▀█▄▀ ▀▄█▄▀▀▄██
██▄▄▄▄▄▄▄█▄▄█▄▄███▄█▄▄▄███
█████████████████████████

Your new secret key is: 4D3FPZ2W6Y7IOMV 
Your verification code is 123456
Your emergency scratch codes are:
81234567
89123456
78123456
69123456
58123456

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

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pay attention to the generated codes: Use The ASCII made QR code (yay, terminal!) to add the TOTP to your smartphone app by adding new entry and scanning the picture, and use the secret key to add a TOTP setup to your keepass application and/or YubiKey. The backup codes should be saved for eventual emergency cases.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2- &lt;strong&gt;Configure SSH server&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Here we will act on two configs: the &lt;code&gt;SSHD&lt;/code&gt; and the &lt;code&gt;PAM&lt;/code&gt;. As we explained in the first section, we will edit the SSH daemon config file to disable password authentication and root user, keep interactive keyboard for &lt;code&gt;TOTP&lt;/code&gt; prompting, enable the Public Key authentication and change the SSH port number. &lt;/li&gt;
&lt;li&gt;The resulting config file &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; should look like this:
&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;sudo &lt;/span&gt;nano /etc/ssh/sshd_config
&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="c"&gt;# This one we changed from default Port 22&lt;/span&gt;
Port 4898

&lt;span class="c"&gt;# Add this entry or edit it if it is already existing&lt;/span&gt;
PermitRootLogin no

&lt;span class="c"&gt;# We can explicitly set this although it should be the default value even if omitted. &lt;/span&gt;
PubkeyAuthentication &lt;span class="nb"&gt;yes&lt;/span&gt;

&lt;span class="c"&gt;# This setting will disable the password based authentication. &lt;/span&gt;
PasswordAuthentication no

&lt;span class="c"&gt;# This entry will allow both publickey authentication and keyboard-interactive which # we need to enter our TOTP code&lt;/span&gt;
AuthenticationMethods publickey,keyboard-interactive

&lt;span class="c"&gt;# PAM (Pluggable Authentication Modules) is also required to allow google &lt;/span&gt;
&lt;span class="c"&gt;# authenticator #integration&lt;/span&gt;
UsePAM &lt;span class="nb"&gt;yes&lt;/span&gt;

&lt;span class="c"&gt;# Depending on your Debian version, if you find the entry &lt;/span&gt;
&lt;span class="c"&gt;# KbdInteractiveAuthentication existing in the config file, then use it, otherwise you &lt;/span&gt;
&lt;span class="c"&gt;# might find the deprecated entry which is ChallengeResponseAuthentication which&lt;/span&gt;
&lt;span class="c"&gt;# in that case should be set to yes instead.&lt;/span&gt;
KbdInteractiveAuthentication &lt;span class="nb"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3- &lt;strong&gt;Configure PAM&lt;/strong&gt;: for this we need to enter some changes to the &lt;code&gt;/etc/pam.d/sshd&lt;/code&gt; file:&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;nano /etc/pam.d/sshd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;First, find the following three lines and comment them out
&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="c"&gt;#@include common-auth&lt;/span&gt;
&lt;span class="c"&gt;# account  required     pam_access.so&lt;/span&gt;
&lt;span class="c"&gt;#@include common-password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Then add the following line to the end of the file:
&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="c"&gt;# two-factor authentication via Google Authenticator&lt;/span&gt;
auth required pam_google_authenticator.so
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now we only have one last step to go: reststart the SSH server
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; sudo systemctl restart sshd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Server setup should be complete ;)&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  SSH like a boss
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Given all the steps above correctly achieved, you should by now be able to ssh to your remote server as easy as the following:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; ssh my_server_name
&lt;span class="o"&gt;(&lt;/span&gt;my_username@192.168.12.34&lt;span class="o"&gt;)&lt;/span&gt; Verification code: 
Linux raspberrypi 6.1.0-rpi6-rpi-v8 &lt;span class="c"&gt;#1 SMP PREEMPT Debian 1:6.1.58-1+rpt2 (2023-10-27) aarch64&lt;/span&gt;

The programs included with the Debian GNU/Linux system are free software&lt;span class="p"&gt;;&lt;/span&gt;
the exact distribution terms &lt;span class="k"&gt;for &lt;/span&gt;each program are described &lt;span class="k"&gt;in &lt;/span&gt;the
individual files &lt;span class="k"&gt;in&lt;/span&gt; /usr/share/doc/&lt;span class="k"&gt;*&lt;/span&gt;/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Wed Dec  6 01:16:51 2023 from 192.168.12.33
my_username@my_server_name:~ &lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;You will be of course prompted for the TOTP code (4 digits) which you should use your smartphone, keepass or YubiKey to get them. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So that was pretty match it. I will be happy to support for any further questions, rectifications or simple likes :)&lt;/p&gt;

</description>
      <category>security</category>
      <category>ssh</category>
      <category>devops</category>
      <category>sre</category>
    </item>
    <item>
      <title>How to log your life easier in Symfony?</title>
      <dc:creator>medunes</dc:creator>
      <pubDate>Tue, 01 Mar 2022 22:26:33 +0000</pubDate>
      <link>https://dev.to/medunes/how-to-log-your-life-easier-in-symfony-5np</link>
      <guid>https://dev.to/medunes/how-to-log-your-life-easier-in-symfony-5np</guid>
      <description>&lt;p&gt;Hi coderz, how you doing? I know you are fed-up with blogposts and have tons of work, yet, check 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%2Fnn0brs3z4g4l40tk0wln.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%2Fnn0brs3z4g4l40tk0wln.png" alt="image.png" width="300" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The goal: 
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Turning any class in your project "logging-able", yet with the least possible changes and boilerplate verbosity. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The classical way:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;So knowing that monolog's logger (however you configure it) is a tagged service at the end, the "only" way to "cleanly" use it is to DI/inject it to the needed service&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Injecting services in Symfony can be done with multiple approaches and strategies. But in best cases you'll have at least to touch two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The class needing the logger by defining a private property to receive the logger service instance, and an assignement in the constructor to actually receive the service.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;services.yaml&lt;/code&gt; if you choose to go with setter injection rather than constructor injection (as the latter can benefit from autowiring)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Now as logging is a "nice and wanted" feature, your urge to log stuff here and there can grow over and over, and you might feel silly polluting your code with one more line of properties and one other in the constructor. Sometimes you'd only write a constructor for the sake eyes of the DI injection of the logger. Having this exact snippet copy/pasted over a dozen of classes might really make you feel unhappy.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  The shortcut:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If you use the autocomplete feature of PHPStorm, you'd notice a pair of interface/trait having the same prefix.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Psr\Log\LoggerAwareInterface&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Psr\Log\LoggerAwareTrait&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Can we use them altogether to solve the issue above? → yes.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;In general the SomethingAwareInterface naming pattern, means the class is supposed to have a method named "setSomething()".&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;And following conventions as well, having that setter means your class should have a "something" property that setter will modify, and here comes the SomethingAwareTrait to define that property for you.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Now implementing/using that interface/trait makes your class having a property named "something" and a setter for it. Nice thing here is that all that code verbosity is totally hidden in the backyard.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Still one single obstacle: How will the trait actually get the service instance.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;One solution we might think about is the "@required" annotation above the setLogger() method, but in our case the setter is defined in the used trait, and we can't modify it.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx7smvgl8vk3if4laye0h.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%2Fx7smvgl8vk3if4laye0h.png" alt="image.png" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A once and for all solution is to slightly modify the application's kernel, so that it loops over all services before the container is built, and check if any service is a "somethingAware", then make him really aware of it by explicitly injecting the service.
Here is a showcase to make any class of your project that implement/use the pair above, being able to use the logger straight away without any further overhead:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="c1"&gt;// src/Kernel.php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Psr\Log\LoggerAwareInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\DependencyInjection\ContainerBuilder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\DependencyInjection\Definition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\HttpKernel\Kernel&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;BaseKernel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Kernel&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseKernel&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;CompilerPassInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MicroKernelTrait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ContainerBuilder&lt;/span&gt; &lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$definitions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getDefinitions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$definitions&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$definition&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="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isAware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$definition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;LoggerAwareInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nv"&gt;$definition&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addMethodCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                 &lt;span class="s1"&gt;'setLogger'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getDefinition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'monolog.logger'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
             &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;isAware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Definition&lt;/span&gt; &lt;span class="nv"&gt;$definition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$awarenessClass&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nv"&gt;$serviceClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$definition&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getClass&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$serviceClass&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="nv"&gt;$implementedClasses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;class_implements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$serviceClass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$implementedClasses&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&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="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\array_key_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$awarenessClass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$implementedClasses&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;

     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now Just use implement the interface and use the trait in your command, for example, and you are ready to go!
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="c1"&gt;// src/Command/MyCommand.php&lt;/span&gt;

&lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&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="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Command&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Psr\Log\LoggerAwareInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Psr\Log\LoggerAwareTrait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\Console\Attribute\AsCommand&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\Console\Command\Command&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\Console\Input\InputInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\Console\Output\OutputInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;AsCommand&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="s1"&gt;'app:my-command'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'test!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyCommand&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Command&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;LoggerAwareInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;LoggerAwareTrait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;InputInterface&lt;/span&gt; &lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;OutputInterface&lt;/span&gt; &lt;span class="nv"&gt;$output&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="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'I can log!'&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;Command&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SUCCESS&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;You can clone/download the code snippets above from this &lt;a href="https://gist.github.com/MedUnes/5f57a004c59dacd2fe19cf56ea2c18f9" rel="noopener noreferrer"&gt;gist on github&lt;/a&gt; as well&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Enough talk for today, hope it helped and see you soon!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>symfony</category>
      <category>php</category>
      <category>cleancode</category>
      <category>log</category>
    </item>
    <item>
      <title>How to make two docker containers from two different projects communicate with each others ?</title>
      <dc:creator>medunes</dc:creator>
      <pubDate>Mon, 07 Feb 2022 23:37:41 +0000</pubDate>
      <link>https://dev.to/medunes/how-to-make-two-docker-containers-from-two-different-projects-communicate-with-each-others--1h18</link>
      <guid>https://dev.to/medunes/how-to-make-two-docker-containers-from-two-different-projects-communicate-with-each-others--1h18</guid>
      <description>&lt;h2&gt;
  
  
  The problem:
&lt;/h2&gt;

&lt;p&gt;You have a container under a docker-compose project, that you want to communicate or access another container which exists under a second docker-compose project&lt;/p&gt;

&lt;h2&gt;
  
  
  A real-life situation?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;For some security policy, your SysOp or hosting provider blocks all external traffic, except those for port 80 (HTTP) or port 443 (HTTPS). However, you want to set up and manage several web (or non-web) services on your machine, basically using docker containers and managed by docker-compose.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To handle this you set-up a reverse-proxy using a dedicated docker-compose project and container, called proxy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The proxy container/project listens on port 80, for all incoming external requests. (The nginx config can distinguish the called domain with the &lt;em&gt;server_name&lt;/em&gt; directive)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On the other side, on another docker-compose project, you have a Kibana service that serves request on kibana.exemple.com, but under port 5601 (still HTTP traffic).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Kibana service is on a dedicated container called &lt;em&gt;kibana&lt;/em&gt;, among three other containers in the ELK docker-compose porject (different from the reverse-proxy project).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The solution as you would guess, is that the &lt;em&gt;reverse-proxy&lt;/em&gt; forwards the external &lt;a href="http://kibana.exemple.com:80" rel="noopener noreferrer"&gt;http://kibana.exemple.com:80&lt;/a&gt; to an internal &lt;a href="http://kibana:5601,%C2%A0to" rel="noopener noreferrer"&gt;http://kibana:5601, to&lt;/a&gt; solve the port restriction issue.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The kibana domain name used when we want to hit the &lt;a href="http://kibana:5601%C2%A0endpoint" rel="noopener noreferrer"&gt;http://kibana:5601 endpoint&lt;/a&gt;, is actually unknown to the outside, or even to the host machine (in principle), it is only internally resolvable by docker built-in DNS to map it to the kibana container.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The problem here is, the network to which the kibana container belongs to is internal and only visible within the ELK docker compose projcet, to which kibana container belongs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Yet, we want the &lt;em&gt;reverse-proxy&lt;/em&gt; container that lives in a separated project to be able to hit that endpoint without much boilerplate work.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A solution?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Fortunately, docker-compose provides an easy way to solve this kind of situations, which is the external networks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An external network must be defined, outside from the docker-compose config (one way through a docker-compose command)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Both networks should join this network, and, that's all.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now you can just refer the container by its name from one or the other side as if they were on the same project&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Here it how the official docker-compose defines the feature:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj65kwt2p5eiqbtv0ev9n.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%2Fj65kwt2p5eiqbtv0ev9n.png" alt=" " width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to make it work?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create an external network from command line, here we suppose you name it proxy_external
&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;$ &lt;/span&gt;docker network create proxy_external

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add the proxy_external under the services → proxy→ networks level of docker-compose.yml of the &lt;em&gt;reverse-proxy&lt;/em&gt; project &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add the proxy_external straight under the networks level of docker-compose.yml of the elk project . Define this as an external network&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# reverse-proxy project&lt;/span&gt;

&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.8"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# ..&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

      &lt;span class="c1"&gt;# This is the internal network for the reverse-proxy project&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;proxy&lt;/span&gt;

      &lt;span class="c1"&gt;# This is the external proxy&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;proxy_external&lt;/span&gt;
&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="c1"&gt;# This is the server's network, defined by the service&lt;/span&gt;
  &lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~&lt;/span&gt;

  &lt;span class="c1"&gt;# This is an external network. It serves as a bridge between reverse-proxy and other projects.&lt;/span&gt;
  &lt;span class="na"&gt;proxy_external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;proxy_external&lt;/span&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add the proxy_external under the services → kibana→ networks  level of docker-compose.yml of the reverse-proxy project &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add the proxy_external straight under the networks level of docker-compose.yml of the elk project . Define this as an external network&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# elk project&lt;/span&gt;

&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.8"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;elk&lt;/span&gt;
    &lt;span class="s"&gt;# ..&lt;/span&gt;
    &lt;span class="s"&gt;networks&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

      &lt;span class="c1"&gt;# This is the internal network for the elk project&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;elk&lt;/span&gt;

      &lt;span class="c1"&gt;# This is the external proxy&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;proxy_external&lt;/span&gt;
&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="c1"&gt;# This is the server's network, defined by the service&lt;/span&gt;
  &lt;span class="na"&gt;elk&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~&lt;/span&gt;

  &lt;span class="c1"&gt;# This is an external network. It serves as a bridge between reverse-proxy and other projects.&lt;/span&gt;
  &lt;span class="na"&gt;proxy_external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;proxy_external&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Further readings:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.docker.com/compose/compose-file/compose-file-v3/#network-configuration-reference" rel="noopener noreferrer"&gt;https://docs.docker.com/compose/compose-file/compose-file-v3/#network-configuration-reference&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://odysee.com/@the-digital-life:a/docker-networking-tutorial-all-network:1" rel="noopener noreferrer"&gt;https://odysee.com/@the-digital-life:a/docker-networking-tutorial-all-network:1&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>nginx</category>
      <category>microservices</category>
    </item>
  </channel>
</rss>
