<?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" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Code&amp;Beyond - Medium]]></title>
        <description><![CDATA[Collaborative platform where experienced developers come together to share insights, best practices, and advanced techniques in backend development and software architecture - Medium]]></description>
        <link>https://medium.com/code-beyond?source=rss----418ae2212e7a---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Code&amp;amp;Beyond - Medium</title>
            <link>https://medium.com/code-beyond?source=rss----418ae2212e7a---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 13 Nov 2026 21:55:20 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/code-beyond" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[API Gateway vs BFF: Two Patterns Everyone Confuses]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/code-beyond/api-gateway-vs-bff-two-patterns-everyone-confuses-7044f3f32c37?source=rss----418ae2212e7a---4"><img src="https://cdn-images-1.medium.com/max/2600/1*Bt7Yt9IiR7SNJdceLCl82A.png" width="3880"></a></p><p class="medium-feed-snippet">It starts small. You add an API gateway in front of your services, and it does exactly what you hoped: one place for authentication, one&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/code-beyond/api-gateway-vs-bff-two-patterns-everyone-confuses-7044f3f32c37?source=rss----418ae2212e7a---4">Continue reading on Code&amp;Beyond »</a></p></div>]]></description>
            <link>https://medium.com/code-beyond/api-gateway-vs-bff-two-patterns-everyone-confuses-7044f3f32c37?source=rss----418ae2212e7a---4</link>
            <guid isPermaLink="false">https://medium.com/p/7044f3f32c37</guid>
            <category><![CDATA[api-gateway]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[backend]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[microservices]]></category>
            <dc:creator><![CDATA[Zahid Çakıcı]]></dc:creator>
            <pubDate>Tue, 21 Jul 2026 19:18:25 GMT</pubDate>
            <atom:updated>2026-07-21T19:18:25.284Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Local AI Coding with Opencode]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/code-beyond/local-ai-coding-with-opencode-ae781018a3ab?source=rss----418ae2212e7a---4"><img src="https://cdn-images-1.medium.com/max/2600/1*TQKJfWfvXhxTn9uUyv8-AQ.png" width="3248"></a></p><p class="medium-feed-snippet">Recently, local AI models have improved significantly. Running a capable coding assistant directly on your own machine is now possible&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/code-beyond/local-ai-coding-with-opencode-ae781018a3ab?source=rss----418ae2212e7a---4">Continue reading on Code&amp;Beyond »</a></p></div>]]></description>
            <link>https://medium.com/code-beyond/local-ai-coding-with-opencode-ae781018a3ab?source=rss----418ae2212e7a---4</link>
            <guid isPermaLink="false">https://medium.com/p/ae781018a3ab</guid>
            <category><![CDATA[coding]]></category>
            <category><![CDATA[vibe-coding]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[ai-coding-agent]]></category>
            <dc:creator><![CDATA[Zahid Çakıcı]]></dc:creator>
            <pubDate>Thu, 07 May 2026 07:06:34 GMT</pubDate>
            <atom:updated>2026-05-07T07:06:36.135Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[A Deep Dive into MongoDB Transactions]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/code-beyond/a-deep-dive-into-mongodb-transactions-e680b106188c?source=rss----418ae2212e7a---4"><img src="https://cdn-images-1.medium.com/max/696/0*AM8y8fWh5i7EGtPG" width="696"></a></p><p class="medium-feed-snippet">If you&#x2019;ve been building backends long enough, you know the cold sweat of a partial write.</p><p class="medium-feed-link"><a href="https://medium.com/code-beyond/a-deep-dive-into-mongodb-transactions-e680b106188c?source=rss----418ae2212e7a---4">Continue reading on Code&amp;Beyond »</a></p></div>]]></description>
            <link>https://medium.com/code-beyond/a-deep-dive-into-mongodb-transactions-e680b106188c?source=rss----418ae2212e7a---4</link>
            <guid isPermaLink="false">https://medium.com/p/e680b106188c</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[database]]></category>
            <category><![CDATA[distributed-systems]]></category>
            <category><![CDATA[mongodb]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Zahid Çakıcı]]></dc:creator>
            <pubDate>Wed, 06 May 2026 07:13:39 GMT</pubDate>
            <atom:updated>2026-05-06T07:13:40.313Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[API Gateway Performance Benchmark]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/code-beyond/api-gateway-performance-benchmark-407500194c76?source=rss----418ae2212e7a---4"><img src="https://cdn-images-1.medium.com/max/1920/1*py3z0k6dU76tMiyn0dvqAA.png" width="1920"></a></p><p class="medium-feed-snippet">Performance comparison of most popular API Gateway tools: Kong, NGINX, APISIX, Tyk, KrakenD, Express Gateway, Ocelot, YARP</p><p class="medium-feed-link"><a href="https://medium.com/code-beyond/api-gateway-performance-benchmark-407500194c76?source=rss----418ae2212e7a---4">Continue reading on Code&amp;Beyond »</a></p></div>]]></description>
            <link>https://medium.com/code-beyond/api-gateway-performance-benchmark-407500194c76?source=rss----418ae2212e7a---4</link>
            <guid isPermaLink="false">https://medium.com/p/407500194c76</guid>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[api-gateway]]></category>
            <category><![CDATA[microservices]]></category>
            <dc:creator><![CDATA[Zahid Çakıcı]]></dc:creator>
            <pubDate>Wed, 11 Jun 2025 10:16:27 GMT</pubDate>
            <atom:updated>2025-06-11T10:16:32.654Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Mastering Event-Driven Microservices with Outbox Pattern (Part2): Implementation]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/code-beyond/mastering-event-driven-microservices-with-outbox-pattern-part2-implementation-64b69670651f?source=rss----418ae2212e7a---4"><img src="https://cdn-images-1.medium.com/max/1918/1*QnupmeHzWTSTmR98bXXelw.png" width="1918"></a></p><p class="medium-feed-snippet">In Part 1 of this series, we examined the dual write problem, where coordinating writes between a database and a message broker can lead&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/code-beyond/mastering-event-driven-microservices-with-outbox-pattern-part2-implementation-64b69670651f?source=rss----418ae2212e7a---4">Continue reading on Code&amp;Beyond »</a></p></div>]]></description>
            <link>https://medium.com/code-beyond/mastering-event-driven-microservices-with-outbox-pattern-part2-implementation-64b69670651f?source=rss----418ae2212e7a---4</link>
            <guid isPermaLink="false">https://medium.com/p/64b69670651f</guid>
            <category><![CDATA[outbox-pattern]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[microservices]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[kafka]]></category>
            <dc:creator><![CDATA[Zahid Çakıcı]]></dc:creator>
            <pubDate>Sat, 12 Apr 2025 06:28:47 GMT</pubDate>
            <atom:updated>2025-04-12T06:28:47.765Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Mastering Event-Driven Microservices with Outbox Pattern]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/code-beyond/mastering-event-driven-microservices-with-outbox-pattern-f84bc8ad24bc?source=rss----418ae2212e7a---4"><img src="https://cdn-images-1.medium.com/max/1600/1*uqfdVeomaCNKk_83Z91jOA.png" width="1600"></a></p><p class="medium-feed-snippet">In microservices architectures, communication between services is essential for efficient operation and sharing data across services&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/code-beyond/mastering-event-driven-microservices-with-outbox-pattern-f84bc8ad24bc?source=rss----418ae2212e7a---4">Continue reading on Code&amp;Beyond »</a></p></div>]]></description>
            <link>https://medium.com/code-beyond/mastering-event-driven-microservices-with-outbox-pattern-f84bc8ad24bc?source=rss----418ae2212e7a---4</link>
            <guid isPermaLink="false">https://medium.com/p/f84bc8ad24bc</guid>
            <category><![CDATA[kafka]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[microservices]]></category>
            <category><![CDATA[outbox-pattern]]></category>
            <dc:creator><![CDATA[Zahid Çakıcı]]></dc:creator>
            <pubDate>Sun, 12 Jan 2025 09:03:20 GMT</pubDate>
            <atom:updated>2025-04-12T07:00:03.791Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[What is Middleware?]]></title>
            <link>https://medium.com/code-beyond/what-is-middleware-87a5bb68e581?source=rss----418ae2212e7a---4</link>
            <guid isPermaLink="false">https://medium.com/p/87a5bb68e581</guid>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[middleware]]></category>
            <category><![CDATA[dotnet]]></category>
            <category><![CDATA[go]]></category>
            <dc:creator><![CDATA[Zahid Çakıcı]]></dc:creator>
            <pubDate>Sat, 04 Jan 2025 18:05:37 GMT</pubDate>
            <atom:updated>2025-01-04T18:05:37.081Z</atom:updated>
            <content:encoded><![CDATA[<p>In the world of web development, the term “middleware” might seem like background noise, but it’s actually the backbone of a well-structured application. Middleware functions are the essential building blocks that keep complex applications running smoothly by organizing and managing tasks in the request-response cycle.</p><p>Middleware exists at different levels — from low-level, system middleware (like network drivers and OS services) to the application-level middleware we’ll focus on here. <em>Application-level middleware</em> operates within a web framework to handle specific tasks as each HTTP request flows through the app.</p><p>You may wonder, <em>What makes middleware so important?</em> Middleware streamlines handling cross-cutting concerns, such as authentication, logging, and error management, without cluttering route logic. In this article, we’ll dive into how application-level middleware works across frameworks like Node.js Express, .NET, and Go Fiber. We’ll see how each framework handles middleware, the role of the next function in controlling request flow, and how middleware shapes the user experience in your web app.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*H1m6z7IjL7gYYBWP49pROQ.png" /></figure><h3>What is Middleware, Anyway?</h3><p>At its core, <strong>middleware</strong> is a function or series of functions that handle requests and responses in a web application. But middleware isn’t just about dealing with requests — it’s about shaping how those requests are processed. It can <strong>modify the request</strong> before it reaches your main handler or <strong>alter the response</strong> before it’s sent back to the client.</p><p>In simpler terms, middleware acts as the <em>in-between</em> layer that lets you process, inspect, or reject HTTP requests at various stages. It allows you to:</p><ul><li><strong>Authentication/Authorization</strong>: Verifies that the user is allowed to access the resource.</li><li><strong>Logging</strong>: Tracks request details like IP, request time, and user actions for monitoring and debugging.</li><li><strong>Error Handling</strong>: Catches errors at a central point, ensuring consistent responses across routes.</li><li><strong>Compression</strong>: Compresses responses to reduce the amount of data transferred, improving load times.</li><li><strong>Rate Limiting</strong>: Limits the number of requests from a single user to prevent overuse or abuse.</li><li><strong>Caching</strong>: Stores the responses temporarily, so frequently requested data can be served faster.</li><li><strong>Request Validation</strong>: Checks that incoming requests meet specific requirements, like valid data formats or required fields.</li></ul><p>Think of middleware like the security checkpoint at an airport. Each request (or passenger) goes through multiple layers of checks — authentication, validation, logging — before reaching the final gate (the route handler).</p><h4>Order Matters: The Importance of Middleware Sequence</h4><p>One critical aspect of middleware is that <strong>order matters</strong>. The sequence in which middleware is defined affects how requests are processed. Middleware functions are executed in the order they are registered, meaning that a misordered middleware can disrupt the application’s flow or even break functionality.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*S0-6kFnlFr1ucHZT8Z4s-g.png" /></figure><p>For example, imagine you have a user based rate limiting:</p><pre>app.use(logRequest);       // Logs the request details<br>app.use(authenticateUser);  // Authenticates the user<br>app.use(rateLimit);         // Limits request rate per user</pre><p>In this order, each request is logged first, then checked for authentication, and finally rate-limited before reaching the handler. However, if we mistakenly swap authentication and rate limiting:</p><pre>app.use(logRequest);       // Logs the request details<br>app.use(rateLimit);         // Limits request rate per user<br>app.use(authenticateUser);  // Authenticates the user</pre><p>If we use a credential like userId it won’t work as expected since the user information that we need is not registered yet. If we use the input, the middleware will act on all requests, even unauthenticated ones, potentially blocking access before a user’s credentials are checked. This could lead to user frustration and wasted resources if unauthenticated requests are rate-limited.</p><p>Ensuring the correct sequence for each middleware layer is key to building a smooth, secure, and reliable pipeline.</p><h3>The “Next” Function</h3><p>One of the most critical parts of understanding middleware is the concept of the `next` function. If you’ve worked with middleware before, you’ve probably seen this:</p><pre>app.use((req, res, next) =&gt; {<br>    console.log(&quot;Middleware 1&quot;);<br>    next(); // Pass control to the next middleware<br>});</pre><h3>But what does `next()` do?</h3><p>When a request enters a web server, it doesn’t just go straight to the route handler. Instead, it moves through a series of middleware functions. When you call next(), you’re saying, “Okay, this middleware has done its job – move the request to the next function in line.”</p><p>In short, middleware <em>intervenes</em> in the request-response pipeline, and the next() function ensures the flow continues.</p><h3>Middleware in Action</h3><p>Now, let’s explore how middleware works in three popular web frameworks: <strong>Node.js Express</strong>, <strong>ASP.NET</strong>, and <strong>Go Fiber</strong>. Each of these frameworks uses middleware, but their implementation looks a little different.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MtwescISltXcI9QfhHPIYQ.png" /></figure><h4>Middleware in Express</h4><p>In <strong>express</strong>, middleware is everywhere. Here’s a basic example:</p><pre>const express = require(&#39;express&#39;);<br>const compression = require(&#39;compression&#39;)<br>const app = express();<br><br>// Standart Middleware that compress every response <br>app.use(compression())<br><br>// Custom Middleware that logs every request<br>app.use((req, res, next) =&gt; {<br>    const start = Date.now();<br>    next(); // Call the next middleware or route handler<br>    const duration = Date.now() - start;<br>    console.log(`Request to ${req.path} took ${duration}ms`);<br>});<br><br>// Route handler<br>app.get(&#39;/home&#39;, (req, res) =&gt; {<br>    res.send(&#39;Welcome to Home Page&#39;);<br>});<br><br>app.listen(3000);</pre><h4><strong>Middleware in .NET</strong></h4><p>In <strong>.NET</strong>, middleware is used in a similar fashion, although the syntax and setup differ slightly. Here’s an example of how it works in an ASP.NET Core application:</p><pre>using Microsoft.AspNetCore.Builder;<br><br>var builder = WebApplication.CreateBuilder(args);<br><br>// Enable response compression with default settings<br>builder.Services.AddResponseCompression();<br><br>var app = builder.Build();<br><br>// Standard Middleware that compresses every response<br>app.UseResponseCompression();<br><br>// Custom Middleware that logs every request<br>app.Use(async (context, next) =&gt;<br>{<br>    var start = DateTime.Now;<br>    await next(); // Pass control to the next middleware or route handler<br>    var duration = DateTime.Now - start;<br>    Console.WriteLine($&quot;Request to {context.Request.Path} took {duration.TotalMilliseconds}ms&quot;);<br>});<br><br>// Route handler<br>app.MapGet(&quot;/home&quot;, () =&gt; &quot;Welcome to Home Page&quot;);<br><br>// Run the application<br>app.Run();</pre><h4><strong>Middleware in Go Fiber</strong></h4><p>Now let’s move to <strong>Go Fiber</strong>, a web framework built on top of Fasthttp, offering performance similar to Express with a Go flavor.</p><p>Here’s an example:</p><pre>package main<br><br>import (<br>    &quot;github.com/gofiber/fiber/v2&quot;<br>    &quot;github.com/gofiber/fiber/v2/middleware/compress&quot;<br>    &quot;log&quot;<br>    &quot;time&quot;<br>)<br><br>func main() {<br>    app := fiber.New()<br><br>    // Standard Middleware that compresses every response<br>    app.Use(compress.New())<br><br>    // Custom Middleware that logs every request<br>    app.Use(func(c *fiber.Ctx) error {<br>        start := time.Now()<br>        err := c.Next() // Pass control to the next middleware or route handler<br>        duration := time.Since(start)<br>        log.Printf(&quot;Request to %s took %v&quot;, c.Path(), duration)<br>        return err<br>    })<br><br>    // Route handler<br>    app.Get(&quot;/home&quot;, func(c *fiber.Ctx) error {<br>        return c.SendString(&quot;Welcome to Home Page&quot;)<br>    })<br><br>    // Start server on port 3000<br>    log.Fatal(app.Listen(&quot;:3000&quot;))<br>}</pre><h3>Conclusion</h3><p>In summary, middleware is a practical way to handle cross-cutting concerns like logging, compression, and authentication in web applications. Whether you’re using Node.js, .NET, or Go, middleware helps you organize these tasks efficiently. By understanding how to work with the next function and middleware order, you can keep your code clean and adaptable, making it easier to build scalable applications that respond well to changing needs.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=87a5bb68e581" width="1" height="1" alt=""><hr><p><a href="https://medium.com/code-beyond/what-is-middleware-87a5bb68e581">What is Middleware?</a> was originally published in <a href="https://medium.com/code-beyond">Code&amp;Beyond</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deployment Archetypes for High Availability and Low Latency]]></title>
            <link>https://medium.com/code-beyond/deployment-archetypes-for-high-availability-and-low-latency-4e7b45770db2?source=rss----418ae2212e7a---4</link>
            <guid isPermaLink="false">https://medium.com/p/4e7b45770db2</guid>
            <category><![CDATA[software]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[cloud]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[software-architecture]]></category>
            <dc:creator><![CDATA[Onurkan Bakırcı]]></dc:creator>
            <pubDate>Wed, 30 Oct 2024 13:07:30 GMT</pubDate>
            <atom:updated>2024-10-30T13:07:30.112Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ydJd2iM5WDn4xtMS9yiEtg.png" /><figcaption><a href="https://cloud.google.com/architecture/deployment-archetypes">https://cloud.google.com/architecture/deployment-archetypes</a></figcaption></figure><p>In the past, as application developers, we were not only responsible for developing our applications but also had to manage the entire deployment process. When we wanted to deploy our application, we had to rent servers, virtual machines, or other resources from hosting providers, and all installations and configurations had to be handled by the developers themselves.</p><p>For example, the installation of the database for the application to be deployed on the server had to be carried out by the developer. Tasks such as regularly taking database backups and, in more advanced applications, setting up synchronization and data replication between databases in a cluster had to be manually configured by the developers.</p><blockquote><strong>Hosting</strong> and <strong>cloud</strong> refer to different concepts!</blockquote><p>However, over time, the requirement for increasingly complex applications to be available 24/7 <strong>(highly available)</strong> and to respond within milliseconds <strong>(low-latency)</strong> made the deployment processes more challenging and time-consuming for developers.</p><p>At this point, cloud and cloud providers came to the rescue of developers. They enable developers to deploy the desired database, server, or application in any region with high availability, all with a single click, without the hassle of complex setups. This allows developers to focus solely on coding and problem-solving, without having to deal with any infrastructure-related tasks.</p><p>So, how do cloud providers manage to offer such highly available and low-latency systems worldwide?</p><p>In this article, we will explore the deployment strategies used by cloud providers such as <strong>AWS</strong>, <strong>Azure</strong>, and <strong>GCP</strong> to deliver cloud services with high availability and low latency to their customers.</p><p>First, we need to examine the concepts of <strong>zone</strong> and <strong>region</strong>.</p><p>A <strong>zone</strong> is a group of servers where applications are deployed in small clusters. This structure can be thought of as different shelves in a library, separate rooms within the same building, or distinct buildings within the same campus. The key aspect of zones is their proximity to each other, allowing them to communicate with very low latency (within milliseconds).</p><p>A <strong>region</strong>, on the other hand, is a structure formed by the combination of at least three zones. Regions are designed to ensure service continuity even in the event of natural disasters such as earthquakes or tsunamis. If one region becomes unavailable, other regions can continue to provide service without interruption.</p><p>The naming convention for zones and regions generally follows the format below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-iNdvTpg8zQcZOhTZOgU5A.png" /></figure><p>For example, AWS has a region called <strong>us-east-1</strong> located in Northern Virginia. This region includes several availability zones: us-east-1a ,us-east-1b ,us-east-1c ,us-east-1d and us-east-1e. Additionally, there is another region, <strong>us-east-2</strong>, located in Ohio, which contains the availability zones us-east-2a ,us-east-2b and us-east-2c</p><p>After discussing the concepts of zones and regions, we will now examine the types of deployments used for deploying cloud applications.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aDy04Kma3zMQRlj75ArfYw.png" /><figcaption><em>Figure 1: Classification of deployment archetypes (Berenberg &amp; Calder, 2023)</em></figcaption></figure><h4>1. Zonal Deployment</h4><p>This type of deployment involves deploying all components of an application within a single zone. It can be further divided into two categories:</p><ul><li><strong>Single Zone:</strong> In this deployment type, all components of the application (such as storage, network, etc.) are hosted in a single zone. This setup is generally not suitable for production environments. It is more appropriate for test applications where data loss is not critical and outages do not pose significant problems.</li><li><strong>Primary Zone with Failover Zone:</strong> This deployment strategy involves deploying the application in two different zones simultaneously. In the event of a failure in one zone due to power outages, fires, or other reasons, traffic is automatically or manually redirected to the same application running in the second zone. This principle helps ensure higher availability and resilience for the application.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lTrreZhCLjWgMzHzpLWsyQ.png" /><figcaption><a href="https://cloud.google.com/architecture/deployment-archetypes/zonal">https://cloud.google.com/architecture/deployment-archetypes/zonal</a></figcaption></figure><h4><strong>2. Regional Deployment</strong></h4><p>This deployment type involves serving the application through multiple zones within a single region. It can be divided into two categories:</p><ul><li><strong>Single Region:</strong> In this deployment type, the application is deployed across multiple zones within a single region. Incoming traffic is directed to the active zone, typically attempting to funnel requests through the same zone to achieve better performance.</li><li><strong>Primary Region with Failover Region:</strong> In this strategy, the application is deployed across multiple zones in several regions. This setup ensures that if one region goes down, the zones in another region can continue to provide service. For example, if a region in Europe becomes unavailable, the application can still be served by zones in the Middle East region.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_gK86FJsJUvyUoBWM1k5IA.png" /><figcaption><a href="https://cloud.google.com/architecture/deployment-archetypes/regional">https://cloud.google.com/architecture/deployment-archetypes/regional</a></figcaption></figure><h4>3. Multi-regional</h4><p>This deployment scenario involves deploying the application across multiple regions. In cases where users from different continents access the application, users are directed to the instance in the region closest to them to achieve low latency.</p><p>Additionally, if the application’s requirements necessitate storing confidential information about users and regulations dictate that the laws in a particular region do not allow this data to be stored in another region <strong>(data sovereignty)</strong>, this deployment type can also be utilized.</p><p>In this setup, users’ data is stored in a single region, ensuring compliance with regional data protection laws.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZKph259PA5-BHBQMLFK6mQ.png" /></figure><h4>4. Global</h4><p>In scenarios where the application is used globally, deployment is desired across multiple regions. Users retrieve data from the region closest to their location. Instead of storing a user’s data in a single region, the data is distributed across all regions. Consequently, the application deployed in all regions operates as if it were a single, unified application, providing a seamless experience to users regardless of their geographic location.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rCvHzYZl7rEJdR9zcdR9kg.png" /></figure><h4>5. Hybrid</h4><p>This scenario involves the application running both on-premises and in the cloud, working together in an integrated manner. By implementing failover mechanisms between the on-premises and cloud environments, the system’s availability is enhanced. This hybrid deployment allows for improved resilience, ensuring that if one environment experiences issues, the other can take over, thereby maintaining service continuity.</p><h4>6. Multi-cloud</h4><p>This scenario involves deploying the application across multiple public cloud providers to enhance its availability. By doing so, if an issue arises with one cloud provider, the application instances running on the other cloud provider can continue to provide service. This multi-cloud deployment strategy ensures greater resilience and minimizes the risk of downtime, allowing for seamless service delivery even in the event of provider-specific failures.</p><p>The image below will make all of this information more memorable for you.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ydJd2iM5WDn4xtMS9yiEtg.png" /><figcaption><a href="https://cloud.google.com/architecture/deployment-archetypes">https://cloud.google.com/architecture/deployment-archetypes</a></figcaption></figure><p>References:</p><ul><li>Berenberg, A., &amp; Calder, B. (2023). <em>Deployment archetypes for cloud applications.</em> Google Inc.</li><li><a href="https://cloud.google.com/architecture/deployment-archetypes">https://cloud.google.com/architecture/deployment-archetypes</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4e7b45770db2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/code-beyond/deployment-archetypes-for-high-availability-and-low-latency-4e7b45770db2">Deployment Archetypes for High Availability and Low Latency</a> was originally published in <a href="https://medium.com/code-beyond">Code&amp;Beyond</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Dockerizing Golang Apps: A Step-by-Step Guide to Reducing Docker Image Size]]></title>
            <link>https://medium.com/code-beyond/dockerizing-golang-apps-a-step-by-step-guide-to-reducing-docker-image-size-306898e7359e?source=rss----418ae2212e7a---4</link>
            <guid isPermaLink="false">https://medium.com/p/306898e7359e</guid>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[dockerizing]]></category>
            <dc:creator><![CDATA[Zahid Çakıcı]]></dc:creator>
            <pubDate>Sat, 05 Oct 2024 09:55:04 GMT</pubDate>
            <atom:updated>2024-10-05T16:58:01.153Z</atom:updated>
            <content:encoded><![CDATA[<p>When building and deploying Go applications, it’s essential to manage the size of the Docker images for faster builds, better performance, and less storage usage. In this article, we will walk through various ways to reduce the Docker image size of a simple Go application, moving from a large image to a small one. We will dive into how much we can reduce the size of an image.</p><p>We are going to use the repo from <a href="https://medium.com/code-beyond/go-fiber-jwt-auth-eab51a7e2129">my previos JWT example story</a>. Since the app uses a db connection and I want to keep everything simple, we will use docker-compose to build image for each step.</p><pre>services:<br>  app:<br>    build: .<br>    container_name: fiber-jwt-app<br>    image: fiber-jwt-app-1  # 1 is the step number<br>    depends_on:<br>      - db<br>    env_file:<br>      - docker.env<br>    ports:<br>      - &quot;3000:3000&quot;<br>  db:<br>    ...</pre><h3>The Primitive Beginner</h3><p>This is the typical approach many beginners follow. We use the full golang:1.22.5 image, which is based on Debian. This image is large because it includes everything needed for Go development, including build tools and libraries. Additionally, there are other large files inherited from the Debian base, such as package managers and system utilities, which are unnecessary for running the compiled application.</p><pre>FROM golang:1.22.5<br><br>WORKDIR /app<br><br>COPY go.mod go.sum ./<br>RUN go mod download<br>COPY . .<br><br>RUN go build -o main .<br><br>CMD [&quot;/app/main&quot;]</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DTGow7-8IfufFvuu3RKg6g.png" /><figcaption>1.25 GB image size</figcaption></figure><blockquote>We copy <em>go.mod</em> and go.sum first and run go mod download to take advantage of Docker’s caching. If dependencies haven’t changed, this step is cached, so Docker skips re-downloading them in future builds. This speeds up the process, as only the application code is re-copied and rebuilt when modified.</blockquote><h3>Using the Alpine Base Image</h3><p>To reduce the size, we switch to the alpine version of the Go image. Alpine is a minimal Linux distribution that’s much smaller than Debian.</p><pre>FROM golang:1.22.5-alpine<br><br>WORKDIR /app<br><br>COPY go.mod go.sum ./<br>RUN go mod download<br>COPY . .<br><br>RUN go build -o main .<br><br>CMD [&quot;/app/main&quot;]</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DHAvUBv-7KSoglx4Kucb2Q.png" /><figcaption>646 MB image size</figcaption></figure><p>We can see a little bit improvement here in image size but,</p><blockquote><strong>Do we need all of the content from the Alpine image to run our application?</strong></blockquote><p>Not really.</p><h3>Multi-stage Build with Alpine</h3><p>The Golang Alpine image, while lightweight compared to standard base images, still includes tools and libraries that are only necessary during the build process, such as the Go compiler and build dependencies. Once we compile our Go application into a static binary, we no longer need these build tools or libraries in the final runtime environment.</p><p>This is where <strong>multi-stage builds</strong> come in handy. In the first stage, we use the Golang Alpine image to compile the Go application. Then, in the second stage, we copy only the final binary into a minimal base image.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7CZtPTcmB6dFQsqmGQR-lw.png" /></figure><p>In the first stage, we compile the Go binary in a larger image. In the second stage, we copy only the compiled binary into a minimal alpine base, discarding everything else.</p><p>This way, we only include what’s absolutely essential for running the app, significantly reducing the image size and removing unnecessary files that could potentially increase the attack surface or slow down deployment.</p><pre>FROM golang:1.22.5-alpine<br><br>WORKDIR /app<br><br>COPY go.mod go.sum ./<br>RUN go mod download<br>COPY . .<br><br>RUN go build -o main .<br><br>FROM alpine:latest<br><br>WORKDIR /app<br><br>COPY --from=builder /app/main .<br><br>CMD [&quot;./main&quot;]</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sIWdnax7aioHOybmykTy5w.png" /><figcaption>26.2 MB image size</figcaption></figure><p>Look how far we’ve reduced the size with this step! By using a <strong>multi-stage build</strong>, we’ve brought the image size down from hundreds of megabytes to just <strong>26.2 MB</strong>. This is a crucial improvement because we are now separating the <strong>build environment</strong> from the <strong>runtime environment</strong></p><h3>Adding Build Flags for Further Optimization</h3><p>To reduce the size even further, we can strip unnecessary debugging information from the Go binary using -ldflags=&quot;-s -w&quot;. By using the build flags -s and -w, we remove the symbol table and DWARF debugging information, which shrinks the size of the binary.</p><pre>FROM golang:1.22.5-alpine as builder<br><br>WORKDIR /app<br><br>COPY go.mod go.sum ./<br>RUN go mod download<br>COPY . .<br><br>RUN go build -ldflags=&quot;-s -w&quot; -o main .<br><br>FROM alpine:latest<br><br>WORKDIR /app<br><br>COPY --from=builder /app/main .<br><br>CMD [&quot;./main&quot;]</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ic4JpdxtGbvJFVwYHVpW1g.png" /><figcaption>20.1 MB image size</figcaption></figure><h3>The Ultimate Minimal Image with scratch</h3><p>Finally, we can use the scratch base image, which is completely empty and doesn’t even include a Linux distribution. It doesn’t even have basic commands like bash, cat and curl. This is the smallest possible image, and since Go can compile statically linked binaries, it’s perfect for Go applications.</p><pre>FROM golang:1.22.5-alpine as builder<br><br>WORKDIR /app<br><br>COPY go.mod go.sum ./<br>RUN go mod download<br>COPY . .<br><br>ENV CGO_ENABLED=0  #Disables C dependencies for portability.<br><br>RUN go build -ldflags=&quot;-s -w&quot; -o main .<br><br>FROM scratch<br><br>WORKDIR /app<br><br>COPY --from=builder /app/main .<br><br>CMD [&quot;./main&quot;]</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_9witM5zPEQZlqJ2vXF7vg.png" /><figcaption>12 MB image size</figcaption></figure><p>We’ve reached the ultimate level of optimization with this final step! By using the <strong>scratch</strong> base image, we’ve reduced the size of the Docker image to an impressive <strong>12 MB</strong>. This is possible because scratch is an entirely empty image—there&#39;s nothing but the Go binary we compiled. By statically compiling the binary with CGO_ENABLED=0, we ensure it doesn’t need any external libraries to run.</p><p>However, while this gives us the maximum efficiency for production, it does require more careful handling since scratch lacks tools for debugging or runtime adjustments.</p><h4>Caveats for scratch image</h4><p>While using the scratch base image creates the smallest Docker image, there are a few disadvantages to keep in mind:</p><ol><li><strong>No utilities or tools</strong>: scratch is completely empty, meaning it doesn’t include basic tools like bash, curl, or cat. This makes debugging and troubleshooting inside the container difficult.</li><li><strong>No package manager</strong>: You can’t install anything into a scratch image, so you need to ensure your Go binary is fully self-contained and statically compiled.</li><li><strong>Limited debugging</strong>: Since there are no tools, you won’t be able to inspect logs or make quick fixes inside the container.</li><li><strong>External dependencies</strong>: If your app relies on external libraries or dynamically linked dependencies, you’ll need to handle them explicitly, as scratch won’t include them.</li></ol><h3>Conclusion</h3><p>By following these steps, we’ve reduced the size of our Docker image from 1.25 GB to 12 MB — a reduction of more than 99%! Using multi-stage builds, alpine, build flags, and the scratch base image, we’ve created a highly optimized Docker image that’s fast to deploy, small to transfer, and efficient to run.</p><p><strong>Key takeaways:</strong></p><ol><li>Start by using a smaller base image like alpine.</li><li>Use multi-stage builds to keep only the necessary runtime files.</li><li>Use build flags to strip out unnecessary information from the binary.</li><li>For ultimate size reduction, use scratch and a statically compiled Go binary. (But be mindful of the caveats)</li></ol><p>By applying these techniques, you can ensure that your Go applications are packaged efficiently in Docker. Happy coding!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=306898e7359e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/code-beyond/dockerizing-golang-apps-a-step-by-step-guide-to-reducing-docker-image-size-306898e7359e">Dockerizing Golang Apps: A Step-by-Step Guide to Reducing Docker Image Size</a> was originally published in <a href="https://medium.com/code-beyond">Code&amp;Beyond</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Writing an HTTP Server on Top of the TCP Layer from Scratch]]></title>
            <link>https://medium.com/code-beyond/to-write-an-http-server-on-top-of-the-tcp-layer-from-scratch-837b0355ff6a?source=rss----418ae2212e7a---4</link>
            <guid isPermaLink="false">https://medium.com/p/837b0355ff6a</guid>
            <category><![CDATA[sockets]]></category>
            <category><![CDATA[tcp]]></category>
            <category><![CDATA[linux]]></category>
            <category><![CDATA[kernel]]></category>
            <category><![CDATA[https]]></category>
            <dc:creator><![CDATA[Onurkan Bakırcı]]></dc:creator>
            <pubDate>Sat, 28 Sep 2024 11:47:22 GMT</pubDate>
            <atom:updated>2024-10-06T20:00:01.952Z</atom:updated>
            <content:encoded><![CDATA[<p>In this article, we will attempt to write an HTTP server from scratch on top of the TCP layer. However, to understand the logic of the process, we need to first delve into processes.</p><p>Processes running on the operating system need to communicate with each other. There are various methods that processes can use to communicate with each other. Shared Memory, Message Passing, and similar methods are commonly used for inter-process communication.</p><p>However, let’s consider that these two processes are running on different machines. In client-server systems like these, the most common methods for communication between processes are sockets, remote procedure calls (RPCs), and pipes.</p><p>In this article, we will utilize communication using sockets. So, what is a socket? Sockets enable processes to communicate with each other over the network. Operating systems have different implementations for sockets. For detailed information about Linux’s socket implementation, for example, we can refer to the Linux Kernel documentation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KRdDbmkoBrPdqiSa46TXPQ.png" /><figcaption><a href="https://linux.die.net/man/2/socket">https://linux.die.net/man/2/socket</a></figcaption></figure><p>Various types of sockets exist, such as TCP sockets, UDP sockets, and others. The primary purpose of these sockets is to facilitate communication between processes running on the network over the network.</p><p>For instance, when two isolated processes, such as the application where you keep your notes and a grammar check application that verifies your writing rules, need to communicate, they can use TCP sockets to exchange information in a specific format.</p><p>In the same way, our server app process (backend) running on the server and the process (client) attempting to access it via a web browser will communicate fundamentally over TCP.</p><p>The HTTP protocol is a protocol built on top of TCP. So, what does the term ‘protocol’ mean? A protocol is a set of predefined rules that two systems follow when communicating with each other. Messages sent in a specific format according to the rules of HTTP are interpreted and necessary actions are taken by the other system that understands the language/rules.</p><p>Now, we can start writing our HTTP server from scratch. First, we will ask the operating system to set up a socket for us:</p><pre>using System.Net.Sockets;<br><br>using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);</pre><p>When we run the above code on a Linux operating system, our code, through the GNU C Library (libc), makes a system call to the Linux kernel, and the kernel provides us with a TCP socket. (This can vary depending on the programming language used.)</p><p>Let’s continue from where we left off in the code. Since we will be using TCP for byte streaming, we specify that the socket is of type Stream. Additionally, we indicate that the protocol to be used is TCP. Now, we need to specify which host and port numbers to bind to this socket.</p><pre>using System.Net;<br>using System.Net.Sockets;<br><br>using Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);<br><br>var host = IPAddress.Parse(&quot;127.0.0.1&quot;);<br>var port = 7888;<br><br>var endpoint = new IPEndPoint(host, port);<br><br>socket.Bind(endpoint);</pre><p>This endpoint is composed of an IP address and a port number. The reason for using a port number is to determine which process on the machine will receive the transmitted message among many processes. The use of an IP address is to understand which machine on the network will receive the message. In the end, the endpoint used by TCP takes a structure like 127.0.0.1:5300.</p><p>As seen, we have bound the socket to the 7888 port on our local machine, identified by the address 127.0.0.1. Now, we can start listening on this endpoint.</p><pre>using System.Net;<br>using System.Net.Sockets;<br><br>using Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);<br><br>var host = IPAddress.Parse(&quot;127.0.0.1&quot;);<br>var port = 7888;<br><br>var endpoint = new IPEndPoint(host, port);<br><br>socket.Bind(endpoint);<br><br>socket.Listen(10);<br><br>Console.WriteLine($&quot;Listening on port {port}...&quot;);<br><br>while (true)<br>{<br>    // Accept incoming connection<br>    Socket clientSocket = socket.Accept();<br><br>    // Handle the accepted socket<br>    HandleClient(clientSocket);<br>}<br><br>void HandleClient(Socket clientSocket)<br>{<br>    // Handle the connection (you can add your logic here)<br>    // For example, read data from the client<br>    byte[] buffer = new byte[1024];<br>    int bytesRead = clientSocket.Receive(buffer);<br>    string receivedData = Encoding.ASCII.GetString(buffer, 0, bytesRead);<br>    Console.WriteLine($&quot;Received data: {receivedData}&quot;);<br><br>    // Close the client socket<br>    clientSocket.Close();<br>}</pre><p>The number 10 in the code specifies the backlog. So, what is the backlog? Multiple clients may want to connect to our server simultaneously. However, establishing the connection between the server and the client takes time due to the three-way handshake. In this case, incoming connection requests are queued up. The backlog parameter determines how many connections will be queued up in this queue. If more connection requests come in, our server will reject them.</p><p>Now, our socket is in a listening state. However, at the moment, it only prints the message sent by the client to the console. Now, let’s see what we will do with these packets coming over TCP, i.e., how we will write the HTTP server. In a sense, we will teach HTTP rules to TCP. For this, let’s take a closer look at HTTP first.</p><p>HTTP requests consist of two main parts: header and body. Below is a simple GET request sent to the google.com address.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EZJNZKm8VeQmzcenHkxAxQ.png" /></figure><p>As can be understood from the image, the steps involved are:</p><ul><li>Connect to the server via TCP connection.</li><li>Send an HTTP message over the TCP connection.</li><li>Read the response sent by the server.</li><li>Close the connection.</li></ul><p>So far, with the above code block, we have only written the part that establishes the TCP connection. Now, we need to interpret the incoming HTTP message in a specific format.</p><p>Accordingly, let’s take a closer look at the HTTP message sent by the client.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PkJcu2ORIjcM2pBO7JrM0g.png" /></figure><p>In addition to the image above, there is a special character \r\n at the end of each line. This represents a line break. Additionally, each empty line between lines indicates a new segment. For example, the empty line between the HTTP response header and the response body distinguishes these two parts.</p><p>We need to parse this incoming HTTP request on our TCP server. We can do this as follows:</p><pre>void HandleClient(Socket clientSocket)<br>{<br>    // Handle the connection (you can add your logic here)<br>    // For example, read data from the client<br>    byte[] buffer = new byte[1024];<br>    int bytesRead = clientSocket.Receive(buffer);<br>    string receivedData = Encoding.ASCII.GetString(buffer, 0, bytesRead);<br>    Console.WriteLine($&quot;Received data: {receivedData}&quot;);<br><br>    string[] lines = receivedData.Split(&quot;\r\n&quot;); // Split all lines by new line<br><br>    string[] requestLine = lines[0].Split(&#39; &#39;); // Split the first line by space<br><br>    string method = requestLine[0]; // GET, POST, PUT, DELETE, etc.<br>    string path = requestLine[1]; // The path of the request like /index.html<br>    string httpVersion = requestLine[2]; // HTTP version like HTTP/1.1<br><br>    // ...<br>    // handle the request based on the method and path <br>    // by using backend application server logic<br>    // ...<br><br>    // Send a simple HTTP response back to the client<br>    string response = &quot;HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello, World!&quot;;<br>    byte[] responseBytes = Encoding.ASCII.GetBytes(response);<br>    clientSocket.Send(responseBytes);<br><br>    // Close the client socket<br>    clientSocket.Close();<br>}</pre><p>Here, we are parsing the incoming request according to HTTP standards. Later, based on the content of this parsed HTTP request, we enable our backend application to perform its logic. Finally, we create a response and send it back to the client as bytes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EYt44s96dTKK1m1PkEnAGA.png" /><figcaption>Response of our http server</figcaption></figure><p>By following this approach, we have essentially written a basic HTTP server. The server we’ve created can be further developed into a real HTTP server. For this, the implementation of all HTTP standards is required. We can refer to the RFC pages for the HTTP standards. For example, to explore the details of the HTTP 1.0 implementation, you can visit this link.</p><p>Finally, let’s take a look at HTTP versions and the features they bring. HTTP is developed by the HTTP community and is becoming more efficient over time. Briefly looking at its history:</p><ul><li><strong>HTTP 0.9</strong>: The first version of HTTP, performing basic request/response operations at a simple level.</li><li><strong>HTTP 1.0</strong>: Introduced new features such as status codes, request/response headers.</li><li><strong>HTTP 1.1</strong>: This version introduced the pipelining feature, eliminating the need to wait for the response of a previous request before sending a new one. This allowed sending multiple requests in succession, increasing speed. It also introduced the ability to reuse the same connection for other requests.</li><li><strong>HTTP 2.0</strong>: In previous HTTP versions, a new connection was opened for each request. With HTTP 1.1, the ability to send multiple parallel requests over a single connection (multiplexing) was introduced.</li><li><strong>HTTP 3.0</strong>: Started using QUIC instead of TCP in the transport layer.</li></ul><p>You can access the GitHub code from the link below.</p><p><a href="https://github.com/onurkanbakirci/HttpOverTCP?tab=readme-ov-file#how-to-send-request">GitHub - onurkanbakirci/HttpOverTCP: Own HTTP implementation</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=837b0355ff6a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/code-beyond/to-write-an-http-server-on-top-of-the-tcp-layer-from-scratch-837b0355ff6a">Writing an HTTP Server on Top of the TCP Layer from Scratch</a> was originally published in <a href="https://medium.com/code-beyond">Code&amp;Beyond</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>