<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[ByRodovalho]]></title><description><![CDATA[From Brazil to the Cloud ☁️  I write about backend engineering, distributed systems and cloud-native architectures.]]></description><link>https://byrodovalho.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1745529454548/6d12f78d-b6d6-4d9e-8f95-30c018d7dd3a.png</url><title>ByRodovalho</title><link>https://byrodovalho.com</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 17 Apr 2026 15:14:30 GMT</lastBuildDate><atom:link href="https://byrodovalho.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The "Dual-Write" Trap: Why Your Microservices Need the Outbox Pattern]]></title><description><![CDATA[The Silent Killer of Consistency
Hey dev! Let’s talk about a scenario that has kept many of us up at night.
You are designing a .NET microservice. Your logic seems solid:

Register a new Order in the database (SQL Server/PostgreSQL).
Publish an Order...]]></description><link>https://byrodovalho.com/the-dual-write-trap-why-your-microservices-need-the-outbox-pattern</link><guid isPermaLink="true">https://byrodovalho.com/the-dual-write-trap-why-your-microservices-need-the-outbox-pattern</guid><category><![CDATA[dotnet]]></category><category><![CDATA[Microservices]]></category><category><![CDATA[# csharp  # beginners  # dotnet  # programming]]></category><category><![CDATA[architecture]]></category><category><![CDATA[cloud native]]></category><dc:creator><![CDATA[Phelipe Rodovalho]]></dc:creator><pubDate>Sun, 16 Nov 2025 17:14:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763312963890/d155256c-5a4b-45ff-8ae4-034191430ebc.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-the-silent-killer-of-consistency">The Silent Killer of Consistency</h2>
<p>Hey dev! Let’s talk about a scenario that has kept many of us up at night.</p>
<p>You are designing a .NET microservice. Your logic seems solid:</p>
<ol>
<li>Register a new <code>Order</code> in the database (SQL Server/PostgreSQL).</li>
<li>Publish an <code>OrderCreated</code> event to the message broker (RabbitMQ/Azure Service Bus) so the Shipping Service can do its job.</li>
</ol>
<p>You write the code, wrap the database save in a transaction, and right after <code>SaveChanges()</code>, you call <code>bus.Publish()</code>. It works on your machine. It works in QA.</p>
<p>But in production, the network blips. The database transaction commits successfully, but the message broker is unreachable for just 500ms.</p>
<p><strong>Result:</strong> You have an order in the database, but the Shipping Service never got the memo. Your system is now inconsistent, and you have a "zombie order" that will never be shipped.</p>
<p>This is the <strong>Dual-Write Problem</strong>. You are trying to write to two different reliable systems (DB and Broker) without a distributed transaction (2PC).</p>
<h2 id="heading-enter-the-outbox-pattern">Enter the Outbox Pattern</h2>
<p>The solution isn't to retry endlessly inside your HTTP request (that kills performance). The solution is to make the "sending of the message" part of the "saving of the data."</p>
<p>This is where the <strong>Transactional Outbox Pattern</strong> comes in.</p>
<p>Instead of publishing directly to the broker, you save the message payload to a specific table (the "Outbox") in your database <strong>inside the same transaction</strong> as your business data.</p>
<ol>
<li>Start Transaction.</li>
<li>Save Order.</li>
<li>Save <code>OrderCreated</code> message to <code>Outbox</code> table.</li>
<li>Commit Transaction.</li>
</ol>
<p>Now, the operation is atomic. If the DB save fails, the message isn't saved. If it succeeds, the message is guaranteed to be there. A separate background process then picks up the message from the table and reliably pushes it to the broker.</p>
<h2 id="heading-doing-it-in-net-the-smart-way">Doing It in .NET (The Smart Way)</h2>
<p>You <em>could</em> implement this mechanism from scratch—polling, locking, retries—but that violates the <strong>DRY (Don't Repeat Yourself)</strong> principle we discussed in our previous article. We don't want to maintain infrastructure code if we don't have to.</p>
<p>In the .NET ecosystem, <strong>MassTransit</strong> handles this elegantly. It integrates directly with Entity Framework Core, effectively making the Outbox pattern a configuration detail rather than a coding burden.</p>
<h3 id="heading-configuration-example">Configuration Example</h3>
<p>Here is how you set it up in <code>Program.cs</code>:</p>
<pre><code class="lang-csharp">services.AddMassTransit(x =&gt;
{
    x.AddEntityFrameworkOutbox&lt;OrderDbContext&gt;(o =&gt;
    {
        <span class="hljs-comment">// The bus will check for messages in the DB every 10s (configurable)</span>
        o.QueryDelay = TimeSpan.FromSeconds(<span class="hljs-number">10</span>);

        <span class="hljs-comment">// Ensure messages are sent to the broker even if the bus is down initially</span>
        o.UsePostgres(); 
        o.UseBusOutbox();
    });

    x.UsingRabbitMq((context, cfg) =&gt;
    {
        cfg.ConfigureEndpoints(context);
    });
});
</code></pre>
<p>And in your application code? You change <strong>nothing</strong>.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// The 'Publish' call here doesn't hit RabbitMQ immediately.</span>
<span class="hljs-comment">// It writes to the DbContext change tracker (Outbox table).</span>
<span class="hljs-keyword">await</span> _publishEndpoint.Publish(<span class="hljs-keyword">new</span> OrderCreated(order.Id));

<span class="hljs-comment">// The atomic commit happens here. Data + Message saved together.</span>
<span class="hljs-comment">// Simplicity is key!</span>
<span class="hljs-keyword">await</span> _dbContext.SaveChangesAsync();
</code></pre>
<h2 id="heading-why-this-matters">Why This Matters</h2>
<p>This isn't just about "clean architecture." It's about operational resilience.</p>
<p>By using the Outbox pattern, you decouple your service's availability from the message broker's availability. If RabbitMQ is down, your service can still accept orders. The messages will just sit safely in the Outbox table until the connection is restored.</p>
<p>It turns a distributed consistency nightmare into a reliable, self-healing mechanism.</p>
<hr />
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>As we move from monoliths to microservices—like the transition from REST to gRPC we explored recently—we trade simple function calls for complex network interactions.</p>
<p>We must ensure our systems are robust enough to handle these complexities without keeping us awake at night. Tools like MassTransit in .NET encapsulate this complexity so you can focus on what matters: the business logic.</p>
<p><strong>What about you?</strong> Have you ever dealt with "zombie records" where data exists but downstream services don't know about it? How did you solve it?</p>
<p>👉 Connect with me on <a target="_blank" href="https://www.linkedin.com/in/phelipe-rodovalho">LinkedIn</a> for more insights on .NET, Cloud, and Architecture.</p>
]]></content:encoded></item><item><title><![CDATA[Docker for the .NET Developer: From "It Works on My Machine" to Production Confidence]]></title><description><![CDATA[Hey there, dev! We’ve all been there. That moment of relief when you finish a feature, everything compiles, the tests pass, and you proudly declare: "It works on my machine!". Hours later, chaos ensues: the application crashes in the QA environment, ...]]></description><link>https://byrodovalho.com/docker-for-the-net-developer-from-it-works-on-my-machine-to-production-confidence</link><guid isPermaLink="true">https://byrodovalho.com/docker-for-the-net-developer-from-it-works-on-my-machine-to-production-confidence</guid><category><![CDATA[Docker]]></category><category><![CDATA[Docker compose]]></category><category><![CDATA[.NET]]></category><dc:creator><![CDATA[Phelipe Rodovalho]]></dc:creator><pubDate>Fri, 17 Oct 2025 21:00:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/cckf4TsHAuw/upload/7c2fb6c39f5880042a8a235929c832b7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey there, dev! We’ve all been there. That moment of relief when you finish a feature, everything compiles, the tests pass, and you proudly declare: <strong>"It works on my machine!"</strong>. Hours later, chaos ensues: the application crashes in the QA environment, or worse, a teammate can't even get the project to run.</p>
<p>For years, we've battled environment drift. Different .NET SDK versions, local environment variables that are never versioned, that one-of-a-kind SQL Server instance running on a machine that behaves "uniquely." This lack of consistency isn't just annoying; it's expensive, creates bugs, and kills productivity.</p>
<p>It was against this backdrop that Docker stopped being "that DevOps tool" and became a cornerstone of my .NET development workflow. And its loyal companion, Docker Compose, became the conductor for our local microservices orchestra. Let's unpack how this duo transformed the way we build software.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1760734326131/3f81a3ce-8bfc-4cc6-9098-c5a5b2a0e256.png" alt class="image--center mx-auto" /></p>
<hr />
<h3 id="heading-1-why-bother-environment-as-code">1. Why Bother? Environment as Code</h3>
<p>The first question many .NET developers ask is, "But Visual Studio already handles everything for me. Why do I need Docker?" The answer lies in a shift in mindset: <strong>treating your development environment as code</strong>.</p>
<p>A <code>Dockerfile</code> is the exact, versionable recipe for building the environment where your application will run. There's no more, "you forgot to install dependency X," or, "which SDK version are we using?" It's all right there, in code.</p>
<p>When combined with <strong>Docker Compose</strong>, the power multiplies. You can define not just your API but its entire ecosystem of dependencies in a single file:</p>
<ul>
<li><p><strong>The database:</strong> Need SQL Server or Postgres? Spin up a container for it.</p>
</li>
<li><p><strong>The cache:</strong> Using Redis? That’s just another service in your <code>docker-compose.yml</code>.</p>
</li>
<li><p><strong>Other microservices:</strong> Does your app depend on another team's service? Add it to the compose file.</p>
</li>
</ul>
<p>With a single command—<code>docker-compose up</code>—anyone on the team can recreate the complete, identical development environment. The era of the "10-page setup guide" is over.</p>
<hr />
<h3 id="heading-2-anatomy-of-a-modern-dockerfile-for-net-8">2. Anatomy of a Modern Dockerfile for .NET 8</h3>
<p>The real elegance of using Docker with .NET lies in <strong>multi-stage builds</strong>. Instead of a monolithic file, we create a production line that optimizes for security, size, and performance.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1760734369677/055159d9-7977-467f-90ab-d05fef3263c3.png" alt class="image--center mx-auto" /></p>
<p>Let's break down the stages, inspired by a real-world Azure Functions microservice I built:</p>
<p><strong>Stage 1: The Builder (</strong><code>build</code>) We start with a base image that has the full .NET SDK (<code>mcr.microsoft.com/dotnet/sdk:8.0</code>). It's large and packed with tools, perfect for compiling our code. The trick here is to copy the <code>.csproj</code> files first and run <code>dotnet restore</code>. Thanks to Docker's layer caching, dependencies are only downloaded again if the project files change.</p>
<pre><code class="lang-dockerfile"><span class="hljs-comment"># Build Stage - Uses the full SDK</span>
<span class="hljs-keyword">FROM</span> mcr.microsoft.com/dotnet/sdk:<span class="hljs-number">8.0</span> AS build
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /<span class="hljs-built_in">source</span></span>

<span class="hljs-comment"># Copy and restore dependencies first to leverage caching</span>
<span class="hljs-keyword">COPY</span><span class="bash"> *.sln .</span>
<span class="hljs-keyword">COPY</span><span class="bash"> src/*/*.csproj ./src/</span>
<span class="hljs-keyword">RUN</span><span class="bash"> dotnet restore</span>

<span class="hljs-comment"># Copy the rest of the source code</span>
<span class="hljs-keyword">COPY</span><span class="bash"> . .</span>
</code></pre>
<p><strong>Stage 2: The Validator (</strong><code>test</code>) One of my favorite practices is <strong>embedding unit tests right into the image build process</strong>. Before publishing anything, we create a stage that simply runs <code>dotnet test</code>.</p>
<pre><code class="lang-dockerfile"><span class="hljs-comment"># Test Stage - Ensures quality</span>
<span class="hljs-keyword">FROM</span> build AS test
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /<span class="hljs-built_in">source</span></span>
<span class="hljs-keyword">RUN</span><span class="bash"> dotnet <span class="hljs-built_in">test</span></span>
</code></pre>
<p>If a test fails, the image build fails. This provides fast feedback and a fantastic quality gate.</p>
<p><strong>Stage 3: The Publisher (</strong><code>publish</code>) Here, we use the build output to generate optimized release artifacts.</p>
<pre><code class="lang-dockerfile"><span class="hljs-comment"># Publish Stage - Creates the final artifacts</span>
<span class="hljs-keyword">FROM</span> build AS publish
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /<span class="hljs-built_in">source</span>/src/MyWebApp.Functions</span>
<span class="hljs-keyword">RUN</span><span class="bash"> dotnet publish -c Release -o /app/publish</span>
</code></pre>
<p><strong>Stage 4: The Final Image (</strong><code>runtime</code>) This is the crown jewel. We discard all previous stages and start fresh with a lean, secure runtime image (e.g., <code>mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated8.0</code>). We copy <strong>only</strong> the published artifacts from the <code>publish</code> stage.</p>
<pre><code class="lang-dockerfile"><span class="hljs-comment"># Final Stage - Lean and secure runtime image</span>
<span class="hljs-keyword">FROM</span> mcr.microsoft.com/azure-functions/dotnet-isolated:<span class="hljs-number">4</span>-dotnet-isolated8.<span class="hljs-number">0</span>
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /home/site/wwwroot</span>
<span class="hljs-keyword">COPY</span><span class="bash"> --from=publish /app/publish .</span>
</code></pre>
<p>The result? A small image with a minimal attack surface (no SDK, no source code), ready for production.</p>
<hr />
<h3 id="heading-3-orchestrating-the-local-symphony-with-docker-compose">3. Orchestrating the Local Symphony with Docker Compose</h3>
<p>The <code>Dockerfile</code> gives us the image. The <code>docker-compose.yml</code> file gets it to play along with the rest of the band. Here’s a practical (and simplified) example of how to orchestrate a .NET API with a SQL Server database:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">'3.8'</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-comment"># Our API microservice</span>
  <span class="hljs-attr">my-api:</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">my-clean-api</span>
    <span class="hljs-attr">build:</span>
      <span class="hljs-attr">context:</span> <span class="hljs-string">.</span>
      <span class="hljs-attr">dockerfile:</span> <span class="hljs-string">Dockerfile</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8080:80"</span> <span class="hljs-comment"># Maps container port 80 to port 8080 on the host</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">ASPNETCORE_ENVIRONMENT=Development</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">ConnectionStrings__SqlConnectionString=Server=db;Database=MyDb;User</span> <span class="hljs-string">Id=sa;Password=${DB_PASSWORD};TrustServerCertificate=True</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">db</span> <span class="hljs-comment"># Ensures the database starts first</span>

  <span class="hljs-comment"># Our database dependency</span>
  <span class="hljs-attr">db:</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">local-sql-server</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mcr.microsoft.com/mssql/server:2022-latest</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">ACCEPT_EULA=Y</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">SA_PASSWORD=${DB_PASSWORD}</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"1433:1433"</span> <span class="hljs-comment"># Exposes the SQL Server port to the host machine</span>
</code></pre>
<p><strong>A Quick Analysis:</strong></p>
<ul>
<li><p><code>build</code>: Compose will use our <code>Dockerfile</code> to build the API image.</p>
</li>
<li><p><code>environment</code>: We inject the connection string, pointing to the <code>db</code> service. The password comes from an environment variable, keeping secrets out of the code.</p>
</li>
<li><p><code>depends_on</code>: The API will only attempt to start after the database container is up, preventing startup connection errors.</p>
</li>
<li><p><strong>A single network:</strong> By default, Compose creates a virtual network for these services, allowing them to communicate using their service names (like <code>db</code>).</p>
</li>
</ul>
<p>The complexity of setting up communication between the API and the database is reduced to a few lines of YAML. That’s productivity.</p>
<hr />
<h3 id="heading-conclusion-more-than-a-tool-a-mindset">Conclusion: More Than a Tool, a Mindset</h3>
<p>Adopting Docker in my .NET workflow was a game-changer. The conversation shifted from, "What version of the SDK do you have?" to, "Did you run <code>docker-compose up</code>?"</p>
<p>The benefits are clear and impactful:</p>
<ul>
<li><p><strong>Radical Consistency:</strong> The same environment from dev to test to production.</p>
</li>
<li><p><strong>Isolation:</strong> Multiple projects with different dependencies can run side-by-side without conflict.</p>
</li>
<li><p><strong>Automation and CI/CD:</strong> The <code>Dockerfile</code> becomes the single contract for the build and deploy pipeline.</p>
</li>
<li><p><strong>Confidence:</strong> The artifact you test locally is the <em>exact same</em> immutable artifact that goes to production.</p>
</li>
</ul>
<p>At the end of the day, Docker isn’t about containers. It’s about <strong>predictability</strong>. It's about focusing our time on solving complex business problems, with the peace of mind that comes from knowing the foundation—the environment—just works. Every single time.</p>
<p><em>Clean code is not about beauty — it’s about predictability.</em></p>
<hr />
<p><strong>📣 Let's Keep the Conversation Going!</strong></p>
<p>What's been your experience using Docker with .NET? Have you ever been caught in the "it works on my machine" trap? Share your stories and tips in the comments below!</p>
]]></content:encoded></item><item><title><![CDATA[.NET 8 Azure Functions: The Definitive Guide to Annihilating Cold Starts and Optimizing Performance]]></title><description><![CDATA[It was a quiet Tuesday, until it wasn't. A P95 latency alert fired for one of our most critical APIs—the one that processed new customer orders. There were no failures, no 500 errors. It was something more subtle and dangerous: a creeping slowness. S...]]></description><link>https://byrodovalho.com/net-8-azure-functions-the-definitive-guide-to-annihilating-cold-starts-and-optimizing-performance</link><guid isPermaLink="true">https://byrodovalho.com/net-8-azure-functions-the-definitive-guide-to-annihilating-cold-starts-and-optimizing-performance</guid><category><![CDATA[.NET]]></category><category><![CDATA[Azure]]></category><category><![CDATA[Azure Functions]]></category><category><![CDATA[cold starts]]></category><dc:creator><![CDATA[Phelipe Rodovalho]]></dc:creator><pubDate>Thu, 09 Oct 2025 20:09:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1760040434588/4fd1740c-f79c-4d29-9dcc-c222905cf4e3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It was a quiet Tuesday, until it wasn't. A P95 latency alert fired for one of our most critical APIs—the one that processed new customer orders. There were no failures, no 500 errors. It was something more subtle and dangerous: a creeping slowness. Sales dashboards were taking ages to load, the user experience was degrading by the minute, and support tickets started mentioning a "sluggish checkout." The business was feeling the impact.</p>
<p>Our architecture was, in theory, flawless: distributed microservices running on modern .NET 8 Azure Functions. Scalable, cost-effective, the promise of the serverless future. However, that promise came with a hidden clause, an enemy every cloud engineer knows and fears: the <strong>Cold Start</strong>.</p>
<p>The first, most instinctive reaction is always the same: "Let's scale up, upgrade the service plan, throw more resources at it." It's the brute-force solution, a response that treats the symptom, not the disease. But as senior engineers, we know this approach just masks the real problem, inflates the Azure bill, and robs us of the opportunity to truly understand our system.</p>
<p>We decided to reject the easy path and follow a more rewarding one: optimizing from the inside out. This isn't just a list of tips; it's the chronicle of a performance investigation, a journey into the guts of the .NET runtime in a serverless environment. Let's dive into the 5 strategies we used to not just mitigate, but annihilate the cold start and squeeze every drop of performance from our Azure Functions.</p>
<h2 id="heading-the-real-enemy-unpacking-the-cost-of-bootstrap">The Real Enemy: Unpacking the Cost of Bootstrap</h2>
<p>Before any optimization, a crucial mental model alignment is necessary. The "cold start" isn't a single, mysterious event. It's a process with clear steps, and each one comes at a cost:</p>
<ol>
<li><strong>Infrastructure Allocation:</strong> The Azure platform needs to find and allocate a worker to run your code. This is the "cold start" in its purest form.</li>
<li><strong>Runtime Initialization:</strong> The allocated worker needs to start the .NET runtime process.</li>
<li><strong>Host Building:</strong> The Azure Function host is built, reading configurations and discovering your function's endpoints.</li>
<li><strong>Application Initialization:</strong> This is where <em>your</em> code comes in. The dependency injection (DI) container is built, validated, and all singleton services are instantiated.</li>
<li><strong>Just-in-Time (JIT) Compilation:</strong> On the very first execution, the .NET JIT Compiler translates the intermediate language (IL) code into optimized machine code. This is a "tax" you pay on the first call.</li>
</ol>
<p>Once we understood this, the target of our optimization changed. We weren't fighting an abstract entity called "the cold." We were fighting the <strong>weight of our own bootstrap process</strong>—an enemy that, often, we ourselves create.</p>
<h2 id="heading-strategy-1-controlling-the-environment-when-to-pay-for-predictability">Strategy 1: Controlling the Environment – When to Pay for Predictability</h2>
<p>Let's start with the most direct solution, the one that involves infrastructure. If your function performs a mission-critical task where milliseconds directly impact revenue—like processing a payment or responding to a real-time bid—latency is non-negotiable. This is where the <strong>Azure Functions Premium Plan</strong> becomes a strategic tool.</p>
<p>It allows you to configure "pre-warmed instances," which keep a number of servers ready and waiting for traffic. Essentially, you're paying for someone else to have already absorbed the cold start cost for you.</p>
<h4 id="heading-the-cost-benefit-analysis">The Cost-Benefit Analysis</h4>
<ul>
<li><strong>Technical Decision:</strong> We enabled the Premium Plan with 2 pre-warmed instances as an immediate containment measure for the orders API.</li>
<li><strong>Result:</strong> The P95 latency dropped to predictable and stable levels within minutes. The crisis was contained.</li>
<li><strong>The Trade-off (The Real Lesson):</strong> This is the most expensive solution. You pay for these instances 24/7, whether they are processing traffic or not. It was the right business decision to stop the bleeding, but the wrong long-term engineering decision. <strong>We used the Premium Plan as a painkiller, not the cure.</strong> It bought us time to investigate the root cause, which almost always lies within the code.</li>
</ul>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Feature</td><td>Consumption Plan</td><td>Premium Plan</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Cost Model</strong></td><td>Pay-per-execution</td><td>Pay for allocated resources</td></tr>
<tr>
<td><strong>Cold Start</strong></td><td>Present and variable</td><td>Eliminated with warmed instances</td></tr>
<tr>
<td><strong>VNet Integration</strong></td><td>Limited</td><td>Full</td></tr>
<tr>
<td><strong>Ideal For</strong></td><td>Sporadic workloads</td><td>Low-latency APIs</td></tr>
</tbody>
</table>
</div><h2 id="heading-strategy-2-the-di-container-diet-every-addscoped-has-a-price">Strategy 2: The DI Container Diet – Every <code>AddScoped</code> Has a Price</h2>
<p>One of the biggest and most underestimated causes of slow startup in .NET applications is an overloaded dependency injection (DI) container. Every <code>builder.Services.AddScoped&lt;...&gt;()</code> in your <code>Program.cs</code> or <code>Startup.cs</code> adds a small weight to the container's build and validation time. Alone, they are harmless. Together, they create a significant bottleneck.</p>
<p>We analyzed our <code>Program.cs</code> and the question was blunt: "Do we really need a full ORM, three API clients, a messaging service, and a caching library just to run a simple validation function?" The answer was a resounding "no."</p>
<ul>
<li><p><strong>Technical Decision:</strong></p>
<ol>
<li><strong>Aggressive Dependency Review:</strong> We removed services that weren't strictly necessary for the critical initialization path.</li>
<li><strong>Focused Function Architecture:</strong> Instead of having a single, monolithic Function App with 20 functions and all the world's dependencies, we started splitting it into smaller, focused Apps. One App for order processing (with its database and messaging dependencies) and another for simple webhooks (with almost no dependencies).</li>
<li><strong>Lightweight Libraries:</strong> We replaced "do-it-all" libraries with lighter, more focused alternatives. For instance, instead of a complex validation library, we used <code>FluentValidation</code>, which is extremely optimized.</li>
</ol>
</li>
<li><p><strong>Result:</strong> Just by refactoring the DI, we reduced the initialization time by nearly 30%. The code also became easier to understand and maintain.</p>
</li>
<li><strong>The Trade-off (The Real Lesson):</strong> This requires greater architectural discipline. It's easier to throw everything into a single project, but that initial convenience turns into technical debt. The lesson is to treat your <code>Program.cs</code> as a piece of high-performance code, not a dumping ground for services.</li>
</ul>
<h2 id="heading-strategy-3-shipping-native-code-the-aot-revolution-in-net-8">Strategy 3: Shipping Native Code – The AOT Revolution in .NET 8</h2>
<p>This is where the modernity of .NET 8 really gave us a competitive edge. As we saw, JIT compilation happens during the cold start, translating IL code to machine code. What if we could do that work ahead of time?</p>
<p>That's exactly what <strong>Native Ahead-of-Time (AOT) Compilation</strong> does. During the <em>build</em> process, it compiles your code directly into a native, self-contained executable. The result is a binary that starts up in a fraction of the time.</p>
<ul>
<li><strong>Technical Decision:</strong> We identified an event-processing function, which was called thousands of times per minute and had no complex dependencies, as the perfect candidate for AOT.<pre><code class="lang-xml">  <span class="hljs-tag">&lt;<span class="hljs-name">PropertyGroup</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PublishAot</span>&gt;</span>true<span class="hljs-tag">&lt;/<span class="hljs-name">PublishAot</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">SelfContained</span>&gt;</span>true<span class="hljs-tag">&lt;/<span class="hljs-name">SelfContained</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">PropertyGroup</span>&gt;</span>
</code></pre>
</li>
<li><strong>Result:</strong> The function's startup time plummeted from hundreds of milliseconds to under 10ms. It was the single most impactful optimization we made.</li>
<li><strong>The Trade-off (The Real Lesson):</strong> AOT isn't a universal silver bullet (yet). Compiling ahead of time imposes restrictions. The most significant is limited support for <em>reflection</em>, a mechanism that allows code to inspect and invoke itself at runtime. Many libraries (older JSON serializers, ORMs, DI frameworks) use reflection heavily. Adopting AOT means ensuring your entire dependency ecosystem is compatible ("trim-safe" and "AOT-safe"). It's an architectural choice that requires planning, but for the right scenarios, the performance gain is transformative.</li>
</ul>
<h2 id="heading-strategy-4-performant-observability-taming-application-insights">Strategy 4: Performant Observability – Taming Application Insights</h2>
<p>Who would have thought that the very tool we use to find performance problems could, itself, be a bottleneck? The default Application Insights configuration in Azure Functions is incredibly robust, but that robustness comes at a cost to startup time.</p>
<ul>
<li><strong>Technical Decision:</strong> We fine-tuned our telemetry configuration.<ol>
<li><strong>Adaptive Sampling:</strong> Instead of sending 100% of telemetry data to Application Insights (which can overwhelm the function in high-traffic scenarios), we configured adaptive sampling. It monitors the event rate and intelligently discards excess data to maintain a target traffic volume.<pre><code class="lang-csharp"><span class="hljs-comment">// In Program.cs</span>
builder.Services.Configure&lt;ApplicationInsightsServiceOptions&gt;(options =&gt;
{
    options.EnableAdaptiveSampling = <span class="hljs-literal">true</span>;
});
</code></pre>
</li>
<li><strong>Log Levels:</strong> We adjusted the default log level to <code>Warning</code> in production. The cost of processing and sending thousands of <code>Information</code> logs per second is not negligible.</li>
</ol>
</li>
<li><strong>Result:</strong> We shaved off more precious milliseconds from the bootstrap time and reduced our telemetry ingestion costs.</li>
<li><strong>The Trade-off (The Real Lesson):</strong> You trade total observability granularity for performance and cost. The key is to have a strategy: in a normal state, we operate with sampling and <code>Warning</code> level logging. When an incident occurs, we have the ability to dynamically increase the detail to <code>Information</code> or <code>Debug</code> for in-depth investigations.</li>
</ul>
<h2 id="heading-strategy-5-mastering-connections-beyond-httpclient">Strategy 5: Mastering Connections – Beyond HttpClient</h2>
<p>This tip is a .NET development classic, but its impact is magnified in serverless environments. Inefficiently managing connections to external resources (APIs, databases, caches) is a leading cause of slowness.</p>
<ul>
<li><strong>Technical Decision:</strong> We adopted a strict policy for connection management.<ol>
<li><strong><code>IHttpClientFactory</code> for APIs:</strong> We ensured that any and all HTTP calls were made through clients managed by the singleton <code>IHttpClientFactory</code>. This allows for the reuse of TCP connections, avoiding TLS handshake overhead and socket exhaustion.</li>
<li><strong><code>DbContext Pooling</code> for Databases:</strong> For interactions with SQL Server using Entity Framework Core, we swapped <code>AddDbContext</code> for <code>AddDbContextPool</code>. Instead of creating and destroying a <code>DbContext</code> (a relatively heavy object) for each execution, pooling allows the function to "rent" a ready-made instance and return it at the end, saving precious initialization time.<pre><code class="lang-csharp"><span class="hljs-comment">// In Program.cs</span>
builder.Services.AddDbContextPool&lt;MyDbContext&gt;(options =&gt;
{
    options.UseSqlServer(connectionString);
});
</code></pre>
</li>
</ol>
</li>
<li><strong>Result:</strong> Subsequent calls to external resources after the initial startup became dramatically faster and more reliable.</li>
<li><strong>The Trade-off (The Real Lesson):</strong> There isn't a negative trade-off here; this is simply correct engineering. The lesson is that the fundamentals of robust software development are even <em>more</em> critical in ephemeral, high-concurrency environments like serverless.</li>
</ul>
<h2 id="heading-conclusion-performance-is-a-culture-not-a-project">Conclusion: Performance is a Culture, Not a Project</h2>
<p>After applying this holistic approach, our API not only stabilized but began operating with a consistently low P95 latency, even after returning to the Consumption plan. We turned off the pre-warmed instances, cut our Azure bill, and, most importantly, regained control over our system.</p>
<p>This journey taught us a fundamental lesson: in serverless architectures, <strong>performance is not a luxury or an optimization project; it's a culture that must be embedded in the software design</strong>. Serverless doesn't remove responsibility for performance; it shifts it from server management to the efficiency of the architecture and the code.</p>
<p>Don't wait for the 3 AM alert. Treat every dependency, every service configuration, and every line of your bootstrap code with the importance it deserves. The pursuit of performance forces us to be better engineers: more disciplined, more curious, and more aware of the real-world impact of our work.</p>
<p>Your future self—and your customers—will thank you. ☕</p>
<hr />
<p><strong>📣 Let's Keep the Conversation Going!</strong></p>
<p>What about you? Have you ever battled the cold start monster? What are your favorite strategies and tools for optimizing .NET Functions? Share your war stories in the comments!</p>
<p>👉 Follow me on Hashnode and connect on <a target="_blank" href="https://www.linkedin.com/in/phelipe-rodovalho">LinkedIn</a> for more content on Clean Code, Cloud, and high-performance backend engineering.</p>
]]></content:encoded></item><item><title><![CDATA[Clean Code Series: The Art of Clean Error Handling]]></title><description><![CDATA[Why elegant error handling is non-negotiable in professional software development
Hey dev, ever been jolted awake by a critical production alert? Or lost hours of data because an error was silently ignored? If you shivered just thinking about it, you...]]></description><link>https://byrodovalho.com/clean-code-series-the-art-of-clean-error-handling</link><guid isPermaLink="true">https://byrodovalho.com/clean-code-series-the-art-of-clean-error-handling</guid><category><![CDATA[clean code]]></category><category><![CDATA[Clean Architecture]]></category><category><![CDATA[software development]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Phelipe Rodovalho]]></dc:creator><pubDate>Wed, 11 Jun 2025 01:01:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1749603266716/7bc13c83-27d1-414e-b45e-95a90253cebd.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Why elegant error handling is non-negotiable in professional software development</p>
<p>Hey dev, ever been jolted awake by a critical production alert? Or lost hours of data because an error was silently ignored? If you shivered just thinking about it, you know what I mean.</p>
<p>Many developers treat error handling as a boring chore—a noisy mix of <code>if err != nil</code> or <code>try/catch</code> blocks cluttering our "perfect" logic. But that mindset couldn’t be more wrong. Poor error handling is a recipe for fragile, unpredictable, and dangerous software.</p>
<p>Here’s the good news: writing robust, clear error-handling code isn't about avoiding errors—it's about managing them with elegance and transparency. This is such a crucial topic that Uncle Bob dedicated an entire chapter to it in <em>Clean Code</em> (Chapter 7: Error Handling).</p>
<p>Let’s dive into how we can treat errors as first-class citizens in our codebases. Ready?</p>
<hr />
<h2 id="heading-1-the-most-common-and-dangerous-error-handling-mistakes">1. The Most Common (and Dangerous) Error Handling Mistakes</h2>
<p>Before we fix anything, let’s confess a few sins. Which of these have you been guilty of? (No shame—we’ve all been there!)</p>
<ul>
<li><p><strong>Silently Swallowing Exceptions:</strong> The infamous <code>catch (Exception e) {}</code>. This is like seeing your house on fire, closing your bedroom door, and pretending nothing is wrong. Ignored problems always come back to haunt you.</p>
</li>
<li><p><strong>Returning <code>null</code> or Using Magic Values:</strong> Forcing callers to guess what went wrong. This leads to chains of <code>if (result != null)</code> and bloated defensive code.</p>
</li>
<li><p><strong>Throwing Context-Free or Generic Exceptions:</strong> <code>throw new Exception("Something went wrong!")</code> is about as helpful as a blank map. What happened? Where? Why?</p>
</li>
<li><p><strong>Using Exceptions for Normal Control Flow:</strong> If you're using <code>try/catch</code> to break out of a loop, you're hammering screws. Exceptions are for <em>exceptional</em> events.</p>
</li>
</ul>
<hr />
<h2 id="heading-2-pillars-of-elegant-error-handling">2. Pillars of Elegant Error Handling</h2>
<p>So how do we handle errors the <em>right</em> way? Uncle Bob gives us several principles:</p>
<ul>
<li><p><strong>Prefer Exceptions Over Return Codes:</strong> The main advantage is separation of concerns. The "happy path" stays clean and readable, while error logic is delegated to <code>catch</code> blocks or conditional handlers.</p>
</li>
<li><p><strong>Provide Context With Your Exceptions:</strong> A good exception tells a story. When catching a low-level error (e.g., DB connection), don’t just throw it upward. Wrap it in a higher-level error that explains <em>what</em> you were trying to do. For example: <code>return nil, fmt.Errorf("failed to process payment for order #%d: %w", orderID, dbErr)</code></p>
</li>
<li><p><strong>Define Domain-Specific Exceptions:</strong> Instead of relying on generic language exceptions, create custom ones that reflect your business logic. <code>InsufficientFundsError</code> is more informative than <code>InvalidOperationException</code>.</p>
</li>
<li><p><strong>Eliminate <code>null</code> from APIs Whenever Possible:</strong> Most <code>NullPointerException</code>s are self-inflicted. Eliminating <code>null</code> from your APIs removes an entire class of bugs and simplifies logic.</p>
</li>
</ul>
<hr />
<h2 id="heading-3-refactoring-error-handling-in-go-from-generic-to-graceful">3. Refactoring Error Handling in Go: From Generic to Graceful</h2>
<p>Go’s explicit error handling via <code>if err != nil</code> is a great playground for writing clear error paths.</p>
<p><strong>Before: Obscure and generic error handling</strong></p>
<pre><code class="lang-Go"><span class="hljs-comment">// Returns nil and a generic error, losing all context</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">findUserConfig</span><span class="hljs-params">(userID <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(*Config, error)</span></span> {
    db, err := connectToDatabase()
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Println(<span class="hljs-string">"Database error"</span>) <span class="hljs-comment">// Swallows the original error</span>
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, errors.New(<span class="hljs-string">"internal server error"</span>)
    }

    <span class="hljs-keyword">var</span> config Config
    <span class="hljs-comment">// ... fetch config logic ...</span>
    <span class="hljs-keyword">if</span> err == sql.ErrNoRows {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, <span class="hljs-literal">nil</span> <span class="hljs-comment">// Uses nil to mean "not found"</span>
    }

    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err <span class="hljs-comment">// Returns raw error without context</span>
    }

    <span class="hljs-keyword">return</span> &amp;config, <span class="hljs-literal">nil</span>
}
</code></pre>
<p><strong>Problems:</strong></p>
<ul>
<li>The real database error is logged and lost.</li>
<li>Returns a generic error that reveals nothing.</li>
<li>Uses <code>nil, nil</code> to mean “not found”, forcing ambiguous checks.</li>
<li>Passes through raw errors with no additional context.</li>
</ul>
<p><strong>After: Context-rich, expressive error handling</strong></p>
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> ErrConfigNotFound = errors.New(<span class="hljs-string">"user configuration not found"</span>)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">findUserConfig</span><span class="hljs-params">(userID <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(*Config, error)</span></span> {
    db, err := connectToDatabase()
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, fmt.Errorf(<span class="hljs-string">"failed to connect to database when retrieving config for user %d: %w"</span>, userID, err)
    }

    <span class="hljs-keyword">var</span> config Config
    <span class="hljs-comment">// ... execute query logic ...</span>
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-comment">// You can now handle errors precisely:</span>
        <span class="hljs-keyword">if</span> errors.Is(err, sql.ErrNoRows) {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, ErrConfigNotFound
        }
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, fmt.Errorf(<span class="hljs-string">"query execution failed for user config %d: %w"</span>, userID, err)
    }

    <span class="hljs-keyword">return</span> &amp;config, <span class="hljs-literal">nil</span>
}
</code></pre>
<p><strong>Why it’s better:</strong></p>
<ul>
<li>The error includes what failed and for whom.</li>
<li>“Not found” becomes a clear, domain-specific error—not a silent <code>nil</code>.</li>
<li>Wrapping with <code>%w</code> allows inspection with <code>errors.Is</code> or <code>errors.Unwrap</code>.</li>
</ul>
<hr />
<h2 id="heading-conclusion-treat-errors-as-first-class-citizens">Conclusion: Treat Errors as First-Class Citizens</h2>
<p>This brings us to the end of our Clean Code series. And the final lesson is clear: great software doesn’t avoid errors—it embraces them.</p>
<p>Errors are not exceptions to reality—they are part of it. In complex systems, they’re inevitable, and the way we handle them defines our system's resilience.</p>
<p>So here’s your final challenge: in your next code review, pay extra attention to error-handling blocks. Are they telling a clear, useful story? Or just deferring disaster?</p>
<p>Robust code doesn’t fear its own failures—it anticipates and manages them.</p>
<hr />
<h2 id="heading-lets-keep-the-conversation-going">📣 Let’s Keep the Conversation Going!</h2>
<p>What are your golden rules for handling errors? Got a horror story (or a victory!) about an exception that saved the day? Share it in the comments!</p>
<p>👉  Follow me on <a target="_blank" href="https://www.linkedin.com/in/phelipe-rodovalho/">LinkedIn</a> and Hashnode to keep the conversation alive.</p>
]]></content:encoded></item><item><title><![CDATA[Mastering Go Concurrency with a Real-World Case Study]]></title><description><![CDATA[Hey there, dev! Who hasn't faced a batch process that grinds the application to a halt or takes hours to run? Whether it's processing payments, generating reports, or, in our case today, validating and registering thousands of bank slips (boletos, a ...]]></description><link>https://byrodovalho.com/mastering-go-concurrency-with-a-real-world-case-study</link><guid isPermaLink="true">https://byrodovalho.com/mastering-go-concurrency-with-a-real-world-case-study</guid><category><![CDATA[Golang developer]]></category><category><![CDATA[golang]]></category><category><![CDATA[concurrency]]></category><category><![CDATA[backend developments]]></category><dc:creator><![CDATA[Phelipe Rodovalho]]></dc:creator><pubDate>Tue, 03 Jun 2025 22:40:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748990314575/558fb9ab-b68e-4b7b-ae33-4520346dc6ed.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey there, dev! Who hasn't faced a batch process that grinds the application to a halt or takes hours to run? Whether it's processing payments, generating reports, or, in our case today, validating and registering thousands of <strong>bank slips (<em>boletos</em>, a common payment method in Brazil)</strong>.</p>
<p>In this post, we'll take a real-world problem and turn it into a hands-on lesson on Go's superpower: <strong>concurrency</strong>. We'll go from slow, predictable code to a solution that flies, using the tools the language provides. Ready to get your hands dirty with <code>goroutines</code>, <code>channels</code>, and <code>sync.WaitGroup</code>? Let's dive in!</p>
<h2 id="heading-1-the-scenario-processing-bank-slips-in-a-single-file-the-slow-way">1. The Scenario: Processing Bank Slips in a Single File (The Slow Way)</h2>
<p>Let's start with the basics. We have a massive list of bank slips to process. Each one needs to be validated, perhaps by consulting an external service, and then saved to the database.</p>
<p>The initial code, likely written in a hurry by someone unfamiliar with concurrency, would be a simple <code>for</code> loop.</p>
<pre><code class="lang-Go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ProcessBankSlipsSequentially</span><span class="hljs-params">(slips []BankSlip)</span></span> {
    <span class="hljs-keyword">for</span> _, slip := <span class="hljs-keyword">range</span> slips {
        validateSlip(slip)
        saveToDatabase(slip)
        <span class="hljs-comment">// Imagine each step takes ~100ms</span>
    }
}
</code></pre>
<p>The problem? If we have 10,000 slips and each takes 100ms, we're talking about... well, way too long! We can do better.</p>
<h2 id="heading-2-first-attempt-unleashing-goroutines-controlled-chaos">2. First Attempt: Unleashing Goroutines (Controlled Chaos?)</h2>
<p>The first idea that comes to mind is, "I'll just throw everything into a <code>goroutine</code>!"</p>
<pre><code class="lang-Go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ProcessBankSlipsWithGoroutines</span><span class="hljs-params">(slips []BankSlip)</span></span> {
    <span class="hljs-keyword">for</span> _, slip := <span class="hljs-keyword">range</span> slips {
        <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(s BankSlip)</span></span> {
            validateSlip(s)
            saveToDatabase(s)
        }(slip)
    }
    <span class="hljs-comment">// Now what? How do we know when it's finished?</span>
}
</code></pre>
<p>This is faster, but it creates two problems:</p>
<ol>
<li>The main function exits immediately without waiting for the goroutines to finish.</li>
<li>If we have 1 million slips, are we going to open 1 million database connections at once? That's a recipe for disaster.</li>
</ol>
<p>We need control. This is where <code>sync.WaitGroup</code> comes in.</p>
<h2 id="heading-3-getting-organized-using-syncwaitgroup-to-wait-for-the-crew">3. Getting Organized: Using <code>sync.WaitGroup</code> to Wait for the Crew</h2>
<p>A <code>WaitGroup</code> is like a bouncer at a club. It keeps track of how many guests (<code>goroutines</code>) are inside and only turns off the lights when everyone has left.</p>
<pre><code class="lang-Go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ProcessWithWaitGroup</span><span class="hljs-params">(slips []BankSlip)</span></span> {
    <span class="hljs-keyword">var</span> wg sync.WaitGroup

    <span class="hljs-keyword">for</span> _, slip := <span class="hljs-keyword">range</span> slips {
        wg.Add(<span class="hljs-number">1</span>) <span class="hljs-comment">// "Another guest is coming in."</span>
        <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(s BankSlip)</span></span> {
            <span class="hljs-keyword">defer</span> wg.Done() <span class="hljs-comment">// "Hey bouncer, I'm heading out."</span>
            validateSlip(s)
            saveToDatabase(s)
        }(slip)
    }

    wg.Wait() <span class="hljs-comment">// "Bouncer, wait for everyone to leave before closing up."</span>
    fmt.Println(<span class="hljs-string">"All bank slips processed!"</span>)
}
</code></pre>
<p>Nice! Now we wait. But what about controlling how many goroutines run at the same time? And how do we handle errors? If one slip fails to process, how do we find out?</p>
<h2 id="heading-4-the-complete-solution-worker-pools-with-channels">4. The Complete Solution: Worker Pools with Channels</h2>
<p>This is the icing on the cake and the pattern that solves the problem elegantly and robustly. The idea is to create a fixed number of "workers" that wait for tasks.</p>
<ul>
<li><strong><code>jobs</code> channel:</strong> A channel to send the bank slips that need processing.</li>
<li><strong><code>results</code> channel:</strong> A channel to receive the outcomes (success or error).</li>
<li><strong>Worker Functions:</strong> The <code>goroutines</code> that read from the <code>jobs</code> channel, process the slip, and send the result to the <code>results</code> channel.</li>
</ul>
<pre><code class="lang-Go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ProcessWithWorkerPool</span><span class="hljs-params">(slips []BankSlip)</span></span> {
    numWorkers := <span class="hljs-number">10</span> <span class="hljs-comment">// We control the number of concurrent routines</span>
    jobs := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> BankSlip, <span class="hljs-built_in">len</span>(slips))
    results := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> error, <span class="hljs-built_in">len</span>(slips))
    <span class="hljs-keyword">var</span> wg sync.WaitGroup

    <span class="hljs-comment">// 1. Start the workers</span>
    <span class="hljs-keyword">for</span> w := <span class="hljs-number">1</span>; w &lt;= numWorkers; w++ {
        wg.Add(<span class="hljs-number">1</span>)
        <span class="hljs-keyword">go</span> worker(w, jobs, results, &amp;wg)
    }

    <span class="hljs-comment">// 2. Send the jobs to the channel</span>
    <span class="hljs-keyword">for</span> _, slip := <span class="hljs-keyword">range</span> slips {
        jobs &lt;- slip
    }
    <span class="hljs-built_in">close</span>(jobs) <span class="hljs-comment">// Close the channel to signal there are no more jobs</span>

    <span class="hljs-comment">// 3. Wait for all workers to finish</span>
    wg.Wait()
    <span class="hljs-built_in">close</span>(results)

    <span class="hljs-comment">// 4. Collect and process the results/errors</span>
    <span class="hljs-keyword">for</span> err := <span class="hljs-keyword">range</span> results {
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            log.Printf(<span class="hljs-string">"Error during processing: %v"</span>, err)
        }
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">worker</span><span class="hljs-params">(id <span class="hljs-keyword">int</span>, jobs &lt;-<span class="hljs-keyword">chan</span> BankSlip, results <span class="hljs-keyword">chan</span>&lt;- error, wg *sync.WaitGroup)</span></span> {
    <span class="hljs-keyword">defer</span> wg.Done()
    <span class="hljs-keyword">for</span> j := <span class="hljs-keyword">range</span> jobs {
        fmt.Printf(<span class="hljs-string">"Worker %d processing slip %s\n"</span>, id, j.ID)
        <span class="hljs-comment">// Simulate the work</span>
        err := validateAndProcess(j)
        results &lt;- err
    }
}
</code></pre>
<p><strong>Solution Analysis:</strong>
With this pattern, we have:</p>
<ul>
<li><strong>Controlled Concurrency:</strong> We avoid overwhelming our resources (database, external APIs).</li>
<li><strong>Error Handling:</strong> We capture all errors in a centralized way.</li>
<li><strong>Decoupling:</strong> The logic for distributing work is separate from the execution logic.</li>
<li><strong>Performance:</strong> We efficiently use the power of multi-core processors.</li>
</ul>
<h2 id="heading-conclusion-concurrency-isnt-rocket-science">Conclusion: Concurrency Isn't Rocket Science</h2>
<p>As we've seen, moving from sequential to parallel code in Go is a journey of evolution. We started with a <code>for</code> loop, moved through the "excitement" of unleashing loose goroutines, and arrived at a robust and scalable pattern with <em>worker pools</em>.</p>
<p>Understanding these patterns (<code>WaitGroup</code>, <code>channels</code>, <code>worker pools</code>) is what allows you to build high-performance systems in Go that don't just work—they work fast and safely.</p>
<p>The next time you hit a bottleneck, remember: Go gives you the tools, and now you know how to use them to turn a crawl into a sprint!</p>
<hr />
<p><strong>📣 Let's Keep the Conversation Going!</strong></p>
<p>Have you used a similar pattern in a project? What has been your biggest challenge with concurrency in Go? Drop a comment below and let's chat!</p>
<p>👉 Follow me on Hashnode (or wherever you're reading this) and let's continue this chat on <a target="_blank" href="https://www.linkedin.com/in/phelipe-rodovalho">LinkedIn</a>!</p>
]]></content:encoded></item><item><title><![CDATA[Clean Code Series: Eliminating Duplication (The DRY Principle)]]></title><description><![CDATA[Hey there, dev! That time you thought, "Oh, it's just a tiny piece of code, I'll just copy it here quickly, no big deal"... Ever stopped to think about the snowball effect that small, seemingly harmless act can create? Well, code duplication is like ...]]></description><link>https://byrodovalho.com/clean-code-series-eliminating-duplication-the-dry-principle</link><guid isPermaLink="true">https://byrodovalho.com/clean-code-series-eliminating-duplication-the-dry-principle</guid><category><![CDATA[dry]]></category><category><![CDATA[development]]></category><category><![CDATA[backend developments]]></category><category><![CDATA[backend]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[clean code]]></category><dc:creator><![CDATA[Phelipe Rodovalho]]></dc:creator><pubDate>Sat, 31 May 2025 22:15:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748729604568/565a4a13-7843-4557-bbca-3e82f0233fad.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey there, dev! That time you thought, "Oh, it's just a tiny piece of code, I'll just copy it here quickly, no big deal"... Ever stopped to think about the snowball effect that small, seemingly harmless act can create? Well, code duplication is like that guest who comes for a week and ends up staying for a month: sneaky, and by the time you realize it, it has taken over your house (or your codebase).</p>
<p>But hold on, all is not lost! To combat this silent villain, we have one of the most powerful mantras in software development: <strong>DRY – Don't Repeat Yourself</strong>. And you can bet that Uncle Bob, even though DRY is the star of "The Pragmatic Programmer," abhors duplication in "Clean Code" – it's the famous "smell": Duplicated Code. Shall we understand why this principle is so crucial and how it can save your project (and your sanity)?</p>
<h2 id="heading-1-dry-unveiling-the-secret-of-unique-and-powerful-code">1. DRY: Unveiling the Secret of Unique and Powerful Code</h2>
<p>What does this DRY thing mean in real life? The classic definition is: <em>"Every piece of knowledge must have a single, unambiguous, authoritative representation within a system."</em> Translating to plain English: every little piece of knowledge or logic in your system should live in one single place, without ambiguity, and be the official source of truth.</p>
<p>And we're not just talking about that identical block of code you blatantly copied. DRY goes beyond that! It fights the duplication of:</p>
<ul>
<li>Business logic.</li>
<li>Algorithms.</li>
<li>Constants and magic values.</li>
<li>Data structures.</li>
<li>Configurations.</li>
</ul>
<p><strong>But why on earth is duplication so bad?</strong></p>
<ul>
<li><strong>Maintenance Nightmare:</strong> Need to fix a bug or change a rule? If that logic is scattered in 10 different places, get ready for a frustrating treasure hunt and a high chance of forgetting a spot. Result? Inconsistencies and more bugs!</li>
<li><strong>Bloated and Potentially Slow Code:</strong> More code means more to read, more to compile, more to test, and sometimes, even an impact on performance.</li>
<li><strong>Difficulty in Understanding:</strong> When the same logic appears in slightly different forms in various places, it becomes hard to understand which is the "correct version" or the expected behavior.</li>
<li><strong>Resistance to Change:</strong> A codebase full of duplications becomes a minefield. Any change is a risk.</li>
</ul>
<h2 id="heading-2-the-many-faces-of-duplication-and-how-not-to-fall-for-its-traps">2. The Many Faces of Duplication (And How Not to Fall for Its Traps)</h2>
<p>Duplication is a master of disguise. Keep an eye out:</p>
<ul>
<li><strong>The Classic Copy-Paste:</strong> The most obvious one. You select a snippet, CTRL+C, find another place that needs "something similar," CTRL+V. Sometimes with a small tweak to "disguise" it.</li>
<li><strong>The "Made-Up" Duplication:</strong><ul>
<li><strong>Similar Logic, Small Differences:</strong> Two functions that do 90% the same thing but with one or two different lines. The temptation to duplicate and alter is strong, but dangerous.</li>
<li><strong>Ghost Data Structures:</strong> Multiple classes or structs representing the same entity in slightly different ways.</li>
<li><strong>Magic Values Everywhere:</strong> That string or number appearing in several <code>if</code>s or calculations without any explanation of what it means. If you need to change it, you know the drill, right?</li>
<li><strong>Fragmented Business Knowledge:</strong> A customer's validation rules, for example, scattered across the UI layer, the API, and the service. Which one is the source of truth?</li>
</ul>
</li>
</ul>
<p>Ever caught yourself justifying duplicating an entire function because of a single different line? Yep, it happens in the best of (code) families.</p>
<h2 id="heading-3-anti-repetition-toolkit-your-weapons-in-the-fight-for-dry-code">3. Anti-Repetition Toolkit: Your Weapons in the Fight for DRY Code</h2>
<p>Enough suffering! We have an arsenal of strategies to send duplication into outer space:</p>
<ul>
<li><strong>Functions and Methods:</strong> Your first and most powerful line of defense. See logic repeating? Encapsulate it in a well-named function and reuse it!</li>
<li><strong>Classes, Structs, and Abstractions:</strong> Model your domain so that knowledge is centralized. Create classes that represent unique concepts and carry their own responsibilities.</li>
<li><strong>Inheritance with Wisdom (or Composition as a Powerful Alternative):</strong> Inheritance can help reuse code, but be careful not to create fragile hierarchies and excessive coupling. Often, composition (where a class <em>has</em> another) is a more flexible and safer way to achieve reuse.</li>
<li><strong>Modules, Packages, and Libraries:</strong> Group related and reusable functionalities that can be imported and used in different parts of your system or even in other projects.</li>
<li><strong>Templates and Code Generators (Use With Moderation!):</strong> For code that is <em>truly</em> boilerplate and follows a very rigid pattern, templates can help. But be careful not to generate complexity or code that no one understands how it works "under the hood."</li>
<li><strong>Centralized Configurations:</strong> Constants, URLs, API keys—everything configuration-related should come from a single place (config files, environment variables, etc.).</li>
<li><strong>Design Principles (SOLID, for example):</strong> Following good design principles naturally leads to less duplicated and more modular code.</li>
</ul>
<p><strong>Practical Refactoring Example (Go):</strong></p>
<p>Imagine we have two functions to process orders, one for regular orders and another for VIP orders, with a lot of repeated logic:</p>
<pre><code class="lang-Go"><span class="hljs-comment">// Before: Lots of logic duplication</span>
<span class="hljs-comment">// Assuming Order and Item are defined structs</span>
<span class="hljs-comment">// type Order struct {</span>
<span class="hljs-comment">//     ID    string</span>
<span class="hljs-comment">//     Items []Item</span>
<span class="hljs-comment">//     Total float64</span>
<span class="hljs-comment">// }</span>
<span class="hljs-comment">// type Item struct {</span>
<span class="hljs-comment">//     // item fields</span>
<span class="hljs-comment">// }</span>

<span class="hljs-comment">// func isStockAvailable(items []Item) bool { /* ... */ return true }</span>
<span class="hljs-comment">// func calculateTotal(items []Item) float64 { /* ... */ return 100.0 }</span>
<span class="hljs-comment">// var fmt = import("fmt") // Mocking for example</span>
<span class="hljs-comment">// var errors = import("errors") // Mocking for example</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">processRegularOrder</span><span class="hljs-params">(order Order)</span> <span class="hljs-title">error</span></span> {
    fmt.Println(<span class="hljs-string">"Validating order items:"</span>, order.ID)
    <span class="hljs-comment">// ... item validation logic ...</span>
    <span class="hljs-keyword">if</span> !isStockAvailable(order.Items) {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"insufficient stock"</span>)
    }

    fmt.Println(<span class="hljs-string">"Calculating order total:"</span>, order.ID)
    total := calculateTotal(order.Items)
    <span class="hljs-comment">// ... apply common discounts ...</span>
    order.Total = total

    fmt.Println(<span class="hljs-string">"Registering order in the system:"</span>, order.ID)
    <span class="hljs-comment">// ... logic to save to database ...</span>
    fmt.Println(<span class="hljs-string">"Regular order processed:"</span>, order.ID)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">processVipOrder</span><span class="hljs-params">(order Order)</span> <span class="hljs-title">error</span></span> {
    fmt.Println(<span class="hljs-string">"Validating VIP order items:"</span>, order.ID) <span class="hljs-comment">// Slight difference in log</span>
    <span class="hljs-comment">// ... item validation logic (IDENTICAL) ...</span>
    <span class="hljs-keyword">if</span> !isStockAvailable(order.Items) {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"insufficient stock for VIP"</span>) <span class="hljs-comment">// Slight difference in msg</span>
    }

    fmt.Println(<span class="hljs-string">"Calculating VIP order total:"</span>, order.ID)
    total := calculateTotal(order.Items)
    <span class="hljs-comment">// ... apply common discounts ...</span>
    <span class="hljs-comment">// ... apply ADDITIONAL VIP DISCOUNTS ...</span>
    order.Total = total * <span class="hljs-number">0.9</span> <span class="hljs-comment">// Example VIP discount</span>

    fmt.Println(<span class="hljs-string">"Registering VIP order in the system with priority:"</span>, order.ID) <span class="hljs-comment">// Difference in log and maybe in save logic</span>
    <span class="hljs-comment">// ... logic to save to database with priority ...</span>
    fmt.Println(<span class="hljs-string">"VIP order processed:"</span>, order.ID)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<p><strong>"Before" Analysis:</strong> Notice how much code is almost identical? Validation, base total calculation, and the general structure. If item validation changes, we have to remember to change it in both places!</p>
<p><strong>Refactoring to DRY Code:</strong></p>
<pre><code class="lang-Go"><span class="hljs-comment">// After: Common logic extracted</span>
<span class="hljs-comment">// Assuming Order and Item are defined structs</span>
<span class="hljs-comment">// type Order struct {</span>
<span class="hljs-comment">//     ID    string</span>
<span class="hljs-comment">//     Items []Item</span>
<span class="hljs-comment">//     Total float64</span>
<span class="hljs-comment">// }</span>
<span class="hljs-comment">// type Item struct {</span>
<span class="hljs-comment">//     // item fields</span>
<span class="hljs-comment">// }</span>

<span class="hljs-comment">// func isStockAvailable(items []Item) bool { /* ... */ return true }</span>
<span class="hljs-comment">// func calculateTotal(items []Item) float64 { /* ... */ return 100.0 }</span>
<span class="hljs-comment">// var fmt = import("fmt") // Mocking for example</span>
<span class="hljs-comment">// var errors = import("errors") // Mocking for example</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">validateItemsAndStock</span><span class="hljs-params">(items []Item)</span> <span class="hljs-title">error</span></span> {
    fmt.Println(<span class="hljs-string">"Validating items and stock..."</span>)
    <span class="hljs-comment">// ... item validation logic ...</span>
    <span class="hljs-keyword">if</span> !isStockAvailable(items) {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"insufficient stock"</span>)
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">calculateBaseTotal</span><span class="hljs-params">(items []Item)</span> <span class="hljs-title">float64</span></span> {
    fmt.Println(<span class="hljs-string">"Calculating base total..."</span>)
    <span class="hljs-comment">// ... logic to calculate total WITHOUT specific discounts ...</span>
    <span class="hljs-keyword">return</span> calculateTotal(items)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">saveOrder</span><span class="hljs-params">(order Order, isVip <span class="hljs-keyword">bool</span>)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">if</span> isVip {
        fmt.Println(<span class="hljs-string">"Registering VIP order in the system with priority:"</span>, order.ID)
        <span class="hljs-comment">// ... logic to save to database with priority ...</span>
    } <span class="hljs-keyword">else</span> {
        fmt.Println(<span class="hljs-string">"Registering order in the system:"</span>, order.ID)
        <span class="hljs-comment">// ... logic to save to database ...</span>
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-keyword">type</span> OrderProcessor <span class="hljs-keyword">struct</span> {
    <span class="hljs-comment">// dependencies like notification service, etc.</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(op *OrderProcessor)</span> <span class="hljs-title">Process</span><span class="hljs-params">(order Order, isVip <span class="hljs-keyword">bool</span>)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">if</span> err := validateItemsAndStock(order.Items); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-comment">// Could add more context to the error here if needed</span>
        <span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"validation for order %s failed: %w"</span>, order.ID, err)
    }

    baseTotal := calculateBaseTotal(order.Items)
    order.Total = baseTotal

    <span class="hljs-keyword">if</span> isVip {
        fmt.Println(<span class="hljs-string">"Applying VIP discounts for order:"</span>, order.ID)
        order.Total = baseTotal * <span class="hljs-number">0.9</span> <span class="hljs-comment">// Example VIP discount</span>
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">// ... apply common discounts if any ...</span>
    }

    <span class="hljs-keyword">if</span> err := saveOrder(order, isVip); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"failed to save order %s: %w"</span>, order.ID, err)
    }

    fmt.Printf(<span class="hljs-string">"Order %s (VIP: %t) processed successfully!\n"</span>, order.ID, isVip)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<p><strong>"After" Analysis:</strong> We extracted the common parts (<code>validateItemsAndStock</code>, <code>calculateBaseTotal</code>, <code>saveOrder</code>) into helper functions. The <code>Process</code> function now orchestrates the flow, applying specific logic (VIP discount) when necessary. Much cleaner and safer to maintain, don't you agree?</p>
<h2 id="heading-4-the-dry-family-meet-wet-and-spot">4. The DRY Family: Meet WET and SPOT</h2>
<p>DRY isn't alone in this fight:</p>
<ul>
<li><strong>WET (Write Everything Twice... or We Enjoy Typing 😉):</strong> This is DRY's arch-nemesis. If you find yourself writing the same thing (or almost the same thing) multiple times, you're swimming in WET code. And believe me, that pool isn't refreshing in the long run.</li>
<li><strong>SPOT (Single Point of Truth):</strong> A very close cousin and a fundamental goal. It means that every piece of information or business rule in your system should have ONE SINGLE place where it's defined and maintained as the absolute truth. The DRY principle is one of the main tools to achieve SPOT. If you don't repeat yourself, it's easier to ensure information is correct and consistent in a single point.</li>
</ul>
<h2 id="heading-conclusion-one-code-many-reuses-zero-headaches">Conclusion: One Code, Many Reuses, Zero Headaches!</h2>
<p>My dear dev, embracing DRY isn't just a good practice; it's an investment in your peace of mind and your project's health. Less duplication means code that's easier to understand, faster to modify, less prone to bugs, and much more elegant.</p>
<p>So, the challenge is on: in your next feature, your next code review, put on your DRY detective glasses and mercilessly hunt down any duplication that crosses your path. Your "future self" (and your entire team) will thank you with a warm cup of coffee! ☕</p>
<hr />
<p><strong>📣 The Conversation Doesn't Stop Here!</strong></p>
<p>What are your ninja strategies for keeping code DRY and avoiding WET? Ever faced a duplication monster that gave you chills? Share your stories and tips in the comments!</p>
<p>👉 Follow me on Hashnode (or wherever you're reading this) and let's continue this chat on <a target="_blank" href="https://www.linkedin.com/in/phelipe-rodovalho">LinkedIn</a>!</p>
<p>🔖 <strong>Next Chapter in the Clean Code Saga:</strong> Get ready to master the art of handling the unexpected! We'll dive into the world of elegant and robust Error Handling. Don't miss it!</p>
]]></content:encoded></item><item><title><![CDATA[Serverless for Backend Engineers: What You Need to Know in 5 Minutes]]></title><description><![CDATA[What's up, devs! Today, I'm excited to dive into a key topic in the current software development landscape: Serverless. The widely discussed premise of abstracting infrastructure has significantly evolved. If you haven't yet explored this paradigm or...]]></description><link>https://byrodovalho.com/serverless-for-backend-engineers-what-you-need-to-know-in-5-minutes</link><guid isPermaLink="true">https://byrodovalho.com/serverless-for-backend-engineers-what-you-need-to-know-in-5-minutes</guid><category><![CDATA[serverless]]></category><category><![CDATA[backend]]></category><category><![CDATA[Cloud Computing]]></category><category><![CDATA[software development]]></category><category><![CDATA[System Architecture]]></category><dc:creator><![CDATA[Phelipe Rodovalho]]></dc:creator><pubDate>Thu, 29 May 2025 16:49:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748536609956/9c6487be-dada-4f7b-a9fe-695168b2f7bf.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>What's up, devs! Today, I'm excited to dive into a key topic in the current software development landscape: <strong>Serverless</strong>. The widely discussed premise of abstracting infrastructure has significantly evolved. If you haven't yet explored this paradigm or are seeking a clearer understanding, I invite you to join me for a concise and practical overview.</p>
<h3 id="heading-serverless-is-it-magic-or-engineering">Serverless: Is It Magic or Engineering?</h3>
<p>When we talk about Serverless, the first thing that comes to mind is "no server." But hold on a second! Servers do exist, and plenty of them! The key insight is that <strong>you, as a developer, don't have to worry about provisioning, scaling, or managing that infrastructure</strong>. The cloud provider (AWS Lambda, Azure Functions, Google Cloud Functions, etc.) handles all of that for you. It's like having a 24/7 ops team working for free (or almost!).</p>
<p><strong>In practice, what do you do?</strong> You write your code, define the events that trigger it (a file upload, an HTTP request, a message in a queue, etc.), and the rest is up to the cloud. It's that simple.</p>
<h3 id="heading-why-serverless-is-winning-hearts-and-mine">Why Serverless Is Winning Hearts (and Mine!)?</h3>
<p>My experiences implementing serverless solutions in real-world projects, especially in microservices running on Kubernetes, have shown me that the benefits are quite tangible.</p>
<ol>
<li><strong>Total Focus on Code:</strong> Forget the infra! Less time configuring VMs or containers, more time coding and solving business problems. That's gold!</li>
<li><strong>Automatic and Nearly Infinite Scalability:</strong> Hit a request peak? Your function scales automatically to meet demand. The request disappeared? It scales down, and you don't pay for idle capacity. You pay for what you use, period.</li>
<li><strong>Drastic Cost Reduction:</strong> Ideal for intermittent or peak workloads. Goodbye, servers sitting idle, consuming resources at night or on weekends.</li>
<li><strong>Accelerated Time-to-Market:</strong> With less infra bureaucracy, you deliver features much faster.</li>
</ol>
<h3 id="heading-but-its-not-all-roses-the-challenges-of-serverless">But It's Not All Roses: The Challenges of Serverless</h3>
<p>Okay, not everything is perfect in serverless paradise. As a good engineer, I know every solution has its trade-offs.</p>
<ol>
<li><strong>Observability is a Challenge:</strong> Debugging a distributed and ephemeral environment, where your functions spin up and down in milliseconds, can be a real puzzle. Robust monitoring tools are essential.</li>
<li><strong>"Cold Starts": The Latency Enemy:</strong> If your function isn't invoked for a while, it "sleeps." The first request might suffer a "cold start," a longer delay while the environment is prepared. For high-performance APIs, this can be an issue.</li>
<li><strong>Vendor Lock-in (Beware!):</strong> While your function's code is, in theory, portable, integrations with specific cloud provider services (queues, databases, API gateways) can tie you to an ecosystem. It's good to be aware and plan accordingly.</li>
</ol>
<h3 id="heading-is-serverless-for-me">Is Serverless for Me?</h3>
<p>The answer, as always, is: <strong>it depends!</strong></p>
<p>If you have a project with:</p>
<ul>
<li>Unpredictable or intermittent workloads.</li>
<li>The need to scale rapidly for peaks.</li>
<li>Small teams that need to focus on development without worrying about infra.</li>
<li>Well-defined microservices.</li>
</ul>
<p>... then, yes, Serverless is a very strong candidate!</p>
<p>On the other hand, for traditional monolithic applications, systems with extremely critical latency, or scenarios where you need granular control over every aspect of the server, other architectures might be more suitable.</p>
<p>My experience has shown that, especially for internal communication between microservices, gRPC, for example, is a powerful tool, and when paired with Go or .NET Core, the performance is impressive. However, gRPC isn't always the best fit for public-facing APIs or mobile/web clients, where REST or GraphQL might still be a better choice. The beauty is that Serverless can integrate well with both approaches!</p>
<h3 id="heading-conclusion-where-cloud-meets-agility">Conclusion: Where Cloud Meets Agility</h3>
<p>Serverless is another powerful tool in our software engineers' utility belt. It's not a silver bullet, but it's definitely a trend that's here to stay and, when well-applied, can bring agility, scalability, and cost optimization to your projects.</p>
<p>Think of Serverless as your next step in evolving to worry less about the machine and spend more time building amazing things!</p>
<p><strong>📣 Keep the Conversation Going!</strong></p>
<p>What's your biggest question or experience with Serverless? Share in the comments! What was your biggest "headache" or your greatest victory using Serverless?</p>
<p>👉 Follow me on Hashnode and connect on <a target="_blank" href="https://www.linkedin.com/in/phelipe-rodovalho">LinkedIn</a> for more content.</p>
<p>🔖 If you found this post useful, share it with your team or other developers who value clean, effective code!</p>
]]></content:encoded></item><item><title><![CDATA[Clean Code Series: The Power of Simplicity]]></title><description><![CDATA[Less is More, Even in Code!
Hey there, dev! Ever found yourself scratching your head, looking at a piece of code and wondering, "Seriously, did it need to be this complicated to do something so simple?" Or that time a twenty-line function felt like i...]]></description><link>https://byrodovalho.com/clean-code-series-the-power-of-simplicity</link><guid isPermaLink="true">https://byrodovalho.com/clean-code-series-the-power-of-simplicity</guid><category><![CDATA[clean code]]></category><category><![CDATA[Backend Development]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[development]]></category><category><![CDATA[KISS Principle]]></category><dc:creator><![CDATA[Phelipe Rodovalho]]></dc:creator><pubDate>Fri, 23 May 2025 19:11:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748025461878/71e2d924-349b-4645-bb45-346b45c150cd.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-less-is-more-even-in-code">Less is More, Even in Code!</h2>
<p>Hey there, dev! Ever found yourself scratching your head, looking at a piece of code and wondering, "Seriously, did it need to be <em>this</em> complicated to do something so simple?" Or that time a twenty-line function felt like it required a Ph.D. in quantum physics to understand? Yeah, my friend, you're not alone in this.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748026431564/4d1b7d73-b835-4339-8974-667c9f1bce89.png" alt class="image--center mx-auto" /></p>
<p>In our universe of curly braces, parentheses, and semicolons, we often think complex solutions are synonymous with genius. But, I'm here to inform you (or perhaps relieve you) that, many times, the magic lies in the exact opposite. This is where an old friend comes into play, a true wake-up call against unnecessary complexity: the <strong>KISS</strong> principle – <em>Keep It Simple, Stupid!</em></p>
<p>"Hold on!" you might think. "Uncle Bob doesn't have a chapter with that name in 'Clean Code'!" And you're right. But the beauty of simplicity, the relentless pursuit of code that's easy to read, understand, and maintain, permeates every page of the book and every piece of advice from the master. KISS isn't just an acronym; it's the soul of clarity in development. Shall we unravel why keeping things simple is not only smart but essential?</p>
<h2 id="heading-1-kiss-unmasking-the-stupid-that-saves-projects">1. KISS: Unmasking the "Stupid" that Saves Projects</h2>
<p>First things first, let's set expectations: the "Stupid" in KISS isn't an insult to you, dev! Far from it. The term originated with Lockheed engineer Kelly Johnson, and it was about creating designs so simple that any mechanic, even under pressure and with basic tools, could fix a jet plane. Bringing it to our world: your code should be so simple that any colleague (or yourself six months from now, let's be honest) can understand it without needing an astral chart.</p>
<p>The idea is for the <em>solution</em> to be simple, not for the developer to be simplistic. On the contrary, creating simplicity from complex problems is a huge sign of intelligence!</p>
<p><strong>Why is simple code happy code?</strong></p>
<ul>
<li><p><strong>Effortless Reading and Understanding:</strong> Fewer moving parts, less mental gymnastics to grasp what's going on.</p>
</li>
<li><p><strong>Smooth Maintenance:</strong> Changing or fixing something in a simple system is like changing a lightbulb. In a complex system, it can feel like open-heart surgery.</p>
</li>
<li><p><strong>Tear-Free Debugging:</strong> Finding a bug in straightforward code is infinitely easier.</p>
</li>
<li><p><strong>Tests That Make Sense:</strong> Isolating and testing simple code units is faster and more effective.</p>
</li>
</ul>
<p>Remember the old saying: code is read MUCH more often than it is written. So, how about making life easier for those who come after (including your future self)?</p>
<h2 id="heading-2-the-dark-side-of-cleverness-when-over-engineering-gets-expensive">2. The Dark Side of "Cleverness": When Over-Engineering Gets Expensive</h2>
<p>Ah, the temptation to build a Palace of Versailles when the client only asked for a doghouse... who among us, right? This is the infamous <em>over-engineering</em>. We get excited, want to predict every possible future scenario in the universe, add all imaginable features "just in case," and before we know it, we've turned a simple task into a monster of complexity.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748025858008/949c1095-8cb7-42c6-8a04-836d31b948f3.png" alt class="image--center mx-auto" /></p>
<p><strong>The consequences of this "genius" don't take long to show up:</strong></p>
<ul>
<li><p><strong>Skyrocketing Accidental Complexity:</strong> The system gets bloated with things that don't add real value at the moment.</p>
</li>
<li><p><strong>Skyscraper-High Learning Curve:</strong> New devs on the team? Prepare the welcome kit with painkillers and a survival guide to understand the code.</p>
</li>
<li><p><strong>Camouflaged Bugs:</strong> The more complex, the more places for bugs to hide and haunt you in the middle of the night.</p>
</li>
<li><p><strong>Time and Money Down the Drain:</strong> Precious hours spent developing and maintaining features nobody uses.</p>
</li>
</ul>
<p>It's like trying to kill an ant with a flamethrower. Does it work? Maybe. Is it the best approach? Definitely not. </p>
<h2 id="heading-3-getting-hands-on-simplifying-your-code-with-kiss-in-practice">3. Getting Hands-On: Simplifying Your Code with KISS in Practice</h2>
<p>Alright, I get it. Simplicity is the way. But how do I walk this path?</p>
<ul>
<li><p><strong>Names That Speak for Themselves (Again!):</strong> We've talked about meaningful names, and here they shine again. <code>calculateSimplifiedIncomeTax</code> is better than <code>calcIncTaxSimp</code> or some cryptic acronym. Clarity and simplicity go hand in hand.</p>
</li>
<li><p><strong>Friendly Little Functions:</strong> Remember our chat about small, focused functions? They are the embodiment of KISS! A function that does ONE THING well is the foundation of a simple design.</p>
</li>
<li><p><strong>Say No to Infernal Ifs:</strong> That cascade of nested <code>if/else</code> statements that looks like a maze? Run away from it! Often, you can simplify with <em>guard clauses</em>, polymorphism (if the language and context call for it), or even by rethinking the logic.</p>
</li>
<li><p><strong>The Right Tool for the Right Screw:</strong> Not every problem needs Kubernetes, microservices, and quantum artificial intelligence. Sometimes, a good Python script, a standard Go lib, or a well-written stored procedure solves the mystery with much less fanfare.</p>
</li>
<li><p><strong>YAGNI (You Ain't Gonna Need It) as Your Best Friend:</strong> This is KISS's cousin. The idea is simple: if you don't need it <em>now</em>, don't build it. Don't try to guess the future and cram your code full of features "for when you might need them." This only adds junk and complexity today. Keep the focus on what's essential <em>now</em>.</p>
</li>
</ul>
<h2 id="heading-practical-example-go"><strong>Practical Example (Go):</strong></h2>
<p>Let's imagine a function that validates a user but tries to be too "clever":</p>
<pre><code class="lang-go"><span class="hljs-comment">// Before: Unnecessary complexity</span>
<span class="hljs-comment">// Assuming User struct is defined elsewhere</span>
<span class="hljs-comment">// type User struct {</span>
<span class="hljs-comment">// Name  string</span>
<span class="hljs-comment">// Email string</span>
<span class="hljs-comment">// }</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">validateUser</span><span class="hljs-params">(user User)</span> <span class="hljs-params">(<span class="hljs-keyword">bool</span>, <span class="hljs-keyword">string</span>)</span></span> {
    <span class="hljs-keyword">if</span> user.Name == <span class="hljs-string">""</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>, <span class="hljs-string">"Name is required"</span>
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(user.Name) &lt; <span class="hljs-number">3</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>, <span class="hljs-string">"Name too short"</span>
        } <span class="hljs-comment">// else {} // Assume "" means "no errors so far"</span>
    }

    <span class="hljs-keyword">if</span> user.Email == <span class="hljs-string">""</span> {
        <span class="hljs-comment">// Could have a pending name error here, but the logic gets confusing</span>
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>, <span class="hljs-string">"Email is required"</span>
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">if</span> !strings.Contains(user.Email, <span class="hljs-string">"@"</span>) {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>, <span class="hljs-string">"Invalid email"</span>
        }
    }
    <span class="hljs-comment">// ... more nested validations ...</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>, <span class="hljs-string">""</span> <span class="hljs-comment">// "No errors"</span>
}
</code></pre>
<p><strong>"Before" Analysis:</strong> This function mixes validation logic with returning multiple values (boolean and error string), and uses else in a way that makes it hard to track the first error found.</p>
<hr />
<h2 id="heading-refactoring-with-kiss-and-a-touch-of-guard-clauses"><strong>Refactoring with KISS (and a touch of <em>guard clauses</em>):</strong></h2>
<pre><code class="lang-go"><span class="hljs-comment">// After: Simple and direct</span>
<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"errors"</span>
    <span class="hljs-string">"strings"</span>
)

<span class="hljs-comment">// Assuming User struct is defined elsewhere</span>
<span class="hljs-keyword">type</span> User <span class="hljs-keyword">struct</span> {
    Name  <span class="hljs-keyword">string</span>
    Email <span class="hljs-keyword">string</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">validateUserName</span><span class="hljs-params">(name <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">if</span> name == <span class="hljs-string">""</span> {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"name is required"</span>)
    }
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(name) &lt; <span class="hljs-number">3</span> {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"name must be at least 3 characters"</span>)
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">validateUserEmail</span><span class="hljs-params">(email <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">if</span> email == <span class="hljs-string">""</span> {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"email is required"</span>)
    }
    <span class="hljs-keyword">if</span> !strings.Contains(email, <span class="hljs-string">"@"</span>) { <span class="hljs-comment">// Simple example, ideally use regex or lib</span>
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"invalid email format"</span>)
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">validateUser</span><span class="hljs-params">(user User)</span> []<span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">var</span> errs []error <span class="hljs-comment">// Renamed from 'erros' to 'errs' for common Go style</span>

    <span class="hljs-keyword">if</span> err := validateUserName(user.Name); err != <span class="hljs-literal">nil</span> {
        errs = <span class="hljs-built_in">append</span>(errs, err)
    }
    <span class="hljs-keyword">if</span> err := validateUserEmail(user.Email); err != <span class="hljs-literal">nil</span> {
        errs = <span class="hljs-built_in">append</span>(errs, err)
    }
    <span class="hljs-comment">// ... call other specific validation functions ...</span>

    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(errs) &gt; <span class="hljs-number">0</span> {
        <span class="hljs-keyword">return</span> errs
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span> <span class="hljs-comment">// Or return an empty slice of errors: return []error{}</span>
}
</code></pre>
<p><strong>"After" Analysis:</strong> We separated each validation responsibility into its own function (look, small functions again!). The <code>validateUser</code> function now just orchestrates and collects errors. It's much easier to read, test, and add new validations without banging your head against the wall. Returning a list of errors is also cleaner than the boolean/string pair.</p>
<p>Felt the relief just by looking at it? Have you ever encountered code that looked like a baroque painting when a simple haiku would have sufficed?</p>
<h2 id="heading-4-kiss-and-yagni-the-dynamic-duo-of-practicality">4. KISS and YAGNI: The Dynamic Duo of Practicality</h2>
<p>As I briefly mentioned above, YAGNI (You Ain't Gonna Need It) is KISS's right-hand man. While KISS tells you to do it the simplest way, YAGNI advises you to do <em>nothing</em> if there isn't a real, immediate need.</p>
<p>Avoid "guesswork engineering," that habit of trying to predict all future project twists and turns and immediately implementing defenses and features for scenarios that might never happen. This only bloats the code, increases the surface area for bugs, and makes life harder for everyone. Implement what's necessary today. Tomorrow, if something new comes up, you add it – and try to do it the simplest way, of course!</p>
<h2 id="heading-conclusion-simplify-or-die-trying-to-maintain-the-code-that-is">💡 Conclusion: Simplify or Die Trying (To Maintain the Code, That Is)</h2>
<p>My dear dev friend, simplicity in code isn't a sign of laziness or lack of knowledge. On the contrary, it's a sign of maturity, of someone who understands that clarity and maintainability are the true superpowers in our daily grind. Simple code is elegant, robust, and, above all, respectful to whoever will interact with it in the future – be it your teammate or yourself a few months down the line, trying to understand that "brilliant hack" you pulled off in a hurry.</p>
<p>So, here's the challenge: in your next line of code, your next function, your next feature, stop and ask: <strong>"What is the SIMPLEST way to solve this, while maintaining correctness and clarity?"</strong></p>
<p>The journey to simplicity is ongoing, a daily effort. But the rewards – fewer bugs, less stress, more time for coffee and for truly innovating – are worth every second invested.</p>
<hr />
<p><strong>📣 Let's Keep the Conversation Going!</strong></p>
<p>So, what are your biggest battles or greatest victories in the art of keeping code simple? Have you fallen into the over-engineering trap or saved the day with an elegantly KISS solution?</p>
<p>Share your stories and tips in the comments below! And if you liked this post, share it with your team!</p>
<p>👉 Follow me here on Hashnode (or wherever you're reading this!) and let's connect on <a target="_blank" href="https://www.linkedin.com/in/phelipe-rodovalho">LinkedIn</a>!</p>
<p>🔖 <strong>Next Up in the Clean Code Series:</strong> Get ready to declare war on CTRL+C / CTRL+V! We'll talk about the DRY (Don't Repeat Yourself) principle and how code duplication can be a silent villain in your codebase. Stay tuned!</p>
]]></content:encoded></item><item><title><![CDATA[Clean Code Series: Writing Self-Documenting Code]]></title><description><![CDATA[When reading code, the what should be obvious, the how should be discoverable, and the why should be rare but justified.
In this post, we explore the concept of self-documenting code: code that clearly communicates its purpose without relying on exce...]]></description><link>https://byrodovalho.com/clean-code-series-writing-self-documenting-code</link><guid isPermaLink="true">https://byrodovalho.com/clean-code-series-writing-self-documenting-code</guid><category><![CDATA[clean code]]></category><category><![CDATA[Clean Architecture]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[golang]]></category><category><![CDATA[C#]]></category><category><![CDATA[.NET]]></category><category><![CDATA[readability]]></category><category><![CDATA[best practices]]></category><dc:creator><![CDATA[Phelipe Rodovalho]]></dc:creator><pubDate>Mon, 19 May 2025 22:52:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747694141154/4f62f73e-31fc-4e99-b61d-13fed43fd1ca.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When reading code, the <em>what</em> should be obvious, the <em>how</em> should be discoverable, and the <em>why</em> should be rare but justified.</p>
<p>In this post, we explore the concept of self-documenting code: code that clearly communicates its purpose without relying on excessive or outdated comments. This is a foundational principle of clean, maintainable software.</p>
<h2 id="heading-1-what-is-self-documenting-code">1. What is Self-Documenting Code?</h2>
<p>Self-documenting code <strong>communicates intent through structure, naming, and clarity</strong>, eliminating the need for most comments.</p>
<p>It relies on:</p>
<ul>
<li><strong>Descriptive names</strong> for variables, functions, and types  </li>
<li><strong>Clear logic flow</strong> and readable control structures  </li>
<li><strong>Well-structured abstractions</strong> that group behavior meaningfully</li>
</ul>
<p>If someone can understand what your code does without reading a comment, that’s self-documenting.</p>
<h3 id="heading-example">Example:</h3>
<pre><code class="lang-go"><span class="hljs-comment">// Bad: requires comment to clarify intent</span>
<span class="hljs-comment">// Check if user is an admin</span>
<span class="hljs-keyword">if</span> u.Role == <span class="hljs-number">1</span> {
    <span class="hljs-comment">// ...</span>
}

<span class="hljs-comment">// Better: self-documenting</span>
<span class="hljs-keyword">if</span> u.IsAdmin() {
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<h2 id="heading-2-the-problem-with-excessive-or-misleading-comments">2. The Problem with Excessive or Misleading Comments</h2>
<p>Comments aren’t inherently bad—but <strong>most are unnecessary, and many are harmful</strong> when they fall out of sync with the code.</p>
<h3 id="heading-common-issues">Common issues:</h3>
<ul>
<li><p><strong>Outdated comments</strong>:  </p>
<blockquote>
<p>"This uses AES-128 encryption" → but the code now uses AES-256.</p>
</blockquote>
</li>
<li><p><strong>Obvious comments</strong>:  </p>
<pre><code class="lang-python">i += <span class="hljs-number">1</span>  <span class="hljs-comment"># Increment i by one  ❌</span>
</code></pre>
</li>
<li><p><strong>Noise</strong>: comments that clutter rather than clarify.</p>
</li>
</ul>
<p>The more your code <em>relies</em> on comments, the more fragile your understanding becomes over time.</p>
<h2 id="heading-3-when-comments-are-acceptable">3. When Comments Are Acceptable</h2>
<p>Use comments <strong>only when the code cannot express something clearly</strong> on its own—especially when explaining <em>why</em>, not <em>what</em>.</p>
<p>✅ <strong>Acceptable comment use cases:</strong></p>
<ul>
<li><strong>Justifying a non-obvious decision</strong><pre><code class="lang-go"><span class="hljs-comment">// Using polling due to webhook unreliability (see issue #123)</span>
</code></pre>
</li>
<li><strong>Linking to external specifications</strong><pre><code class="lang-go"><span class="hljs-comment">// Follows RFC 7519 for JWT validation</span>
</code></pre>
</li>
<li><strong>Clarifying complex business rules that can't be modeled directly</strong></li>
</ul>
<p>In short: <em>comment the why, not the what.</em></p>
<h2 id="heading-4-how-to-make-code-self-explaining">4. How to Make Code Self-Explaining</h2>
<p>Achieving self-documenting code is a discipline. These are key strategies:</p>
<h3 id="heading-use-meaningful-names">✅ Use Meaningful Names</h3>
<p>Bad:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">calc</span>(<span class="hljs-params">d, f</span>):</span> ...
</code></pre>
<p>Good:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">calculate_final_price</span>(<span class="hljs-params">discount, fees</span>):</span> ...
</code></pre>
<h3 id="heading-keep-functions-small-and-focused">✅ Keep Functions Small and Focused</h3>
<p>Small functions are easier to name and understand.</p>
<p>Bad:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Process</span><span class="hljs-params">(data []<span class="hljs-keyword">byte</span>)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-comment">// parsing, validating, persisting, logging...</span>
}
</code></pre>
<p>Good:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Process</span><span class="hljs-params">(data []<span class="hljs-keyword">byte</span>)</span> <span class="hljs-title">error</span></span> {
    parsed := parseData(data)
    <span class="hljs-keyword">if</span> err := validate(parsed); err != <span class="hljs-literal">nil</span> { <span class="hljs-keyword">return</span> err }
    <span class="hljs-keyword">return</span> persist(parsed)
}
</code></pre>
<h3 id="heading-use-clear-abstractions">✅ Use Clear Abstractions</h3>
<p>Group related behavior meaningfully.</p>
<p>Instead of:</p>
<pre><code class="lang-go">user.SetRole(<span class="hljs-number">1</span>)
</code></pre>
<p>Use:</p>
<pre><code class="lang-go">user.PromoteToAdmin()
</code></pre>
<h2 id="heading-5-before-and-after-example">5. Before and After Example</h2>
<h3 id="heading-before-with-unnecessary-comment">❌ Before (with unnecessary comment):</h3>
<pre><code class="lang-python"><span class="hljs-comment"># Check if the order total is greater than the discount threshold</span>
<span class="hljs-keyword">if</span> order.total &gt; <span class="hljs-number">100</span>:
    apply_discount(order)
</code></pre>
<h3 id="heading-after-self-documenting">✅ After (self-documenting):</h3>
<pre><code class="lang-python"><span class="hljs-keyword">if</span> order.qualifies_for_discount():
    order.apply_discount()
</code></pre>
<p>This version is cleaner, more readable, and the comment becomes unnecessary.</p>
<h2 id="heading-6-key-takeaways">6. Key Takeaways</h2>
<ul>
<li>Comments should be a <strong>last resort</strong>, not a first instinct.</li>
<li>Aim to write code that <strong>explains itself</strong> through naming, structure, and abstraction.</li>
<li>Use comments <strong>only to document intent, non-obvious decisions, or external context</strong>.</li>
<li>Self-documenting code leads to <strong>cleaner diffs, easier reviews, and fewer bugs</strong>.</li>
</ul>
<hr />
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Writing self-documenting code isn’t just about style—it’s a communication skill that separates good developers from great ones.</p>
<p>Next time you write a comment, ask yourself: <em>“Could I improve the code instead?”</em><br />Your future self—and your team—will thank you.</p>
<hr />
<p><strong>📣 Keep the Conversation Going!</strong></p>
<p>What are your challenges or strategies with self-documenting code? Share your thoughts in the comments!</p>
<p>👉 Follow me on Hashnode and connect on <a target="_blank" href="https://www.linkedin.com/in/phelipe-rodovalho">LinkedIn</a> for more insights on Clean Code, and Cloud.</p>
<p>🔖 If you found this article useful, share it with your team or fellow developers who value clean, maintainable code!</p>
<p>🔗 Previous article: <a target="_blank" href="https://byrodovalho.com/clean-code-series-the-principle-of-small-functions">Clean Code Series: The Principle of Small Functions</a></p>
]]></content:encoded></item><item><title><![CDATA[🚀 gRPC in Microservices: Go vs .NET in Real-World Projects]]></title><description><![CDATA[In recent months, I’ve implemented gRPC in both Go and .NET environments, building internal APIs for microservices running on Kubernetes. Here’s what stood out across both stacks:
✅ Key Benefits of gRPC:

Strongly-typed APIs with .proto contracts

Na...]]></description><link>https://byrodovalho.com/grpc-in-microservices-go-vs-net-in-real-world-projects</link><guid isPermaLink="true">https://byrodovalho.com/grpc-in-microservices-go-vs-net-in-real-world-projects</guid><category><![CDATA[architecture]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[backend]]></category><category><![CDATA[cloud native]]></category><category><![CDATA[Microservices]]></category><category><![CDATA[gRPC]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[golang]]></category><category><![CDATA[C#]]></category><category><![CDATA[.NET]]></category><dc:creator><![CDATA[Phelipe Rodovalho]]></dc:creator><pubDate>Wed, 14 May 2025 22:55:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747263191592/4439e069-e35b-4c9f-8f86-359d58f2b33a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In recent months, I’ve implemented gRPC in both Go and .NET environments, building internal APIs for microservices running on Kubernetes. Here’s what stood out across both stacks:</p>
<h3 id="heading-key-benefits-of-grpc"><strong>✅ Key Benefits of gRPC:</strong></h3>
<ul>
<li><p>Strongly-typed APIs with .proto contracts</p>
</li>
<li><p>Native HTTP/2 support (multiplexing, lower latency)</p>
</li>
<li><p>Built-in streaming (unary, server/client/bidi)</p>
</li>
<li><p>Auto-generated client/server code, reducing boilerplate and ensuring consistency</p>
</li>
</ul>
<h3 id="heading-go-vs-net-in-grpc-projects"><strong>⚙️ Go vs .NET in gRPC Projects</strong></h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Feature</strong></td><td><strong>Go</strong></td><td><strong>.NET</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>Dev experience</strong></td><td>Lightweight, fast builds</td><td>Mature tooling, strong IDE integration</td></tr>
<tr>
<td><strong>Performance</strong></td><td>Extremely low memory/CPU overhead</td><td>Very efficient with Kestrel + gRPC</td></tr>
<tr>
<td><strong>Learning curve</strong></td><td>Straightforward with idiomatic Go</td><td>Familiar to C# devs, but setup can vary</td></tr>
<tr>
<td><strong>Tooling</strong></td><td>grpc-gateway, buf</td><td>Visual Studio tooling, protobuf-net/grpc</td></tr>
</tbody>
</table>
</div><h3 id="heading-real-world-impact"><strong>🛠️ Real-World Impact:</strong></h3>
<p>Switching from REST to gRPC in internal Go services resulted in:</p>
<ul>
<li><p>⚡ ~50% smaller payloads</p>
</li>
<li><p>⏱️ 2-3x faster internal communication</p>
</li>
<li><p>🧼 Versioned, contract-driven design shared across teams</p>
</li>
</ul>
<h3 id="heading-limitations"><strong>👀 Limitations:</strong></h3>
<p>gRPC isn’t always the best fit for public-facing APIs or mobile/web clients, where REST or GraphQL might still be a better choice.</p>
<p>However, for internal microservices communication, gRPC is a powerful tool — especially when paired with Go or .NET Core.</p>
<h3 id="heading-bonus-tip"><strong>🔍 Bonus tip:</strong></h3>
<p>grpc-gateway (Go) and grpc-json-transcoding (.NET) make it easy to expose gRPC as REST endpoints.</p>
<p>👉 Are you using gRPC with Go or .NET? Share your experience!</p>
]]></content:encoded></item><item><title><![CDATA[Clean Code Series: The Principle of Small Functions]]></title><description><![CDATA[Have you ever been lost in a function with hundreds of lines and unclear responsibilities? Long functions are a sign that the code is going to be a maintenance headache. In the last article, we talked about the power of meaningful names. Now, let's t...]]></description><link>https://byrodovalho.com/clean-code-series-the-principle-of-small-functions</link><guid isPermaLink="true">https://byrodovalho.com/clean-code-series-the-principle-of-small-functions</guid><category><![CDATA[golang]]></category><category><![CDATA[clean code]]></category><category><![CDATA[Cloud Computing]]></category><category><![CDATA[distributed system]]></category><category><![CDATA[AWS]]></category><category><![CDATA[backend]]></category><category><![CDATA[architecture]]></category><dc:creator><![CDATA[Phelipe Rodovalho]]></dc:creator><pubDate>Wed, 07 May 2025 00:39:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746577993377/10ba0f1a-4b07-42ab-95b1-b49e043f7d98.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you ever been lost in a function with hundreds of lines and unclear responsibilities? Long functions are a sign that the code is going to be a maintenance headache. In the last article, we talked about the power of meaningful names. Now, let's take a look at the solution to giant functions: the small functions principle.</p>
<p>Breaking your code into smaller, focused functions is a key strategy for writing robust, testable, and maintainable software.</p>
<h2 id="heading-the-hidden-cost-of-giant-functions">The Hidden Cost of Giant Functions</h2>
<p>At first glance, grouping lots of logic into a single function might seem efficient. After all, everything’s in one place, right? Not quite. This convenience hides a cost known as <strong>accidental complexity</strong>. As functions grow, they turn into mazes where every new line makes understanding and maintaining the code exponentially harder.</p>
<p>Imagine searching for a specific tool in a huge, messy box full of unrelated items. That’s exactly what it’s like navigating a long function.</p>
<p>The issues are evident:</p>
<ul>
<li><p><strong>Hard to Read:</strong> High cognitive load.</p>
</li>
<li><p><strong>Difficult to Test:</strong> Isolating logic is almost impossible.</p>
</li>
<li><p><strong>More Bugs:</strong> Changes can have unexpected side effects.</p>
</li>
<li><p><strong>Poor Reusability:</strong> Specific logic gets trapped.</p>
</li>
<li><p><strong>SRP Violation:</strong> The function does more than one thing.</p>
</li>
</ul>
<p>It’s like technical debt: harmless at first, but costly in the long run-and it can bring entire projects down.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746134519866/28fcb9ad-dfd7-4df6-8258-f65ae8cab62a.png" alt="Big Functions = Big Problems. Small Functions = Clarity" class="image--center mx-auto" /></p>
<h2 id="heading-the-power-of-conciseness-why-small-functions-shine">The Power of Conciseness: Why Small Functions Shine</h2>
<p>Keeping your functions short and focused brings a cascade of benefits that directly impact your code’s health and your team’s productivity:</p>
<ul>
<li><p><strong>Instant Readability:</strong> A short function with a meaningful name (remember our last article?) reveals its purpose almost instantly.</p>
</li>
<li><p><strong>Simpler Testing:</strong> Testing a unit that does one thing is much easier (think unit tests).</p>
</li>
<li><p><strong>Reusability:</strong> Well-defined, single-purpose functions are natural candidates for reuse.</p>
</li>
<li><p><strong>Easier Maintenance:</strong> Changes are safer and quicker. When a bug appears or requirements change, it’s much faster and safer to locate and update the relevant logic in a small function.</p>
</li>
</ul>
<p>In short, small functions turn intimidating monoliths into manageable building blocks-like Lego pieces you can easily understand, test, combine, and modify.</p>
<h2 id="heading-finding-the-right-size-how-small-is-small-enough">Finding the Right Size: How Small Is Small Enough?</h2>
<p>We get it-smaller functions are better. But how small is <em>really</em> small? Is there a magic number of lines?</p>
<p>The short answer: <strong>there’s no magic number, but there is a guiding principle.</strong></p>
<p>The golden rule, echoing the Single Responsibility Principle (SRP), is that <strong>a function should do one thing, and do it well.</strong> If you can describe what a function does with a single, clear verb-without using “and” or “or”-you’re probably on the right track.</p>
<ul>
<li>Metrics like 5–15 lines are guidelines, not strict rules. Clarity and single responsibility matter more.</li>
</ul>
<p>Another key concept is <strong>level of abstraction</strong>. A function should operate at a single level. This means the operations inside should be at a similar level of detail. For example, a high-level function might coordinate calls to other, more detailed functions. Mixing high-level ideas with low-level details in the same function makes it confusing and hard to read.</p>
<p><strong>So, when evaluating a function’s size, ask yourself:</strong></p>
<ul>
<li><p>Does it do only one thing?</p>
</li>
<li><p>Does its name clearly describe that one responsibility?</p>
</li>
<li><p>Does it operate at a single level of abstraction?</p>
</li>
<li><p>Can I easily test it in isolation?</p>
</li>
</ul>
<p>Clarity, cohesion, and single responsibility are far more important than sticking to an arbitrary line count.</p>
<h2 id="heading-from-monolith-to-modules-refactoring-in-practice">From Monolith to Modules: Refactoring in Practice</h2>
<p>Theory is great, but nothing beats a practical example. Let’s refactor a <code>registerUser</code> function that validates data, checks for duplicates, and saves to the database.</p>
<p><strong>“Before” Code (Go):</strong></p>
<pre><code class="lang-go"><span class="hljs-comment">// Monolithic function</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">registerUser</span><span class="hljs-params">(name, email, password, confirmPassword <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-comment">// 1. Field validations</span>
    <span class="hljs-keyword">if</span> name == <span class="hljs-string">""</span> || email == <span class="hljs-string">""</span> <span class="hljs-comment">/* ... more validations ... */</span> {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"validation error"</span>)
    }

    <span class="hljs-comment">// 2. Email format validation</span>
    _, err := mail.ParseAddress(email)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        fmt.Println(<span class="hljs-string">"Error: Invalid email"</span>)
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"invalid email format"</span>)
    }

    <span class="hljs-comment">// 3. Password validation</span>
    <span class="hljs-keyword">if</span> password != confirmPassword {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"passwords do not match"</span>)
    }

    <span class="hljs-comment">// 4. Check if email already exists in DB</span>
    exists, err := checkEmailExists(email)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { <span class="hljs-keyword">return</span> err }
    <span class="hljs-keyword">if</span> exists { <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"email already registered"</span>) }

    <span class="hljs-comment">// 5. Normalize name</span>
    processedName := normalizeUserName(name)

    <span class="hljs-comment">// 6. Save to DB</span>
    err = saveUserToDatabase(processedName, email)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { <span class="hljs-keyword">return</span> err }

    fmt.Println(<span class="hljs-string">"User registered!"</span>)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<p><strong>Analysis:</strong></p>
<p>This <code>registerUser</code> function does <em>too</em> many things:</p>
<ol>
<li><p>Validates basic fields.</p>
</li>
<li><p>Validates email format.</p>
</li>
<li><p>Checks password rules.</p>
</li>
<li><p>Checks for duplicate emails.</p>
</li>
<li><p>Processes/normalizes the name.</p>
</li>
<li><p>Prepares and executes the database insert.</p>
</li>
<li><p>Logs messages at various stages.</p>
</li>
</ol>
<p>It mixes validation, business logic, data processing, and database interaction. Testing just the password validation, for example, is impossible without running everything else. Reading it is tough, since you have to follow multiple levels of indentation and responsibilities.</p>
<p><strong>Refactoring: Applying the Small Functions Principle</strong></p>
<p>Let’s extract each responsibility into its own function:</p>
<p><strong>“After” Code (Go):</strong></p>
<pre><code class="lang-go"><span class="hljs-comment">// --- Validation Functions ---</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">validateRequiredFields</span><span class="hljs-params">(name, email, password <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">if</span> name == <span class="hljs-string">""</span> || email == <span class="hljs-string">""</span> || password == <span class="hljs-string">""</span> {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"missing required fields"</span>)
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">validateEmailFormat</span><span class="hljs-params">(email <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">error</span></span> {
    _, err := mail.ParseAddress(email)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"invalid email format"</span>)
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">validatePassword</span><span class="hljs-params">(password, confirmPassword <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(password) &amp;lt; <span class="hljs-number">8</span> {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"password must be at least 8 characters"</span>)
    }
    <span class="hljs-keyword">if</span> password != confirmPassword {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"passwords do not match"</span>)
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-comment">// --- Processing Functions ---</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">normalizeUserName</span><span class="hljs-params">(name <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">return</span> strings.TrimSpace(strings.Title(strings.ToLower(name)))
}
<span class="hljs-comment">// ... checkEmailExists and saveUserToDatabase omitted for brevity ...</span>

<span class="hljs-comment">// --- Orchestrator Function ---</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">registerUserRefactored</span><span class="hljs-params">(name, email, password, confirmPassword <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">error</span></span> {
    fmt.Println(<span class="hljs-string">"Starting registration..."</span>)

    <span class="hljs-comment">// 1. Validations</span>
    <span class="hljs-keyword">if</span> err := validateRequiredFields(name, email, password); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"validation failed: %w"</span>, err)
    }
    <span class="hljs-keyword">if</span> err := validateEmailFormat(email); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"validation failed: %w"</span>, err)
    }
    <span class="hljs-keyword">if</span> err := validatePassword(password, confirmPassword); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"validation failed: %w"</span>, err)
    }

    <span class="hljs-comment">// 2. Check uniqueness</span>
    exists, err := checkEmailExists(email)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"error checking email"</span>) }
    <span class="hljs-keyword">if</span> exists { <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"email already registered"</span>) }

    <span class="hljs-comment">// 3. Process</span>
    processedName := normalizeUserName(name)

    <span class="hljs-comment">// 4. Save</span>
    <span class="hljs-keyword">if</span> err := saveUserToDatabase(processedName, email); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"error saving user"</span>)
    }

    fmt.Println(<span class="hljs-string">"User registered successfully:"</span>, processedName)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<p><strong>Visible Benefits:</strong></p>
<p>The <code>registerUserRefactored</code> function just orchestrates. Each step (validation, checking, processing, saving) is now a separate, clear, testable, and potentially reusable function. The complexity is distributed.</p>
<h2 id="heading-conclusion-small-pieces-strong-code">Conclusion: Small Pieces, Strong Code</h2>
<p>Breaking the habit of writing long functions in favor of small, focused units is one of the most impactful changes you can make to improve your code’s quality. As we’ve seen, the benefits go far beyond aesthetics:</p>
<ul>
<li><p><strong>More readable and understandable code</strong></p>
</li>
<li><p><strong>Easier, more effective unit testing</strong></p>
</li>
<li><p><strong>Greater potential for logic reuse</strong></p>
</li>
<li><p><strong>Simpler, safer maintenance</strong></p>
</li>
</ul>
<p>There’s no magic line count, but the principle of <strong>single responsibility</strong> should be your guide. Functions that do just one thing, and do it well, are the foundation of robust, sustainable software.</p>
<p><strong>Key Takeaways:</strong></p>
<ol>
<li><p><strong>Aim for Single Responsibility:</strong> Does your function do just one thing?</p>
</li>
<li><p><strong>Prioritize Clarity Over Line Count:</strong> The goal is understanding, not hitting a number.</p>
</li>
<li><p><strong>Small Functions Are Easier to Test:</strong> If it’s hard to test, it might be too big.</p>
</li>
<li><p><strong>Meaningful Names Matter (Even More Here):</strong> Clear names are crucial for small functions.</p>
</li>
<li><p><strong>Find the Balance:</strong> Avoid excessive fragmentation that hurts overall clarity.</p>
</li>
</ol>
<p>Start small. Next time you write or refactor a function, ask yourself: “Can I break this down into smaller, more focused parts?” Most of the time, the answer will be a resounding “yes”-and your future self (and your team) will thank you.</p>
<hr />
<p><strong>📣 Keep the Conversation Going!</strong></p>
<p>What’s your experience refactoring large functions? Share your thoughts in the comments!</p>
<p>👉 Follow me on Hashnode and connect on <a target="_blank" href="https://www.linkedin.com/in/phelipe-rodovalho">LinkedIn</a> for more content on Go, AWS, Clean Code, and Cloud.</p>
<p>🔖 If you found this article useful, share it with your team or other developers who value clean, effective code!</p>
<p>🔗 Previous article: <a target="_blank" href="https://byrodovalho.com/clean-code-series-the-power-of-meaningful-names">Clean Code: The Power of Meaningful Names</a></p>
]]></content:encoded></item><item><title><![CDATA[Clean Code Series: The Power of Meaningful Names]]></title><description><![CDATA[Is Your Code a Clear Map or an Unreadable Maze?
Most software systems don’t collapse because of missing features. They collapse under their own weight—when the code becomes so tangled it’s nearly impossible to understand, modify, or evolve without br...]]></description><link>https://byrodovalho.com/clean-code-series-the-power-of-meaningful-names</link><guid isPermaLink="true">https://byrodovalho.com/clean-code-series-the-power-of-meaningful-names</guid><category><![CDATA[AWS]]></category><category><![CDATA[Go Language]]></category><category><![CDATA[golang]]></category><category><![CDATA[software development]]></category><category><![CDATA[backend]]></category><category><![CDATA[architecture]]></category><category><![CDATA[clean code]]></category><category><![CDATA[Clean Architecture]]></category><dc:creator><![CDATA[Phelipe Rodovalho]]></dc:creator><pubDate>Thu, 01 May 2025 15:51:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746046440797/be92afcb-80c0-4ae2-a7b1-b6fd0b69cda5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-is-your-code-a-clear-map-or-an-unreadable-maze">Is Your Code a Clear Map or an Unreadable Maze?</h1>
<p>Most software systems don’t collapse because of missing features. They collapse under their own weight—when the code becomes so tangled it’s nearly impossible to understand, modify, or evolve without breaking something.</p>
<p>How many times, in the middle of a project, have you or a teammate said something like:</p>
<blockquote>
<p>“It’s better to rewrite everything than try to fix this.”</p>
</blockquote>
<p>Or</p>
<blockquote>
<p>“Whoever wrote this is probably not even at the company anymore…”</p>
</blockquote>
<p>These comments don’t come out of nowhere.</p>
<p>They’re symptoms of what Robert C. Martin calls the “progressive decay of code”: the natural tendency of every system to move toward chaos unless there’s a continuous effort to keep it clean.</p>
<p>The cost of ignoring code quality is invisible at first—but fatal in the long run.</p>
<p>The truth is: messy code increases maintenance costs, slows teams down, and creates bugs that are hard to fix. But let’s cut to the chase: the solution starts with something fundamental (and often overlooked): choosing meaningful names. It’s the first pillar of code that actually works in the long term.</p>
<h2 id="heading-clean-code-more-than-a-technique-a-professional-act-of-respect">Clean Code: More Than a Technique — A Professional Act of Respect</h2>
<p>Robert C. Martin, in his seminal book <em>Clean Code</em>, defines writing clean code not just as a technical skill, but as an art — a mix of discipline, technique, and, above all, care for those who will interact with that code in the future.</p>
<blockquote>
<p>“Clean code is code that has been written by someone who cares.” — Robert C. Martin</p>
</blockquote>
<p>When you think about it, the pursuit of clean code is, at its core, an act of respect:</p>
<ul>
<li><strong>Respect for your future self:</strong> Who will inevitably have to revisit and understand that code months or years later.</li>
<li><strong>Respect for your teammates:</strong> Who rely on code clarity to collaborate, maintain, and add new features.</li>
<li><strong>Respect for clients and end users:</strong> Who count on the system’s stability and reliability.</li>
</ul>
<p>Adopting clean code practices is an investment in the sustainability of the project and the sanity of the team.</p>
<h2 id="heading-what-defines-truly-clean-code">What Defines Truly Clean Code?</h2>
<p>Clean code goes far beyond formatting rules or naming conventions. It’s about creating code that is inherently:</p>
<ul>
<li><strong>Easy to read:</strong> The logic flows naturally, with no hidden intentions to decipher.</li>
<li><strong>Easy to understand:</strong> Another developer (or future you) can quickly grasp its purpose and function.</li>
<li><strong>Easy to change:</strong> Updates and new features can be added confidently, without the fear of breaking something.</li>
</ul>
<p>Truly clean code:</p>
<ul>
<li>Feels like it was written for humans first, and machines second.</li>
<li>Tells a clear and cohesive story about what the system does and how it does it.</li>
<li>Avoids surprises, unexpected behaviors, and hard-to-trace “magic.”</li>
<li>Dramatically reduces the learning curve for new team members or maintainers.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746026205903/022ce885-b7b3-4e98-8762-9efb0d8bc480.png" alt="Split-screen illustration of spaghetti code vs clean code for software development." /></p>
<h2 id="heading-meaningful-names-the-fundamental-pillar-of-clean-code">Meaningful Names: The Fundamental Pillar of Clean Code</h2>
<p>One of the most basic—and surprisingly neglected—foundations of clean code is the <strong>use of meaningful names</strong> for everything: variables, functions, classes, packages, modules, etc.</p>
<p>Every name in your code should clearly communicate its <strong>intention</strong>, <strong>purpose</strong>, and <strong>use</strong>.</p>
<p>If a well-chosen name makes a comment unnecessary, you’re on the right track.</p>
<p><strong>Avoid vague, generic, ambiguous names at all costs.</strong> Names like <code>data</code>, <code>info</code>, <code>process</code>, <code>temp</code>, <code>a</code>, <code>b</code>, <code>x</code> are enemies of clarity.</p>
<h3 id="heading-the-contrast-messy-code-vs-clean-code-practical-example">The Contrast: Messy Code vs. Clean Code (Practical Example)</h3>
<p>Here’s a simple but illustrative example (using Go syntax, but the principle is universal):</p>
<p>❌ <strong>Messy code:</strong></p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">doStuff</span><span class="hljs-params">(a <span class="hljs-keyword">int</span>, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-comment">// Adds two numbers</span>
    <span class="hljs-keyword">return</span> a + b
}
</code></pre>
<p>Looking at this code, you have to guess (or rely on a comment, which could be outdated):</p>
<ul>
<li>What does <code>doStuff</code> mean? What “stuff” is it doing?</li>
<li>What do <code>a</code> and <code>b</code> represent?</li>
</ul>
<p>Now see the same logic with meaningful names:</p>
<p>✅ <strong>Clean code:</strong></p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">calculateTotalOrderPrice</span><span class="hljs-params">(orderPrice <span class="hljs-keyword">int</span>, shippingPrice <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> orderPrice + shippingPrice
}
</code></pre>
<p>Just by reading the function signature, the purpose is crystal clear:</p>
<ul>
<li>It calculates the total price of an order.</li>
<li>It adds the order price (<code>orderPrice</code>) and shipping (<code>shippingPrice</code>).</li>
<li>No ambiguity, and the comment is no longer necessary.</li>
</ul>
<h3 id="heading-universal-best-practices-for-naming">💡 Universal Best Practices for Naming</h3>
<ul>
<li><strong>Favor clarity over brevity.</strong> Explicit, descriptive names &gt; short and cryptic ones.</li>
<li><strong>Reveal intent.</strong> Why does this exist? What does it do?</li>
<li><strong>Avoid misinformation.</strong> Don’t use names that can be misleading.</li>
<li><strong>Make meaningful distinctions.</strong> Different names should mean different concepts.</li>
<li><strong>Use pronounceable names.</strong> Helps in discussion and team communication.</li>
<li><strong>Use searchable names.</strong> Avoid <code>e</code>, <code>list</code>, etc., which are hard to find in large codebases.</li>
<li><strong>Avoid unnecessary encodings</strong> (like Hungarian or type prefixes, unless enforced by the language/framework).</li>
<li><strong>Name functions/methods as actions (verbs):</strong> <code>calculateTotal()</code>, <code>saveUser()</code>, <code>sendInvoice()</code>.</li>
<li><strong>Name variables/constants/classes as nouns:</strong> <code>userBirthdate</code>, <code>productQuantity</code>, <code>OrderProcessor</code>.</li>
</ul>
<p>Always ask yourself when naming something:</p>
<blockquote>
<p>“If someone else (or me in six months) sees this name, will they instantly know what it represents without guessing or digging through code?”</p>
</blockquote>
<h2 id="heading-conclusion-the-first-step-to-readable-and-sustainable-code">Conclusion: The First Step to Readable and Sustainable Code</h2>
<p>Writing clean code is a lifelong journey, but it starts with the basics. Choosing meaningful names is undoubtedly one of the most powerful and impactful habits in that journey.</p>
<p>Clear names aren’t just about aesthetics; they reduce cognitive load, simplify debugging, streamline maintenance, and make team collaboration smoother.</p>
<p>Next time you create a function, variable, or class, pause for a moment and think about the name. Remember:</p>
<p><strong>The first and most frequent reader of your code will probably be you. Make your future self’s life easier—and your teammates’, too—by investing time in names that tell the right story.</strong></p>
<hr />
<h2 id="heading-next-article-the-principle-of-small-functions">🚀 Next Article: The Principle of Small Functions</h2>
<p>In the next post in this <em>Clean Code in Practice</em> series, we’ll dive into another core principle: <strong>small functions that do one thing only</strong>. We’ll explore why this is critical to building robust, testable, and maintainable systems.</p>
<hr />
<h2 id="heading-lets-keep-the-conversation-going">📣 Let’s Keep the Conversation Going</h2>
<p>Have you ever had to clean up a codebase that looked like an unsolvable puzzle?<br />Drop your experience in the comments or share this article with your team!</p>
<p>👉 Follow me here on Medium and let’s connect on <a target="_blank" href="https://www.linkedin.com/in/phelipe-rodovalho">LinkedIn</a><br />🔖 Don’t forget to tag your team or share this with other developers who value clean code!</p>
]]></content:encoded></item></channel></rss>